1010using System . Buffers ;
1111using System . Collections . Generic ;
1212using System . Diagnostics ;
13+ using System . Threading ;
1314
1415using IronPython . Runtime ;
1516using IronPython . Runtime . Exceptions ;
@@ -38,7 +39,7 @@ public static partial class CTypes {
3839 /// Base class for all ctypes interop types.
3940 /// </summary>
4041 [ PythonType ( "_CData" ) , PythonHidden ]
41- public abstract class CData : IBufferProtocol , IPythonBuffer {
42+ public abstract class CData : IBufferProtocol , IDisposable {
4243 internal MemoryHolder MemHolder {
4344 get => _memHolder ?? throw new InvalidOperationException ( $ "{ nameof ( CData ) } object not fully initialized.") ;
4445 set {
@@ -47,6 +48,7 @@ internal MemoryHolder MemHolder {
4748 }
4849 }
4950 private MemoryHolder ? _memHolder ;
51+ private bool _disposed ;
5052
5153 // members: __setstate__, __reduce__ _b_needsfree_ __ctypes_from_outparam__ __hash__ _objects _b_base_ __doc__
5254 protected CData ( ) { }
@@ -108,25 +110,37 @@ internal void InitializeFromBufferCopy(object? data, int offset, int size) {
108110 internal virtual PythonTuple GetBufferInfo ( )
109111 => PythonTuple . MakeTuple ( NativeType . TypeFormat , 0 , PythonTuple . EMPTY ) ;
110112
113+ [ PythonHidden ]
114+ public void Dispose ( ) {
115+ if ( ! _disposed ) {
116+ _disposed = true ;
117+ if ( _numExports == 0 ) {
118+ MemoryHolder ? holder = Interlocked . Exchange ( ref _memHolder , null ) ;
119+ holder ? . Dispose ( ) ;
120+ }
121+ }
122+ }
111123
112- #region IBufferProtocol Members
113-
114- IPythonBuffer IBufferProtocol . GetBuffer ( BufferFlags flags ) => this ;
115-
116- void IDisposable . Dispose ( ) { } // TODO
117-
118- object IPythonBuffer . Object => this ;
119-
120- unsafe ReadOnlySpan < byte > IPythonBuffer . AsReadOnlySpan ( )
121- => new Span < byte > ( MemHolder . UnsafeAddress . ToPointer ( ) , MemHolder . Size ) ;
124+ #region IBufferProtocol
122125
123- unsafe Span < byte > IPythonBuffer . AsSpan ( )
124- => new Span < byte > ( MemHolder . UnsafeAddress . ToPointer ( ) , MemHolder . Size ) ;
126+ private int _numExports ;
125127
126- unsafe MemoryHandle IPythonBuffer . Pin ( )
127- => new MemoryHandle ( MemHolder . UnsafeAddress . ToPointer ( ) ) ;
128+ IPythonBuffer IBufferProtocol . GetBuffer ( BufferFlags flags ) {
129+ if ( _disposed ) throw new ObjectDisposedException ( GetType ( ) . Name ) ;
130+ _ = MemHolder ; // check if fully initialized
131+ Interlocked . Increment ( ref _numExports ) ;
132+ return new CDataView ( this ) ;
133+ }
128134
129- int IPythonBuffer . Offset => 0 ;
135+ // May be executed concurremtly with Dispose(), GetBuffer(), or another ReleaseBuffer()
136+ private void ReleaseBuffer ( ) {
137+ Debug . Assert ( _numExports > 0 ) ;
138+ int cnt = Interlocked . Decrement ( ref _numExports ) ;
139+ if ( cnt == 0 && _disposed ) {
140+ MemoryHolder ? holder = Interlocked . Exchange ( ref _memHolder , null ) ;
141+ holder ? . Dispose ( ) ;
142+ }
143+ }
130144
131145 [ PythonHidden ]
132146 public virtual int ItemCount {
@@ -141,21 +155,53 @@ public virtual int ItemCount {
141155 }
142156 }
143157
144- string IPythonBuffer . Format => NativeType . TypeFormat ;
145-
146158 [ PythonHidden ]
147159 public virtual int ItemSize => NativeType . Size ;
148160
149- int IPythonBuffer . NumOfDims => Shape ? . Count ?? ( ItemCount > 1 ? 1 : 0 ) ;
150-
151- bool IPythonBuffer . IsReadOnly => false ;
152-
153161 [ PythonHidden ]
154162 public virtual IReadOnlyList < int > ? Shape => null ;
155163
156- IReadOnlyList < int > ? IPythonBuffer . Strides => null ;
157164
158- IReadOnlyList < int > ? IPythonBuffer . SubOffsets => null ;
165+ private sealed class CDataView : IPythonBuffer {
166+ private CData ? _cdata ;
167+ private CData CData => _cdata ?? throw new ObjectDisposedException ( nameof ( CDataView ) ) ;
168+
169+ internal CDataView ( CData cdata ) => _cdata = cdata ;
170+
171+ public void Dispose ( ) {
172+ CData ? cdata = Interlocked . Exchange ( ref _cdata , null ) ;
173+ cdata ? . ReleaseBuffer ( ) ;
174+ }
175+
176+ public object Object => CData ;
177+
178+ unsafe ReadOnlySpan < byte > IPythonBuffer . AsReadOnlySpan ( )
179+ => new Span < byte > ( CData . MemHolder . UnsafeAddress . ToPointer ( ) , CData . MemHolder . Size ) ;
180+
181+ unsafe Span < byte > IPythonBuffer . AsSpan ( )
182+ => new Span < byte > ( CData . MemHolder . UnsafeAddress . ToPointer ( ) , CData . MemHolder . Size ) ;
183+
184+ unsafe MemoryHandle IPythonBuffer . Pin ( )
185+ => new MemoryHandle ( CData . MemHolder . UnsafeAddress . ToPointer ( ) ) ;
186+
187+ public int Offset => 0 ;
188+
189+ public int ItemCount => CData . ItemCount ;
190+
191+ public string Format => CData . NativeType . TypeFormat ;
192+
193+ public int ItemSize => CData . ItemSize ;
194+
195+ public int NumOfDims => Shape ? . Count ?? ( ItemCount > 1 ? 1 : 0 ) ;
196+
197+ public bool IsReadOnly => false ;
198+
199+ public IReadOnlyList < int > ? Shape => CData . Shape ;
200+
201+ IReadOnlyList < int > ? IPythonBuffer . Strides => null ;
202+
203+ IReadOnlyList < int > ? IPythonBuffer . SubOffsets => null ;
204+ }
159205
160206 #endregion
161207 }
0 commit comments