コード例 #1
0
        internal override ProjectRootElement Get(
            string projectFile,
            OpenProjectRootElement openProjectRootElement,
            bool isExplicitlyLoaded,
            bool?preserveFormatting)
        {
            // Should already have been canonicalized
            ErrorUtilities.VerifyThrowInternalRooted(projectFile);

            return(openProjectRootElement == null
                ? GetFromCache(projectFile)
                : GetFromOrAddToCache(projectFile, openProjectRootElement));
        }
コード例 #2
0
        private ProjectRootElement GetFromOrAddToCache(string projectFile, OpenProjectRootElement openFunc)
        {
            return(_cache.GetOrAdd(projectFile, key =>
            {
                ProjectRootElement rootElement = openFunc(key, this);
                ErrorUtilities.VerifyThrowInternalNull(rootElement, "projectRootElement");
                ErrorUtilities.VerifyThrow(rootElement.FullPath.Equals(key, StringComparison.OrdinalIgnoreCase),
                                           "Got project back with incorrect path");
                ErrorUtilities.VerifyThrow(_cache.TryGetValue(key, out _),
                                           "Open should have renamed into cache and boosted");

                return rootElement;
            }));
        }
コード例 #3
0
        private ProjectRootElement GetFromOrAddToCache(string projectFile, OpenProjectRootElement loadFunc)
        {
            return(_cache.GetOrAdd(projectFile, key =>
            {
                ProjectRootElement rootElement = loadFunc(key, this);
                ErrorUtilities.VerifyThrowInternalNull(rootElement, "projectRootElement");
                ErrorUtilities.VerifyThrow(
                    rootElement.FullPath.Equals(key, StringComparison.OrdinalIgnoreCase),
                    "Got project back with incorrect path. Expected path: {0}, received path: {1}.",
                    key,
                    rootElement.FullPath
                    );

                AddEntry(rootElement);

                ErrorUtilities.VerifyThrow(_cache.TryGetValue(key, out _),
                                           "Project should have been added into cache and boosted");

                return rootElement;
            }));
        }
コード例 #4
0
 internal abstract ProjectRootElement Get(string projectFile, OpenProjectRootElement loadProjectRootElement,
                                          bool isExplicitlyLoaded,
                                          bool?preserveFormatting);
