| 109 | | This example walks through an example of the reference count handshaking between the core and the IronPython interface & garbage collector. |
| | 111 | '''NOTE: This example is under construction and currently incomplete and probably incorrect in places.''' |
| | 112 | |
| | 113 | This example is similar to the reference counted example above but shows how the memory management system works with a garbage collected environment. The examples used apply to IronPython and the C#/.NET environment but should be generally applicable to other garbage collected systems. |
| | 114 | |
| | 115 | When the core library constructs a new !NpyArray instance, '''A''', a callback is made to the array wrapper callback provided by the interface (`!NpyArray.ArrayNewWrapper` in NpyArray.cs for the IronPython interace). This function returns an IntPtr to a GCHandle instance that references the wrapper object (`ndarray` for the IronPython interface), '''Awrap'''. At this point we have: |
| | 116 | {{{ |
| | 117 | A->nob_refcnt = 1 |
| | 118 | A->nob_interface = GCHandle(Awrap) |
| | 119 | }}} |
| | 120 | |
| | 121 | '''A''' has a reference count of '1' and holds a GC reference to the wrapper instance. |
| | 122 | |
| | 123 | Now suppose '''A''' is returned from the core to the interface layer. The interface layer can convert '''A''' to '''Awrap''' by calling `NpyArray.ToInterface<ndarray>(A)`. This causes the reference to be moved from the core to the interface layer: |
| | 124 | {{{ |
| | 125 | ndarray *Awrap; |
| | 126 | IntPtr *A = someFunctionReturningNpyArray(); |
| | 127 | |
| | 128 | Awrap = NpyArray.ToInterface(A); |
| | 129 | NpyArray.Decref(A); |
| | 130 | }}} |
| | 131 | |
| | 132 | The call to `ToInterface` accesses the `nob_interface` field of '''A''' to retrieve the `GCHandle` referencing '''Awrap''' and returns the ''''Awrap''' instance. Calling `NpyArray.Decref` results in the reference count of '''A''' going from 1 to 0, and thus '''A''' releases it's reference to '''Awrap'''. Releasing the reference in this environment means that the `GCHandle` instance pointed to by `A->nob_interface` is deallocated. This would break the link to '''Awrap''' so a new weak reference is allocated: |
| | 133 | {{{ |
| | 134 | GCHandle old = A.nob_interface; |
| | 135 | A.nob_interface = GCHandle.Alloc(old.Target, GCHandleType.Weak); |
| | 136 | old.Free(); |
| | 137 | }}} |
| | 138 | |
| | 139 | At this point, if the interface code stops referencing '''Awrap''', the garbage collector will be free to deallocate it since the '''A''' only holds a weak reference. In this case, the finalizer for '''Awrap''' will dealloc '''A''' as well. |
| | 140 | |
| | 141 | Instead, consider the case where '''A''' is retrieved from '''Awrap'''. In the IronPython interface this is done using the `Array` property: |
| | 142 | {{{ |
| | 143 | ndarray Awrap; |
| | 144 | IntPtr A; |
| | 145 | |
| | 146 | A = Awrap.Array; |
| | 147 | NpyArray.Incref(A); |
| | 148 | }}} |
| | 149 | |
| | 150 | The call to `!NpyArray.IncRef` causes the reference count on '''A''' to go from 0 to 1 and thus it must hold a reference to '''Awrap'''. This is done by deallocating the weak reference in `nob_interface` and replacing it with a newly allocated full reference. For example: |
| | 151 | {{{ |
| | 152 | GCHandle oldWeak = A.nob_interface; |
| | 153 | A.nob_interface = GCHandle.Alloc(oldWeak.Target); |
| | 154 | oldWeak.Free(); |
| | 155 | }}} |
| | 156 | |
| | 157 | At this point, if the interface stops referencing '''Awrap''' then there is one reference on '''A''' and '''A''' holds the the only GC reference to '''Awrap'''. If the core calls `Npy_DECREF` on '''A''', the reference count will go to zero and the GCHandle to '''Awrap''' will also be deallocated. Since neither instance is now references, the garbage collector will at some point collect '''Awrap''' and the finalizer for '''Awrap''' will deallocate '''A'''. |