https://github.com/python/cpython/commit/1af21ea32043ad5bd4eaacd48a1718d4e0bef945
commit: 1af21ea32043ad5bd4eaacd48a1718d4e0bef945
branch: main
author: AN Long <[email protected]>
committer: vstinner <[email protected]>
date: 2025-12-27T10:33:56Z
summary:

gh-63016: Add flags parameter on mmap.flush (#139553)

Co-authored-by: Victor Stinner <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-10-04-20-48-02.gh-issue-63016.EC9QN_.rst
M Doc/library/mmap.rst
M Lib/test/test_mmap.py
M Modules/clinic/mmapmodule.c.h
M Modules/mmapmodule.c

diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst
index 41b90f2c3b3111..28b2d1e244787a 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 <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]])
 
@@ -461,3 +470,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 aad916ecfe2c27..bc3593ce4ba992 100644
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -1166,6 +1166,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)
+
     @unittest.skipUnless(sys.platform == 'linux', 'Linux only')
     @support.requires_linux_version(5, 17, 0)
     def test_set_name(self):
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/clinic/mmapmodule.c.h b/Modules/clinic/mmapmodule.c.h
index b63f7df2a7e334..db640800ad780f 100644
--- a/Modules/clinic/mmapmodule.c.h
+++ b/Modules/clinic/mmapmodule.c.h
@@ -2,6 +2,10 @@
 preserve
 [clinic start generated code]*/
 
+#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+#  include "pycore_gc.h"          // PyGC_Head
+#  include "pycore_runtime.h"     // _Py_ID()
+#endif
 #include "pycore_abstract.h"      // _PyNumber_Index()
 #include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
 #include "pycore_modsupport.h"    // _PyArg_CheckPositional()
@@ -371,29 +375,63 @@ mmap_mmap_tell(PyObject *self, PyObject 
*Py_UNUSED(ignored))
 }
 
 PyDoc_STRVAR(mmap_mmap_flush__doc__,
-"flush($self, offset=0, size=-1, /)\n"
+"flush($self, offset=0, size=-1, /, *, flags=0)\n"
 "--\n"
 "\n");
 
 #define MMAP_MMAP_FLUSH_METHODDEF    \
-    {"flush", _PyCFunction_CAST(mmap_mmap_flush), METH_FASTCALL, 
mmap_mmap_flush__doc__},
+    {"flush", _PyCFunction_CAST(mmap_mmap_flush), METH_FASTCALL|METH_KEYWORDS, 
mmap_mmap_flush__doc__},
 
 static PyObject *
-mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size);
+mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size,
+                     int flags);
 
 static PyObject *
-mmap_mmap_flush(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+mmap_mmap_flush(PyObject *self, PyObject *const *args, Py_ssize_t nargs, 
PyObject *kwnames)
 {
     PyObject *return_value = NULL;
+    #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+    #define NUM_KEYWORDS 1
+    static struct {
+        PyGC_Head _this_is_not_used;
+        PyObject_VAR_HEAD
+        Py_hash_t ob_hash;
+        PyObject *ob_item[NUM_KEYWORDS];
+    } _kwtuple = {
+        .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+        .ob_hash = -1,
+        .ob_item = { &_Py_ID(flags), },
+    };
+    #undef NUM_KEYWORDS
+    #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+    #else  // !Py_BUILD_CORE
+    #  define KWTUPLE NULL
+    #endif  // !Py_BUILD_CORE
+
+    static const char * const _keywords[] = {"", "", "flags", NULL};
+    static _PyArg_Parser _parser = {
+        .keywords = _keywords,
+        .fname = "flush",
+        .kwtuple = KWTUPLE,
+    };
+    #undef KWTUPLE
+    PyObject *argsbuf[3];
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 
0;
     Py_ssize_t offset = 0;
     Py_ssize_t size = -1;
+    int flags = 0;
 
-    if (!_PyArg_CheckPositional("flush", nargs, 0, 2)) {
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser,
+            /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf);
+    if (!args) {
         goto exit;
     }
     if (nargs < 1) {
-        goto skip_optional;
+        goto skip_optional_posonly;
     }
+    noptargs--;
     {
         Py_ssize_t ival = -1;
         PyObject *iobj = _PyNumber_Index(args[0]);
@@ -407,8 +445,9 @@ mmap_mmap_flush(PyObject *self, PyObject *const *args, 
Py_ssize_t nargs)
         offset = ival;
     }
     if (nargs < 2) {
-        goto skip_optional;
+        goto skip_optional_posonly;
     }
+    noptargs--;
     {
         Py_ssize_t ival = -1;
         PyObject *iobj = _PyNumber_Index(args[1]);
@@ -421,9 +460,17 @@ mmap_mmap_flush(PyObject *self, PyObject *const *args, 
Py_ssize_t nargs)
         }
         size = ival;
     }
-skip_optional:
+skip_optional_posonly:
+    if (!noptargs) {
+        goto skip_optional_kwonly;
+    }
+    flags = PyLong_AsInt(args[2]);
+    if (flags == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+skip_optional_kwonly:
     Py_BEGIN_CRITICAL_SECTION(self);
-    return_value = mmap_mmap_flush_impl((mmap_object *)self, offset, size);
+    return_value = mmap_mmap_flush_impl((mmap_object *)self, offset, size, 
flags);
     Py_END_CRITICAL_SECTION();
 
 exit:
@@ -832,4 +879,4 @@ mmap_mmap_madvise(PyObject *self, PyObject *const *args, 
Py_ssize_t nargs)
 #ifndef MMAP_MMAP_MADVISE_METHODDEF
     #define MMAP_MMAP_MADVISE_METHODDEF
 #endif /* !defined(MMAP_MMAP_MADVISE_METHODDEF) */
-/*[clinic end generated code: output=fd9ca0ef425af934 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=8389e3c8e3db3a78 input=a9049054013a1b77]*/
diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c
index 0928ea6a8b70ba..16e3c0ecefd05d 100644
--- a/Modules/mmapmodule.c
+++ b/Modules/mmapmodule.c
@@ -1034,12 +1034,15 @@ mmap.mmap.flush
     offset: Py_ssize_t = 0
     size: Py_ssize_t = -1
     /
+    *
+    flags: int = 0
 
 [clinic start generated code]*/
 
 static PyObject *
-mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size)
-/*[clinic end generated code: output=956ced67466149cf input=c50b893bc69520ec]*/
+mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size,
+                     int flags)
+/*[clinic end generated code: output=4225f4174dc75a53 input=42ba5fb716b6c294]*/
 {
     CHECK_VALID(NULL);
     if (size == -1) {
@@ -1060,8 +1063,10 @@ mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t 
offset, Py_ssize_t size)
     }
     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;
     }
@@ -2331,6 +2336,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

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to