コード例 #5
0
        /// <summary>
        /// Returns an existing ProjectRootElement for the specified file path, if any.
        /// If none exists, calls the provided delegate to load one, and adds that to the cache.
        /// The reason that it calls back to do this is so that the cache is locked between determining
        /// that the entry does not exist and adding the entry.
        ///
        /// If <see cref="_autoReloadFromDisk"/> was set to true, and the file on disk has changed since it was cached,
        /// it will be reloaded before being returned.
        ///
        /// Thread safe.
        /// </summary>
        /// <remarks>
        /// Never needs to consult the strong cache as well, since if the item is in there, it will
        /// not have left the weak cache.
        /// If item is found, boosts it to the top of the strong cache.
        /// </remarks>
        /// <param name="projectFile">The project file which contains the ProjectRootElement.  Must be a full path.</param>
        /// <param name="openProjectRootElement">The delegate to use to load if necessary. May be null.</param>
        /// <param name="isExplicitlyLoaded"><code>true</code> if the project is explicitly loaded, otherwise <code>false</code>.</param>
        /// <param name="preserveFormatting"><code>true</code> to the project was loaded with the formated preserved, otherwise <code>false</code>.</param>
        /// <returns>The ProjectRootElement instance if one exists.  Null otherwise.</returns>
        internal ProjectRootElement Get(string projectFile, OpenProjectRootElement openProjectRootElement, bool isExplicitlyLoaded,
                                        bool?preserveFormatting)
        {
            // Should already have been canonicalized
            ErrorUtilities.VerifyThrowInternalRooted(projectFile);

            lock (_locker)
            {
                ProjectRootElement projectRootElement;
                _weakCache.TryGetValue(projectFile, out projectRootElement);

                if (preserveFormatting != null && projectRootElement != null && projectRootElement.XmlDocument.PreserveWhitespace != preserveFormatting)
                {
                    //  Cached project doesn't match preserveFormatting setting, so reload it
                    projectRootElement.Reload(true, preserveFormatting);
                }

                if (projectRootElement != null && _autoReloadFromDisk)
                {
                    var fileInfo = FileUtilities.GetFileInfoNoThrow(projectFile);

                    // If the file doesn't exist on disk, go ahead and use the cached version.
                    // It's an in-memory project that hasn't been saved yet.
                    if (fileInfo != null)
                    {
                        bool forgetEntry = false;

                        if (fileInfo.LastWriteTime != projectRootElement.LastWriteTimeWhenRead)
                        {
                            // File was changed on disk by external means. Cached version is no longer reliable.
                            // We could throw here or ignore the problem, but it is a common and reasonable pattern to change a file
                            // externally and load a new project over it to see the new content. So we dump it from the cache
                            // to force a load from disk. There might then exist more than one ProjectRootElement with the same path,
                            // but clients ought not get themselves into such a state - and unless they save them to disk,
                            // it may not be a problem.
                            forgetEntry = true;
                        }
                        else if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDCACHECHECKFILECONTENT")))
                        {
                            // QA tests run too fast for the timestamp check to work. This environment variable is for their
                            // use: it checks the file content as well as the timestamp. That's better than completely disabling
                            // the cache as we get test coverage of the rest of the cache code.
                            XmlDocument document = new XmlDocument();
                            document.PreserveWhitespace = projectRootElement.XmlDocument.PreserveWhitespace;

                            using (var xtr = XmlReaderExtension.Create(projectRootElement.FullPath, projectRootElement.ProjectRootElementCache.LoadProjectsReadOnly))
                            {
                                document.Load(xtr.Reader);
                            }

                            string diskContent  = document.OuterXml;
                            string cacheContent = projectRootElement.XmlDocument.OuterXml;

                            if (diskContent != cacheContent)
                            {
                                forgetEntry = true;
                            }
                        }

                        if (forgetEntry)
                        {
                            ForgetEntry(projectRootElement);

                            DebugTraceCache("Out of date dropped from XML cache: ", projectFile);
                            projectRootElement = null;
                        }
                    }
                }

                if (projectRootElement == null && openProjectRootElement != null)
                {
                    projectRootElement = openProjectRootElement(projectFile, this);

                    ErrorUtilities.VerifyThrowInternalNull(projectRootElement, "projectRootElement");
                    ErrorUtilities.VerifyThrow(projectRootElement.FullPath == projectFile, "Got project back with incorrect path");
                    ErrorUtilities.VerifyThrow(_weakCache.Contains(projectFile), "Open should have renamed into cache and boosted");
                }
                else if (projectRootElement != null)
                {
                    DebugTraceCache("Satisfied from XML cache: ", projectFile);
                    BoostEntryInStrongCache(projectRootElement);
                }

                // An implicit load will never reset the explicit flag.
                if (projectRootElement != null && isExplicitlyLoaded)
                {
                    projectRootElement.MarkAsExplicitlyLoaded();
                }

                return(projectRootElement);
            }
        }
