/DICK.B1/IronPython/Runtime/WeakRef.cs

https://bitbucket.org/williamybs/uidipythontool · C# · 181 lines · 110 code · 28 blank · 43 comment · 17 complexity · 5728ca9007d30fbbc3e56f3855deb386 MD5 · raw file

  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using System.Dynamic;
  19. using Microsoft.Scripting.Runtime;
  20. using Microsoft.Scripting.Actions;
  21. using Microsoft.Scripting.Utils;
  22. using IronPython.Runtime.Binding;
  23. using IronPython.Runtime.Operations;
  24. namespace IronPython.Runtime {
  25. /// <summary>
  26. /// single finalizable instance used to track and deliver all the
  27. /// callbacks for a single object that has been weakly referenced by
  28. /// one or more references and proxies. The reference to this object
  29. /// is held in objects that implement IWeakReferenceable.
  30. /// </summary>
  31. public class WeakRefTracker {
  32. struct CallbackInfo {
  33. object callback;
  34. WeakHandle _longRef, _shortRef;
  35. public CallbackInfo(object callback, object weakRef) {
  36. this.callback = callback;
  37. // we need a short ref & a long ref to deal with the case
  38. // when what we're finalizing is cyclic trash. (see test_weakref
  39. // test_callbacks_on_callback). If the short ref is dead, but the
  40. // long ref still lives then it means we'the weakref is in the
  41. // finalization queue and we shouldn't run it's callback - we're
  42. // just unlucky and are getting ran first.
  43. this._longRef = new WeakHandle(weakRef, true);
  44. this._shortRef = new WeakHandle(weakRef, false);
  45. }
  46. public object Callback {
  47. get {
  48. return callback;
  49. }
  50. }
  51. public WeakHandle LongRef {
  52. get {
  53. return _longRef;
  54. }
  55. }
  56. public bool IsFinalizing {
  57. get {
  58. return _longRef.IsAlive && !_shortRef.IsAlive ||
  59. _longRef.Target != _shortRef.Target;
  60. }
  61. }
  62. }
  63. List<CallbackInfo> callbacks;
  64. public WeakRefTracker(object callback, object weakRef) {
  65. callbacks = new List<CallbackInfo>(1);
  66. ChainCallback(callback, weakRef);
  67. }
  68. public void ChainCallback(object callback, object weakRef) {
  69. callbacks.Add(new CallbackInfo(callback, weakRef));
  70. }
  71. public int HandlerCount {
  72. get {
  73. return callbacks.Count;
  74. }
  75. }
  76. public void RemoveHandlerAt(int index) {
  77. callbacks.RemoveAt(index);
  78. }
  79. public void RemoveHandler(object o) {
  80. for (int i = 0; i < HandlerCount; i++) {
  81. if (GetWeakRef(i) == o) {
  82. RemoveHandlerAt(i);
  83. break;
  84. }
  85. }
  86. }
  87. public object GetHandlerCallback(int index) {
  88. return callbacks[index].Callback;
  89. }
  90. public object GetWeakRef(int index) {
  91. return callbacks[index].LongRef.Target;
  92. }
  93. ~WeakRefTracker() {
  94. // callbacks are delivered last registered to first registered.
  95. for (int i = callbacks.Count - 1; i >= 0; i--) {
  96. CallbackInfo ci = callbacks[i];
  97. try {
  98. try {
  99. // a little ugly - we only run callbacks that aren't a part
  100. // of cyclic trash. but classes use a single field for
  101. // finalization & GC - and that's always cyclic, so we need to special case it.
  102. if (ci.Callback != null &&
  103. (!ci.IsFinalizing ||
  104. ci.LongRef.Target is InstanceFinalizer)) {
  105. InstanceFinalizer fin = ci.Callback as InstanceFinalizer;
  106. if (fin != null) {
  107. // Going through PythonCalls / Rules requires the types be public.
  108. // Explicit check so that we can keep InstanceFinalizer internal.
  109. fin.CallDirect(DefaultContext.Default);
  110. } else {
  111. // Non-instance finalizer goes through normal call mechanism.
  112. PythonCalls.Call(ci.Callback, ci.LongRef.Target);
  113. }
  114. }
  115. } catch (Exception) {
  116. ExceptionHelpers.DynamicStackFrames = null;
  117. }
  118. callbacks[i].LongRef.Free();
  119. } catch (InvalidOperationException) {
  120. // target was freed
  121. }
  122. }
  123. }
  124. }
  125. /// <summary>
  126. /// Finalizable object used to hook up finalization calls for OldInstances.
  127. ///
  128. /// We create one of these each time an object w/ a finalizer gets created. The
  129. /// only reference to this object is the instance so when that goes out of context
  130. /// this does as well and this will get finalized.
  131. /// </summary>
  132. internal sealed class InstanceFinalizer {
  133. private object _instance;
  134. internal InstanceFinalizer(CodeContext/*!*/ context, object inst) {
  135. Debug.Assert(inst != null);
  136. _instance = inst;
  137. }
  138. // This corresponds to a __del__ method on a class.
  139. // Callers will do a direct invoke so that instanceFinalizer can stay non-public.
  140. internal object CallDirect(CodeContext context) {
  141. object o;
  142. IronPython.Runtime.Types.OldInstance oi = _instance as IronPython.Runtime.Types.OldInstance;
  143. if (oi != null) {
  144. if (oi.TryGetBoundCustomMember(context, "__del__", out o)) {
  145. return PythonContext.GetContext(context).CallSplat(o);
  146. }
  147. } else {
  148. PythonTypeOps.TryInvokeUnaryOperator(context, _instance, "__del__", out o);
  149. }
  150. return null;
  151. }
  152. }
  153. }