/// <summary> /// Does one step in the work process (mostly garbage collection for freeing up unused handles). /// True is returned if more work is pending, and false otherwise. /// </summary> bool _DoWorkStep() { // ... check one abandoned object ... LinkedListNode <IV8Disposable> abandoned = null; lock (_AbandondObjects) { if (_AbandondObjects.Count > 0) { abandoned = _AbandondObjects.First; // (take the first abandoned object and try to dispose of it) _AbandondObjects.RemoveFirst(); // WARNING: Until '_AbandondObjectsIndex' is also updated, DO NOT release lock until the node is put back, or removed from the index! } if (abandoned != null) { if (abandoned.Value.CanDispose) { abandoned.Value.Dispose(); GC.SuppressFinalize(abandoned); // (required otherwise the object's finalizer will be triggered again) _AbandondObjectsIndex.Remove(abandoned.Value); } else { _AbandondObjects.AddLast(abandoned); // (still can't dispose this yet) } } } // ... and do one handle ready to be disposed ... IV8Disposable disposable = null; lock (_DisposalQueue) // TODO: consider using read/write locks in more places { if (_DisposalQueue.Count > 0) { disposable = _DisposalQueue.Dequeue(); } } if (disposable != null) { if (disposable.CanDispose) { disposable.Dispose(); } else { // ... cannot dispose this yet; place it in the "abandoned" queue for later (next generation disposal) ... lock (_AbandondObjects) { _AbandondObjectsIndex[disposable] = _AbandondObjects.AddLast(disposable); } // ... if this is a manage object, it is now abandoned, so make the native handle weak ... if (disposable is V8NativeObject) { var v8Obj = (V8NativeObject)disposable; if (!v8Obj.InternalHandle.IsEmpty && v8Obj.InternalHandle._HandleProxy->IsPendingDisposal) { V8NetProxy.MakeWeakHandle(v8Obj.InternalHandle); // ('Disposed' will be 2 after this, so no worries about doing this more than once) /* Once the native GC attempts to collect the underlying native object, then '_OnNativeGCRequested()' will get * called to finalize the disposal of the managed object. * Note 1: Once the native V8 engine agrees, this object will be removed due to a global V8 GC callback * registered when the V8.Net wrapper engine was created. * Note 2: Don't call this while '_Objects' is locked, because the main thread may be executing script that may * also need a lock, and this call may become blocked by a native side mutex. */ } } } } return(_WeakObjects.Count + _DisposalQueue.Count > 0); }
/// <summary> /// Called by the worker thread to make the native handle weak. Once the native GC attempts to collect the underlying native object, then /// '_OnNativeGCRequested()' will get called to finalize the disposal of the managed object. /// </summary> internal void _MakeWeak() { V8NetProxy.MakeWeakHandle(_Handle); // (once the native V8 engine agrees, this instance will be removed due to a global GC callback registered when the engine was created) }