/// <summary>
        /// Called to manually trigger a refresh of <see cref="IsCurrent"/>.
        /// After completion, <see cref="IsCurrentChanged"/> will always be
        /// raised, regardless of whether the values were changed.
        /// </summary>
        public virtual void RefreshIsCurrent() {
#if DEBUG
            // Indicate that we've arrived here at least once. Otherwise we
            // will assert.
            _hasEverCheckedDatabase = true;
#endif

            if (GlobalInterpreterOptions.SuppressFileSystemWatchers || string.IsNullOrEmpty(DatabasePath)) {
                _isCheckingDatabase = false;
                _isValid = true;
                _missingModules = null;
                _isCurrentException = null;
                OnIsCurrentChanged();
                return;
            }
            if (IsGenerating) {
                if (PythonTypeDatabase.IsDatabaseRegenerating(DatabasePath)) {
                    _isValid = false;
                    _missingModules = null;
                    _isCurrentException = null;
                    return;
                }
                Interlocked.Exchange(ref _generating, null);
            }

            try {
                if (!_isCurrentSemaphore.Wait(0)) {
                    // Another thread is working on our state, so we will wait for
                    // them to finish and return, since the value is up to date.
                    _isCurrentSemaphore.Wait();
                    _isCurrentSemaphore.Release();
                    return;
                }
            } catch (ObjectDisposedException) {
                // We've been disposed and the call has come in from
                // externally, probably a timer.
                return;
            }
            
            try {
                _isCheckingDatabase = true;
                OnIsCurrentChanged();

                // Wait for a slot that will allow us to scan the disk. This
                // prevents too many threads from updating at once and causing
                // I/O saturation.
                _sharedIsCurrentSemaphore.Wait();

                try {
                    if (PythonTypeDatabase.IsDatabaseRegenerating(DatabasePath) ||
                        !PythonTypeDatabase.IsDatabaseVersionCurrent(DatabasePath)) {
                        // Skip the rest of the checks, because we know we're
                        // not current.
                        _isValid = false;
                        _missingModules = null;
                        _isCurrentException = null;
                    } else {
                        _isValid = true;
                        HashSet<string> existingDatabase = null;
                        string[] missingModules = null;

                        for (int retries = 3; retries > 0; --retries) {
                            try {
                                existingDatabase = GetExistingDatabase(DatabasePath);
                                break;
                            } catch (UnauthorizedAccessException) {
                            } catch (IOException) {
                            }
                            Thread.Sleep(100);
                        }

                        if (existingDatabase == null) {
                            // This will either succeed or throw again. If it throws
                            // then the error is reported to the user.
                            existingDatabase = GetExistingDatabase(DatabasePath);
                        }

                        for (int retries = 3; retries > 0; --retries) {
                            try {
                                missingModules = GetMissingModules(existingDatabase);
                                break;
                            } catch (UnauthorizedAccessException) {
                            } catch (IOException) {
                            }
                            Thread.Sleep(100);
                        }

                        if (missingModules == null) {
                            // This will either succeed or throw again. If it throws
                            // then the error is reported to the user.
                            missingModules = GetMissingModules(existingDatabase);
                        }

                        if (missingModules.Length > 0) {
                            var oldModules = _missingModules;
                            if (oldModules == null ||
                                oldModules.Length != missingModules.Length ||
                                !oldModules.SequenceEqual(missingModules)) {
                            }
                            _missingModules = missingModules;
                        } else {
                            _missingModules = null;
                        }
                    }
                    _isCurrentException = null;
                } finally {
                    _sharedIsCurrentSemaphore.Release();
                }
            } catch (Exception ex) {
                // Report the exception text as the reason.
                _isCurrentException = ex.ToString();
                _missingModules = null;
            } finally {
                _isCheckingDatabase = false;
                try {
                    _isCurrentSemaphore.Release();
                } catch (ObjectDisposedException) {
                    // The semaphore is not locked for disposal as it is only
                    // used to prevent reentrance into this function. As a
                    // result, it may have been disposed while we were in here.
                }
            }

            OnIsCurrentChanged();
        }
        /// <summary>
        /// Clears any cached type databases and raises the
        /// <see cref="NewDatabaseAvailable"/> event.
        /// </summary>
        protected void OnNewDatabaseAvailable() {
            _typeDb = null;
            _typeDbWithoutPackages = null;

            NewDatabaseAvailable?.Invoke(this, EventArgs.Empty);
        }
Example #3
0
        public CPythonType(IMemberContainer parent, ITypeDatabaseReader typeDb, string typeName, Dictionary <string, object> typeTable, BuiltinTypeId typeId)
        {
            Debug.Assert(parent is CPythonType || parent is CPythonModule);
            Debug.Assert(!typeId.IsVirtualId());

            _typeName = typeName;
            _typeId   = typeId;
            _module   = GetDeclaringModule(parent);

            object value;

            if (typeTable.TryGetValue("is_hidden", out value))
            {
                _includeInModule = !Convert.ToBoolean(value);
            }
            else
            {
                _includeInModule = true;
            }

            if (typeTable.TryGetValue("doc", out value))
            {
                _doc = value as string;
            }

            if (typeTable.TryGetValue("builtin", out value))
            {
                _isBuiltin = Convert.ToBoolean(value);
            }
            else
            {
                _isBuiltin = true;
            }

            if (typeTable.TryGetValue("bases", out value))
            {
                var basesList = (List <object>)value;
                if (basesList != null)
                {
                    _bases = new List <IPythonType>();
                    foreach (var baseType in basesList)
                    {
                        typeDb.LookupType(baseType, StoreBase);
                    }
                }
            }

            if (typeTable.TryGetValue("mro", out value))
            {
                var mroList = (List <object>)value;
                if (mroList != null && mroList.Count >= 1)
                {
                    _mro = new IPythonType[mroList.Count];
                    // Many scraped types have invalid MRO entries because they
                    // report their own module/name incorrectly. Since the first
                    // item in the MRO is always self, we set it now. If the MRO
                    // has a resolvable entry it will replace this one.
                    _mro[0] = this;
                    for (int i = 0; i < mroList.Count; ++i)
                    {
                        var capturedI = i;
                        typeDb.LookupType(mroList[i], t => _mro[capturedI] = t);
                    }
                }
            }

            if (typeTable.TryGetValue("members", out value))
            {
                var membersTable = (Dictionary <string, object>)value;
                if (membersTable != null)
                {
                    LoadMembers(typeDb, membersTable);
                }
            }

            _hasLocation = PythonTypeDatabase.TryGetLocation(typeTable, ref _line, ref _column);
        }