/// <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); }
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); }