private Task <CompatibilityProfileCacheEntry> GetUnionProfile(DirectoryInfo profileDir)
        {
            IEnumerable <string> profilePaths = profileDir.EnumerateFiles("*.json")
                                                .Where(file => file.Name.IndexOf("union", StringComparison.OrdinalIgnoreCase) < 0) // Filter out union files
                                                .Select(file => file.FullName);

            IEnumerable <CompatibilityProfileCacheEntry> profiles = GetProfilesFromPaths(profilePaths).Result;

            string unionId   = GetUnionIdFromProfiles(profiles);
            string unionPath = Path.Combine(profileDir.FullName, unionId + ".json");

            return(_profileCache.GetOrAdd(unionPath, new Lazy <Task <CompatibilityProfileCacheEntry> >(() => Task.Run(() => {
                try
                {
                    // We read the ID first to avoid needing to rehydrate MBs of unneeded JSON
                    if (JsonProfileSerializer.ReadIdFromProfileFile(unionPath) == unionId)
                    {
                        CompatibilityProfileDataMut loadedUnionProfile = _jsonSerializer.DeserializeFromFile(unionPath);

                        // This is unlikely, but the ID has limited entropy
                        if (UnionMatchesProfiles(loadedUnionProfile, profiles))
                        {
                            return new CompatibilityProfileCacheEntry(
                                loadedUnionProfile,
                                new CompatibilityProfileData(loadedUnionProfile));
                        }
                    }

                    // We found the union file, but it didn't match for some reason
                    File.Delete(unionPath);
                }
                catch (Exception)
                {
                    // Do nothing, we will now generate the profile
                }

                // Loading the union file failed, so we are forced to generate it
                CompatibilityProfileDataMut generatedUnionProfile = ProfileCombination.UnionMany(unionId, profiles.Select(p => p.MutableProfileData));

                // Write the union to the filesystem for faster startup later
                Task.Run(() => {
                    _jsonSerializer.SerializeToFile(generatedUnionProfile, unionPath);
                });

                return new CompatibilityProfileCacheEntry(
                    generatedUnionProfile,
                    new CompatibilityProfileData(generatedUnionProfile));
            }))).Value);
        }
        /// <summary>
        /// Create a new compatibility profile loader with an empty cache.
        /// </summary>
        public CompatibilityProfileLoader()
        {
            _jsonSerializer = JsonProfileSerializer.Create();

            // Cache keys are filenames, which must be case-insensitive in Windows and macOS
#if CORECLR
            if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                _profileCache = new ConcurrentDictionary <string, Lazy <Task <CompatibilityProfileCacheEntry> > >();
            }
            else
            {
                _profileCache = new ConcurrentDictionary <string, Lazy <Task <CompatibilityProfileCacheEntry> > >(StringComparer.OrdinalIgnoreCase);
            }
#else
            _profileCache = new ConcurrentDictionary <string, Lazy <Task <CompatibilityProfileCacheEntry> > >(StringComparer.OrdinalIgnoreCase);
#endif
        }