diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst index b6ffb5cebc020e..3d5e2c9964e678 100644 --- a/Doc/library/mmap.rst +++ b/Doc/library/mmap.rst @@ -212,7 +212,7 @@ To map anonymous memory, -1 should be passed as the fileno along with the length Writable :term:`bytes-like object` is now accepted. - .. method:: flush([offset[, size]]) + .. method:: flush([offset[, size]], *, flags=MS_SYNC) Flushes changes made to the in-memory copy of a file back to disk. Without use of this call there is no guarantee that changes are written back before @@ -221,6 +221,12 @@ To map anonymous memory, -1 should be passed as the fileno along with the length whole extent of the mapping is flushed. *offset* must be a multiple of the :const:`PAGESIZE` or :const:`ALLOCATIONGRANULARITY`. + The *flags* parameter specifies the synchronization behavior. + *flags* must be one of the :ref:`MS_* constants ` available + on the system. + + On Windows, the *flags* parameter is ignored. + ``None`` is returned to indicate success. An exception is raised when the call failed. @@ -235,6 +241,9 @@ To map anonymous memory, -1 should be passed as the fileno along with the length specified alone, and the flush operation will extend from *offset* to the end of the mmap. + .. versionchanged:: next + Added *flags* parameter to control synchronization behavior. + .. method:: madvise(option[, start[, length]]) @@ -450,3 +459,22 @@ MAP_* Constants :data:`MAP_TPRO`, :data:`MAP_TRANSLATED_ALLOW_EXECUTE`, and :data:`MAP_UNIX03` constants. +.. _ms-constants: + +MS_* Constants +++++++++++++++ + +.. data:: MS_SYNC + MS_ASYNC + MS_INVALIDATE + + These flags control the synchronization behavior for :meth:`mmap.flush`: + + * :data:`MS_SYNC` - Synchronous flush: writes are scheduled and the call + blocks until they are physically written to storage. + * :data:`MS_ASYNC` - Asynchronous flush: writes are scheduled but the call + returns immediately without waiting for completion. + * :data:`MS_INVALIDATE` - Invalidate cached data: invalidates other mappings + of the same file so they can see the changes. + + .. versionadded:: next diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 0571eed23f72dc..bd79d8c3bf7f74 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -1157,6 +1157,15 @@ def test_flush_parameters(self): m.flush(PAGESIZE) m.flush(PAGESIZE, PAGESIZE) + if hasattr(mmap, 'MS_SYNC'): + m.flush(0, PAGESIZE, flags=mmap.MS_SYNC) + if hasattr(mmap, 'MS_ASYNC'): + m.flush(flags=mmap.MS_ASYNC) + if hasattr(mmap, 'MS_INVALIDATE'): + m.flush(PAGESIZE * 2, flags=mmap.MS_INVALIDATE) + if hasattr(mmap, 'MS_ASYNC') and hasattr(mmap, 'MS_INVALIDATE'): + m.flush(0, PAGESIZE, flags=mmap.MS_ASYNC | mmap.MS_INVALIDATE) + class LargeMmapTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-04-20-48-02.gh-issue-63016.EC9QN_.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-04-20-48-02.gh-issue-63016.EC9QN_.rst new file mode 100644 index 00000000000000..a0aee6ce83a508 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-04-20-48-02.gh-issue-63016.EC9QN_.rst @@ -0,0 +1 @@ +Add a ``flags`` parameter to :meth:`mmap.mmap.flush` to control synchronization behavior. diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 41d117162882d7..9e3ae6d161afe0 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -930,13 +930,16 @@ mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored)) } static PyObject * -mmap_flush_method(PyObject *op, PyObject *args) +mmap_flush_method(PyObject *op, PyObject *args, PyObject *kwargs) { Py_ssize_t offset = 0; Py_ssize_t size = -1; + int flags = 0; mmap_object *self = mmap_object_CAST(op); + static char *kwlist[] = {"offset", "size", "flags", NULL}; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size)) { + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|nn$i:flush", kwlist, + &offset, &size, &flags)) { return NULL; } if (size == -1) { @@ -957,8 +960,10 @@ mmap_flush_method(PyObject *op, PyObject *args) } Py_RETURN_NONE; #elif defined(UNIX) - /* XXX flags for msync? */ - if (-1 == msync(self->data + offset, size, MS_SYNC)) { + if (flags == 0) { + flags = MS_SYNC; + } + if (-1 == msync(self->data + offset, size, flags)) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -1203,7 +1208,7 @@ static struct PyMethodDef mmap_object_methods[] = { {"close", mmap_close_method, METH_NOARGS}, {"find", mmap_find_method, METH_VARARGS}, {"rfind", mmap_rfind_method, METH_VARARGS}, - {"flush", mmap_flush_method, METH_VARARGS}, + {"flush", _PyCFunction_CAST(mmap_flush_method), METH_VARARGS | METH_KEYWORDS}, #ifdef HAVE_MADVISE {"madvise", mmap_madvise_method, METH_VARARGS}, #endif @@ -2047,6 +2052,16 @@ mmap_exec(PyObject *module) ADD_INT_MACRO(module, ACCESS_WRITE); ADD_INT_MACRO(module, ACCESS_COPY); +#ifdef MS_INVALIDATE + ADD_INT_MACRO(module, MS_INVALIDATE); +#endif +#ifdef MS_ASYNC + ADD_INT_MACRO(module, MS_ASYNC); +#endif +#ifdef MS_SYNC + ADD_INT_MACRO(module, MS_SYNC); +#endif + #ifdef HAVE_MADVISE // Conventional advice values #ifdef MADV_NORMAL