|
5 | 5 | #if FEATURE_MMAP |
6 | 6 |
|
7 | 7 | using System; |
| 8 | +using System.Buffers; |
| 9 | +using System.Collections.Generic; |
8 | 10 | using System.ComponentModel; |
9 | 11 | using System.Diagnostics; |
10 | 12 | using System.Globalization; |
@@ -277,7 +279,7 @@ private static MemoryMappedFileAccess ToMmapFileAccess(int access) { |
277 | 279 | } |
278 | 280 |
|
279 | 281 | [PythonHidden] |
280 | | - public class MmapDefault : IWeakReferenceable { |
| 282 | + public class MmapDefault : IWeakReferenceable, IBufferProtocol { |
281 | 283 | private MemoryMappedFile _file; |
282 | 284 | private MemoryMappedViewAccessor _view; |
283 | 285 | private long _position; |
@@ -570,12 +572,20 @@ public void __exit__(CodeContext/*!*/ context, params object[] excinfo) { |
570 | 572 | public bool closed => _isClosed; |
571 | 573 |
|
572 | 574 | public void close() { |
573 | | - if (!_isClosed) { |
574 | | - lock (this) { |
575 | | - if (!_isClosed) { |
576 | | - _isClosed = true; |
577 | | - CloseWorker(); |
578 | | - } |
| 575 | + if (_isClosed) return; |
| 576 | + |
| 577 | + if (_refCount > 1) { |
| 578 | + GC.Collect(); |
| 579 | + GC.WaitForPendingFinalizers(); |
| 580 | + if (_refCount > 1) { |
| 581 | + throw PythonOps.BufferError("cannot close exported pointers exist"); |
| 582 | + } |
| 583 | + } |
| 584 | + |
| 585 | + lock (this) { |
| 586 | + if (!_isClosed) { |
| 587 | + _isClosed = true; |
| 588 | + CloseWorker(); |
579 | 589 | } |
580 | 590 | } |
581 | 591 | } |
@@ -820,6 +830,14 @@ public string readline() { |
820 | 830 | } |
821 | 831 |
|
822 | 832 | public void resize(long newsize) { |
| 833 | + if (_refCount > 1) { |
| 834 | + GC.Collect(); |
| 835 | + GC.WaitForPendingFinalizers(); |
| 836 | + if (_refCount > 1) { |
| 837 | + throw PythonOps.BufferError("mmap can't resize with extant buffers exported."); |
| 838 | + } |
| 839 | + } |
| 840 | + |
823 | 841 | using (new MmapLocker(this)) { |
824 | 842 | if (_fileAccess is not MemoryMappedFileAccess.ReadWrite and not MemoryMappedFileAccess.ReadWriteExecute) { |
825 | 843 | throw PythonOps.TypeError("mmap can't resize a readonly or copy-on-write memory map."); |
@@ -1076,8 +1094,10 @@ private long Position { |
1076 | 1094 | } |
1077 | 1095 | } |
1078 | 1096 |
|
| 1097 | + private bool IsReadOnly => _fileAccess is MemoryMappedFileAccess.Read or MemoryMappedFileAccess.ReadExecute; |
| 1098 | + |
1079 | 1099 | private void EnsureWritable() { |
1080 | | - if (_fileAccess is MemoryMappedFileAccess.Read or MemoryMappedFileAccess.ReadExecute) { |
| 1100 | + if (IsReadOnly) { |
1081 | 1101 | throw PythonOps.TypeError("mmap can't modify a read-only memory map."); |
1082 | 1102 | } |
1083 | 1103 | } |
@@ -1198,6 +1218,84 @@ void IWeakReferenceable.SetFinalizer(WeakRefTracker value) { |
1198 | 1218 | } |
1199 | 1219 |
|
1200 | 1220 | #endregion |
| 1221 | + |
| 1222 | +#nullable enable |
| 1223 | + |
| 1224 | + public IPythonBuffer GetBuffer(BufferFlags flags = BufferFlags.Simple) { |
| 1225 | + if (flags.HasFlag(BufferFlags.Writable) && IsReadOnly) |
| 1226 | + throw PythonOps.BufferError("Object is not writable."); |
| 1227 | + |
| 1228 | + return new MmapBuffer(this, flags); |
| 1229 | + } |
| 1230 | + |
| 1231 | + private sealed class MmapBuffer : IPythonBuffer { |
| 1232 | + private readonly MmapDefault _mmap; |
| 1233 | + private readonly BufferFlags _flags; |
| 1234 | + private SafeMemoryMappedViewHandle? _handle; |
| 1235 | + |
| 1236 | + public MmapBuffer(MmapDefault mmap, BufferFlags flags) { |
| 1237 | + mmap.EnsureOpen(); |
| 1238 | + |
| 1239 | + _mmap = mmap; |
| 1240 | + _flags = flags; |
| 1241 | + Interlocked.Increment(ref _mmap._refCount); |
| 1242 | + _handle = _mmap._view.SafeMemoryMappedViewHandle; |
| 1243 | + ItemCount = _mmap.__len__() is int i ? i : throw new NotImplementedException(); |
| 1244 | + } |
| 1245 | + |
| 1246 | + public object Object => _mmap; |
| 1247 | + |
| 1248 | + public bool IsReadOnly => _mmap.IsReadOnly; |
| 1249 | + |
| 1250 | + public int Offset => 0; |
| 1251 | + |
| 1252 | + public string? Format => _flags.HasFlag(BufferFlags.Format) ? "B" : null; |
| 1253 | + |
| 1254 | + public int ItemCount { get; } |
| 1255 | + |
| 1256 | + public int ItemSize => 1; |
| 1257 | + |
| 1258 | + public int NumOfDims => 1; |
| 1259 | + |
| 1260 | + public IReadOnlyList<int>? Shape => null; |
| 1261 | + |
| 1262 | + public IReadOnlyList<int>? Strides => null; |
| 1263 | + |
| 1264 | + public IReadOnlyList<int>? SubOffsets => null; |
| 1265 | + |
| 1266 | + public unsafe ReadOnlySpan<byte> AsReadOnlySpan() { |
| 1267 | + if (_handle is null) throw new ObjectDisposedException(nameof(MmapBuffer)); |
| 1268 | + byte* pointer = null; |
| 1269 | + _handle.AcquirePointer(ref pointer); |
| 1270 | + return new ReadOnlySpan<byte>(pointer, ItemCount); |
| 1271 | + } |
| 1272 | + |
| 1273 | + public unsafe Span<byte> AsSpan() { |
| 1274 | + if (_handle is null) throw new ObjectDisposedException(nameof(MmapBuffer)); |
| 1275 | + if (IsReadOnly) throw new InvalidOperationException("object is not writable"); |
| 1276 | + byte* pointer = null; |
| 1277 | + _handle.AcquirePointer(ref pointer); |
| 1278 | + return new Span<byte>(pointer, ItemCount); |
| 1279 | + } |
| 1280 | + |
| 1281 | + public unsafe MemoryHandle Pin() { |
| 1282 | + if (_handle is null) throw new ObjectDisposedException(nameof(MmapBuffer)); |
| 1283 | + byte* pointer = null; |
| 1284 | + _handle.AcquirePointer(ref pointer); |
| 1285 | + return new MemoryHandle(pointer); |
| 1286 | + } |
| 1287 | + |
| 1288 | + public void Dispose() { |
| 1289 | + var handle = Interlocked.Exchange(ref _handle, null); |
| 1290 | + if (handle is null) return; |
| 1291 | + handle.Dispose(); |
| 1292 | + _mmap.CloseWorker(); |
| 1293 | + } |
| 1294 | + |
| 1295 | + } |
| 1296 | + |
| 1297 | +#nullable restore |
| 1298 | + |
1201 | 1299 | } |
1202 | 1300 |
|
1203 | 1301 | #region P/Invoke for allocation granularity |
|
0 commit comments