Watch at EaseStories. Answers. Perspective.

Technology · PRACTICAL GUIDE

mmap cannot close exported pointers? Trace every borrower before changing the array

A released parent or deleted array name can leave a slice or alias alive. Prove whether the result borrows the mapping or owns an independent copy.

Original diagram comparing six active borrowed-buffer cases and two independent-copy cases on CPython3.12.14 and NumPy2.3.5.
Original technical illustration: six borrowed cases blocked close until cleanup; two independent copies retained values after closure. Local CPython3.12.14/NumPy2.3.5 only. Watch at Ease

BufferError at mmap.close() can be the visible sign that a borrower still needs the memory. Removing that exception by changing an array constructor does not by itself establish a safe lifetime.

Start with a reference graph: mapping, direct views, derived views and every retained array handle. Eight local controls distinguish releasing a name, ending a borrow and creating independently owned data.

1. Name the owner and the escaping borrowers

Record Python and NumPy versions, the mapping constructor, array dtype and where each result escapes. np.frombuffer creates a view into its input. Basic array slicing can create another view, and alias = arr creates another name for the same array. Neither operation promises an independent data buffer.

Choose the API contract before repairing cleanup. A borrowed result requires the mapping owner to remain alive until all users finish. A result that must outlive that owner needs independent storage. Read-only describes permitted writes through a view; it does not turn borrowed memory into an owned copy.

2. What the eight local controls showed

We used CPython 3.12.14 and NumPy 2.3.5 on Linux with fresh eight-byte anonymous mappings containing values 0 through 7. A direct memoryview blocked close until release(). A sliced memoryview still blocked close after its parent was released; releasing the slice allowed close. A derived read-only memoryview behaved the same way.

np.frombuffer(mm,dtype=np.uint8) blocked close while its array lived. Keeping arr[:2] alive still blocked close after deleting arr. Keeping alias = arr did too. Removing each remaining NumPy borrower reference allowed closure in these simple CPython fixtures. This does not make del arr a general cleanup guarantee in code with caches, containers, closures or reference cycles.

3. A copy changes the lifetime contract

In the seventh case, owned = arr.copy() created an independent uint8 array. After dropping the borrowed array, the map closed and owned.tolist() still returned 0 through 7. In the eighth, owned = mm[:2] produced independent bytes; after close it retained values 0 and 1. These are copied results, with extra allocation and no continuing synchronization with the mapping.

Make the copy while the source is valid and only for the region that must escape. A local deletion cannot invalidate another caller’s alias. Do not manually release a NumPy array’s private base chain, suppress the close failure or switch constructors merely to bypass a guard. If zero-copy is required, extend the owner’s lifetime through an explicit application contract.

4. Verify shutdown and surviving data separately

For memoryviews, end each retained child view and its parent in a deliberate scope; context managers help only for the views actually managed. For NumPy, ensure all borrowed slices and aliases have ended before closing their owner. On failure, inspect the object graph rather than repeatedly attempting close or assuming garbage collection proves that no borrower remains.

Our eight cases asserted successful closure after their exact cleanup. Only independent copies were read after close; no borrowed pointer was dereferenced after unmapping. We tested no real files, shared-memory unlinking, concurrent users or other platforms. In the application, verify the intended shutdown path and copied result values separately. File flushing and persistence are another contract, not a cure for outstanding exported views.

Original diagram distinguishing mapping owner lifetime, independent copied data and separate shutdown/value checks.
Original technical illustration: borrowed data needs its owner; independent copies can outlive mapping closure. Verify shutdown and ownership separately. Watch at Ease