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

Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
AN Long 2025-12-27 19:33:56 +09:00 committed by GitHub
parent 57d569942c
commit 1af21ea320
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 115 additions and 15 deletions

View file

@ -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

View file

@ -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):

View file

@ -0,0 +1 @@
Add a ``flags`` parameter to :meth:`mmap.mmap.flush` to control synchronization behavior.

View file

@ -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 @@ exit:
#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]*/

View file

@ -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