コード例 #6
0
        /// <summary>
        /// Returns an existing ProjectRootElement for the specified file path, if any.
        /// If none exists, calls the provided delegate to load one, and adds that to the cache.
        /// The reason that it calls back to do this is so that the cache is locked between determining
        /// that the entry does not exist and adding the entry.
        /// 
        /// If <see cref="_autoReloadFromDisk"/> was set to true, and the file on disk has changed since it was cached,
        /// it will be reloaded before being returned.
        /// 
        /// Thread safe.
        /// </summary>
        /// <remarks>
        /// Never needs to consult the strong cache as well, since if the item is in there, it will
        /// not have left the weak cache.
        /// If item is found, boosts it to the top of the strong cache.
        /// </remarks>
        /// <param name="projectFile">The project file which contains the ProjectRootElement.  Must be a full path.</param>
        /// <param name="openProjectRootElement">The delegate to use to load if necessary. May be null.</param>
        /// <returns>The ProjectRootElement instance if one exists.  Null otherwise.</returns>
        internal ProjectRootElement Get(string projectFile, OpenProjectRootElement openProjectRootElement, bool isExplicitlyLoaded,
            bool preserveFormatting)
        {
            // Should already have been canonicalized
            ErrorUtilities.VerifyThrowInternalRooted(projectFile);

            lock (_locker)
            {
                ProjectRootElement projectRootElement;
                _weakCache.TryGetValue(projectFile, out projectRootElement);

                if (projectRootElement != null && projectRootElement.XmlDocument.PreserveWhitespace != preserveFormatting)
                {
                    //  Cached project doesn't match preserveFormatting setting, so don't use it
                    projectRootElement = null;
                }

                if (projectRootElement != null && _autoReloadFromDisk)
                {
                    FileInfo fileInfo = FileUtilities.GetFileInfoNoThrow(projectFile);

                    // If the file doesn't exist on disk, go ahead and use the cached version.
                    // It's an in-memory project that hasn't been saved yet.
                    if (fileInfo != null)
                    {
                        bool forgetEntry = false;

                        if (fileInfo.LastWriteTime != projectRootElement.LastWriteTimeWhenRead)
                        {
                            // File was changed on disk by external means. Cached version is no longer reliable. 
                            // We could throw here or ignore the problem, but it is a common and reasonable pattern to change a file 
                            // externally and load a new project over it to see the new content. So we dump it from the cache
                            // to force a load from disk. There might then exist more than one ProjectRootElement with the same path,
                            // but clients ought not get themselves into such a state - and unless they save them to disk,
                            // it may not be a problem.  
                            forgetEntry = true;
                        }
                        else if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDCACHECHECKFILECONTENT")))
                        {
                            // QA tests run too fast for the timestamp check to work. This environment variable is for their
                            // use: it checks the file content as well as the timestamp. That's better than completely disabling
                            // the cache as we get test coverage of the rest of the cache code.
                            XmlDocument document = new XmlDocument();
                            document.PreserveWhitespace = projectRootElement.XmlDocument.PreserveWhitespace;

                            XmlReaderSettings dtdSettings = new XmlReaderSettings();
                            dtdSettings.DtdProcessing = DtdProcessing.Ignore;
                            using (var stream = new StreamReader(projectRootElement.FullPath))
                            using (XmlReader xtr = XmlReader.Create(stream, dtdSettings))
                            {
                                document.Load(xtr);
                            }

                            string diskContent = document.OuterXml;
                            string cacheContent = projectRootElement.XmlDocument.OuterXml;

                            if (diskContent != cacheContent)
                            {
                                forgetEntry = true;
                            }
                        }

                        if (forgetEntry)
                        {
                            ForgetEntry(projectRootElement);

                            DebugTraceCache("Out of date dropped from XML cache: ", projectFile);
                            projectRootElement = null;
                        }
                    }
                }

                if (projectRootElement == null && openProjectRootElement != null)
                {
                    projectRootElement = openProjectRootElement(projectFile, this);

                    ErrorUtilities.VerifyThrowInternalNull(projectRootElement, "projectRootElement");
                    ErrorUtilities.VerifyThrow(projectRootElement.FullPath == projectFile, "Got project back with incorrect path");
                    ErrorUtilities.VerifyThrow(_weakCache.Contains(projectFile), "Open should have renamed into cache and boosted");
                }
                else if (projectRootElement != null)
                {
                    DebugTraceCache("Satisfied from XML cache: ", projectFile);
                    BoostEntryInStrongCache(projectRootElement);
                }

                // An implicit load will never reset the explicit flag.
                if (projectRootElement != null && isExplicitlyLoaded)
                {
                    projectRootElement.MarkAsExplicitlyLoaded();
                }

                return projectRootElement;
            }
        }
