[Python-Dev] ctypes, memory mapped files and context manager
Hans-Peter Jansen
hpj at urpla.net
Wed Jan 4 19:28:27 EST 2017
Hi,
first of all, sorry for being such a pest, but all former attempts to solve
this issue on other fora has been grinding to a halt.
In short: I try to combine a context manager with ctypes structures on memory
mapped files in order to manage huge binary files. (An approach, that performs
great, while easy to use and keeping the resource usage low).
FWIW, the code is targeted for Linux environments running Python3.
The smallest script demonstrating the issue (thanks to Peter Otten):
import ctypes
import mmap
from contextlib import contextmanager
class T(ctypes.Structure):
_fields = [("foo", ctypes.c_uint32)]
@contextmanager
def map_struct(m, n):
m.resize(n * mmap.PAGESIZE)
yield T.from_buffer(m)
SIZE = mmap.PAGESIZE * 2
f = open("tmp.dat", "w+b")
f.write(b"\0" * SIZE)
f.seek(0)
m = mmap.mmap(f.fileno(), mmap.PAGESIZE)
with map_struct(m, 1) as a:
a.foo = 1
with map_struct(m, 2) as b:
b.foo = 2
resulting in:
$ python3 mmap_test.py
Traceback (most recent call last):
File "mmap_test.py", line 23, in <module>
with map_struct(m, 2) as b:
File "/usr/lib64/python3.4/contextlib.py", line 59, in __enter__
return next(self.gen)
File "mmap_test.py", line 12, in map_struct
m.resize(n * mmap.PAGESIZE)
BufferError: mmap can't resize with extant buffers exported.
Python2 does not crash, but that's a different story. What happens here is:
the context manager variable "a" keeps a reference to a memory mapped area
alive, that results in a unresizable and not properly closable mmap.
Right now, this rather ugly and error prone workaround must be used, that
renders the purpose of the context manager ad absurdum:
with map_struct(m, 1) as a:
a.foo = 1
del a
with map_struct(m, 2) as b:
b.foo = 2
del b
In order to get this working properly, the ctypes mapping needs a method to
free the mapping actively. E.g.:
@contextmanager
def map_struct(m, n):
m.resize(n * mmap.PAGESIZE)
yield T.from_buffer(m)
T.unmap_buffer(m)
Other attempts with weakref and the like do not work due to the nature of the
ctypes types.
My own investigations in the _ctypes module were unsuccessful so far.
Hopefully, somebody in the audience cares enough for this module in order to
get this fixed up (or probably I'm missing something obvious..).
Any ideas are very much appreciated.
Pete
More information about the Python-Dev
mailing list