77#if FEATURE_MMAP
88
99using System ;
10+ using System . Buffers ;
11+ using System . Collections . Generic ;
1012using System . Diagnostics ;
1113using System . Globalization ;
1214using System . IO ;
@@ -278,7 +280,7 @@ private static MemoryMappedFileAccess ToMmapFileAccess(int access) {
278280 }
279281
280282 [ PythonHidden ]
281- public class MmapDefault : IWeakReferenceable {
283+ public class MmapDefault : IWeakReferenceable , IBufferProtocol {
282284 private MemoryMappedFile _file ;
283285 private MemoryMappedViewAccessor _view ;
284286 private long _position ;
@@ -599,6 +601,11 @@ private bool TryAddRef(bool exclusive, out int reason) {
599601 reason = StateBits . Exporting ;
600602 return false ;
601603 }
604+ if ( exclusive && ( ( oldState & StateBits . RefCount ) > StateBits . RefCountOne ) ) {
605+ // mmap in non-exclusive use, temporarily no exclusive use allowed
606+ reason = StateBits . Exclusive ;
607+ return false ;
608+ }
602609 Debug . Assert ( ( oldState & StateBits . RefCount ) > 0 , "resurrecting disposed mmap object (disposed without being closed)" ) ;
603610
604611 newState = oldState + StateBits . RefCountOne ;
@@ -635,6 +642,9 @@ private void Release(bool exclusive) {
635642 if ( exclusive ) {
636643 newState &= ~ StateBits . Exclusive ;
637644 }
645+ if ( ( newState & StateBits . RefCount ) == StateBits . RefCountOne ) {
646+ newState &= ~ StateBits . Exporting ;
647+ }
638648 } while ( Interlocked . CompareExchange ( ref _state , newState , oldState ) != oldState ) ;
639649
640650 if ( performDispose ) {
@@ -648,25 +658,28 @@ private void Release(bool exclusive) {
648658 }
649659 }
650660
661+ private int InterlockedOrState ( int value ) {
662+ #if NET5_0_OR_GREATER
663+ return Interlocked . Or ( ref _state , value ) ;
664+ #else
665+ int current = _state ;
666+ while ( true ) {
667+ int newValue = current | value ;
668+ int oldValue = Interlocked . CompareExchange ( ref _state , newValue , current ) ;
669+ if ( oldValue == current ) {
670+ return oldValue ;
671+ }
672+ current = oldValue ;
673+ }
674+ #endif
675+ }
651676
652677 public void close ( ) {
653678 // close is idempotent; it must never block
654- #if NET5_0_OR_GREATER
655- if ( ( Interlocked . Or ( ref _state , StateBits . Closed ) & StateBits . Closed ) != StateBits . Closed ) {
679+ if ( ( InterlockedOrState ( StateBits . Closed ) & StateBits . Closed ) != StateBits . Closed ) {
656680 // freshly closed, release the construction time reference
657681 Release ( exclusive : false ) ;
658682 }
659- #else
660- int oldState , newState ;
661- do {
662- oldState = _state ;
663- newState = oldState | StateBits . Closed ;
664- } while ( Interlocked . CompareExchange ( ref _state , newState , oldState ) != oldState ) ;
665- if ( ( oldState & StateBits . Closed ) != StateBits . Closed ) {
666- // freshly closed, release the construction time reference
667- Release ( exclusive : false ) ;
668- }
669- #endif
670683 }
671684
672685
@@ -897,7 +910,6 @@ public string readline() {
897910 }
898911 }
899912
900-
901913 public void resize ( long newsize ) {
902914 using ( new MmapLocker ( this , exclusive : true ) ) {
903915 if ( _fileAccess is not MemoryMappedFileAccess . ReadWrite and not MemoryMappedFileAccess . ReadWriteExecute ) {
@@ -931,13 +943,13 @@ public void resize(long newsize) {
931943 int fd = unchecked ( ( int ) _handle . DangerousGetHandle ( ) ) ;
932944 PythonNT . ftruncateUnix ( fd , newsize ) ;
933945
934- #if NET8_0_OR_GREATER
946+ #if NET8_0_OR_GREATER
935947 _file = MemoryMappedFile . CreateFromFile ( _handle , _mapName , newsize , _fileAccess , HandleInheritability . None , leaveOpen : true ) ;
936- #else
948+ #else
937949 _sourceStream ? . Dispose ( ) ;
938950 _sourceStream = new FileStream ( new SafeFileHandle ( ( IntPtr ) fd , ownsHandle : false ) , FileAccess . ReadWrite ) ;
939951 _file = CreateFromFile ( _sourceStream , _mapName , newsize , _fileAccess , HandleInheritability . None , leaveOpen : true ) ;
940- #endif
952+ #endif
941953 _view = _file . CreateViewAccessor ( _offset , newsize , _fileAccess ) ;
942954 return ;
943955 } catch {
@@ -1168,8 +1180,10 @@ private long Position {
11681180 }
11691181 }
11701182
1183+ private bool IsReadOnly => _fileAccess is MemoryMappedFileAccess . Read or MemoryMappedFileAccess . ReadExecute ;
1184+
11711185 private void EnsureWritable ( ) {
1172- if ( _fileAccess is MemoryMappedFileAccess . Read or MemoryMappedFileAccess . ReadExecute ) {
1186+ if ( IsReadOnly ) {
11731187 throw PythonOps . TypeError ( "mmap can't modify a read-only memory map." ) ;
11741188 }
11751189 }
@@ -1299,8 +1313,77 @@ void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
12991313 }
13001314
13011315 #endregion
1302- }
13031316
1317+ public IPythonBuffer GetBuffer ( BufferFlags flags = BufferFlags . Simple ) {
1318+ if ( flags . HasFlag ( BufferFlags . Writable ) && IsReadOnly )
1319+ throw PythonOps . BufferError ( "Object is not writable." ) ;
1320+
1321+ return new MmapBuffer ( this , flags ) ;
1322+ }
1323+
1324+ private sealed unsafe class MmapBuffer : IPythonBuffer {
1325+ private readonly MmapDefault _mmap ;
1326+ private readonly MmapLocker _locker ;
1327+ private readonly BufferFlags _flags ;
1328+ private SafeMemoryMappedViewHandle ? _handle ;
1329+ private byte * _pointer = null ;
1330+
1331+ public MmapBuffer ( MmapDefault mmap , BufferFlags flags ) {
1332+ _mmap = mmap ;
1333+ _flags = flags ;
1334+ _locker = new MmapLocker ( mmap ) ;
1335+ mmap . InterlockedOrState ( StateBits . Exporting ) ;
1336+ _handle = _mmap . _view . SafeMemoryMappedViewHandle ;
1337+ ItemCount = _mmap . __len__ ( ) is int i ? i : throw new NotImplementedException ( ) ;
1338+ }
1339+
1340+ public object Object => _mmap ;
1341+
1342+ public bool IsReadOnly => _mmap . IsReadOnly ;
1343+
1344+ public int Offset => 0 ;
1345+
1346+ public string ? Format => _flags . HasFlag ( BufferFlags . Format ) ? "B" : null ;
1347+
1348+ public int ItemCount { get ; }
1349+
1350+ public int ItemSize => 1 ;
1351+
1352+ public int NumOfDims => 1 ;
1353+
1354+ public IReadOnlyList < int > ? Shape => null ;
1355+
1356+ public IReadOnlyList < int > ? Strides => null ;
1357+
1358+ public IReadOnlyList < int > ? SubOffsets => null ;
1359+
1360+ public unsafe ReadOnlySpan < byte > AsReadOnlySpan ( ) {
1361+ if ( _handle is null ) throw new ObjectDisposedException ( nameof ( MmapBuffer ) ) ;
1362+ if ( _pointer is null ) _handle . AcquirePointer ( ref _pointer ) ;
1363+ return new ReadOnlySpan < byte > ( _pointer , ItemCount ) ;
1364+ }
1365+
1366+ public unsafe Span < byte > AsSpan ( ) {
1367+ if ( _handle is null ) throw new ObjectDisposedException ( nameof ( MmapBuffer ) ) ;
1368+ if ( IsReadOnly ) throw new InvalidOperationException ( "object is not writable" ) ;
1369+ if ( _pointer is null ) _handle . AcquirePointer ( ref _pointer ) ;
1370+ return new Span < byte > ( _pointer , ItemCount ) ;
1371+ }
1372+
1373+ public unsafe MemoryHandle Pin ( ) {
1374+ if ( _handle is null ) throw new ObjectDisposedException ( nameof ( MmapBuffer ) ) ;
1375+ if ( _pointer is null ) _handle . AcquirePointer ( ref _pointer ) ;
1376+ return new MemoryHandle ( _pointer ) ;
1377+ }
1378+
1379+ public void Dispose ( ) {
1380+ var handle = Interlocked . Exchange ( ref _handle , null ) ;
1381+ if ( handle is null ) return ;
1382+ if ( _pointer is not null ) handle . ReleasePointer ( ) ;
1383+ _locker . Dispose ( ) ;
1384+ }
1385+ }
1386+ }
13041387
13051388 #region P/Invoke for allocation granularity
13061389
0 commit comments