コード例 #7
0
        internal override ProjectRootElement Get(string projectFile, OpenProjectRootElement loadProjectRootElement, bool isExplicitlyLoaded,
                                                 bool?preserveFormatting)
        {
#if DEBUG
            // Verify that loadProjectRootElement delegate does not call ProjectRootElementCache.Get().
            s_getEntriesNumber++;
            ErrorUtilities.VerifyThrow(
                s_getEntriesNumber == 1,
                "Reentrance to the ProjectRootElementCache.Get function detected."
                );

            try {
#endif
            // Should already have been canonicalized
            ErrorUtilities.VerifyThrowInternalRooted(projectFile);

            ProjectRootElement projectRootElement;
            lock (_locker)
            {
                _weakCache.TryGetValue(projectFile, out projectRootElement);

                if (projectRootElement != null)
                {
                    BoostEntryInStrongCache(projectRootElement);

                    // An implicit load will never reset the explicit flag.
                    if (isExplicitlyLoaded)
                    {
                        projectRootElement.MarkAsExplicitlyLoaded();
                    }
                }
                else
                {
                    DebugTraceCache("Not found in cache: ", projectFile);
                }

                if (preserveFormatting != null && projectRootElement != null && projectRootElement.XmlDocument.PreserveWhitespace != preserveFormatting)
                {
                    // Cached project doesn't match preserveFormatting setting, so reload it
                    projectRootElement.Reload(true, preserveFormatting);
                }
            }

            bool projectRootElementIsInvalid = IsInvalidEntry(projectFile, projectRootElement);
            if (projectRootElementIsInvalid)
            {
                DebugTraceCache("Not satisfied from cache: ", projectFile);
                ForgetEntryIfExists(projectRootElement);
            }

            if (loadProjectRootElement == null)
            {
                if (projectRootElement == null || projectRootElementIsInvalid)
                {
                    return(null);
                }
                else
                {
                    DebugTraceCache("Satisfied from XML cache: ", projectFile);
                    return(projectRootElement);
                }
            }

            // Use openProjectRootElement to reload the element if the cache element does not exist or need to be reloaded.
            if (projectRootElement == null || projectRootElementIsInvalid)
            {
                // We do not lock loading with common _locker of the cache, to avoid lock contention.
                // Decided also not to lock this section with the key specific locker to avoid the overhead and code overcomplication, as
                // it is not likely that two threads would use Get function for the same project simultaneously and it is not a big deal if in some cases we load the same project twice.

                projectRootElement = loadProjectRootElement(projectFile, this);
                ErrorUtilities.VerifyThrowInternalNull(projectRootElement, "projectRootElement");
                ErrorUtilities.VerifyThrow(
                    projectRootElement.FullPath.Equals(projectFile, StringComparison.OrdinalIgnoreCase),
                    "Got project back with incorrect path. Expected path: {0}, received path: {1}.",
                    projectFile,
                    projectRootElement.FullPath
                    );

                // An implicit load will never reset the explicit flag.
                if (isExplicitlyLoaded)
                {
                    projectRootElement.MarkAsExplicitlyLoaded();
                }

                // Update cache element.
                // It is unlikely, but it might be that while without the lock, the projectRootElement in cache was updated by another thread.
                // And here its entry will be replaced with the loaded projectRootElement. This is fine:
                // if loaded projectRootElement is out of date (so, it changed since the time we loaded it), it will be updated the next time some thread calls Get function.
                AddEntry(projectRootElement);
            }
            else
            {
                DebugTraceCache("Satisfied from XML cache: ", projectFile);
            }


            return(projectRootElement);

#if DEBUG
        }

        finally
        {
            s_getEntriesNumber--;
        }
#endif
        }