]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-116738: make `mmap` module thread-safe (GH-139237) (#139825)
authorKumar Aditya <kumaraditya@python.org>
Thu, 9 Oct 2025 14:49:03 +0000 (20:19 +0530)
committerGitHub <noreply@github.com>
Thu, 9 Oct 2025 14:49:03 +0000 (20:19 +0530)
* [3.14] gh-116738: make `mmap` module thread-safe (GH-139237)
(cherry picked from commit 7f155f9c46e2dd7dec5e12ebec3323dfd95fe1f0)

Co-authored-by: Alper <alperyoney@fb.com>
Lib/test/test_free_threading/test_mmap.py [new file with mode: 0644]
Lib/test/test_mmap.py
Misc/NEWS.d/next/Core_and_Builtins/2025-09-21-14-33-17.gh-issue-116738.vNaI4h.rst [new file with mode: 0644]
Modules/clinic/mmapmodule.c.h [new file with mode: 0644]
Modules/mmapmodule.c

diff --git a/Lib/test/test_free_threading/test_mmap.py b/Lib/test/test_free_threading/test_mmap.py
new file mode 100644 (file)
index 0000000..e23a0ee
--- /dev/null
@@ -0,0 +1,270 @@
+import unittest
+
+from test.support import import_helper, threading_helper
+from test.support.threading_helper import run_concurrently
+
+import os
+import string
+import tempfile
+import threading
+
+from collections import Counter
+
+mmap = import_helper.import_module("mmap")
+
+NTHREADS = 10
+ANONYMOUS_MEM = -1
+
+
+@threading_helper.requires_working_threading()
+class MmapTests(unittest.TestCase):
+    def test_read_and_read_byte(self):
+        ascii_uppercase = string.ascii_uppercase.encode()
+        # Choose a total mmap size that evenly divides across threads and the
+        # read pattern (3 bytes per loop).
+        mmap_size = 3 * NTHREADS * len(ascii_uppercase)
+        num_bytes_to_read_per_thread = mmap_size // NTHREADS
+        bytes_read_from_mmap = []
+
+        def read(mm_obj):
+            nread = 0
+            while nread < num_bytes_to_read_per_thread:
+                b = mm_obj.read_byte()
+                bytes_read_from_mmap.append(b)
+                b = mm_obj.read(2)
+                bytes_read_from_mmap.extend(b)
+                nread += 3
+
+        with mmap.mmap(ANONYMOUS_MEM, mmap_size) as mm_obj:
+            for i in range(mmap_size // len(ascii_uppercase)):
+                mm_obj.write(ascii_uppercase)
+
+            mm_obj.seek(0)
+            run_concurrently(
+                worker_func=read,
+                args=(mm_obj,),
+                nthreads=NTHREADS,
+            )
+
+        self.assertEqual(len(bytes_read_from_mmap), mmap_size)
+        # Count each letter/byte to verify read correctness
+        counter = Counter(bytes_read_from_mmap)
+        self.assertEqual(len(counter), len(ascii_uppercase))
+        # Each letter/byte should be read (3 * NTHREADS) times
+        for letter in ascii_uppercase:
+            self.assertEqual(counter[letter], 3 * NTHREADS)
+
+    def test_readline(self):
+        num_lines = 1000
+        lines_read_from_mmap = []
+        expected_lines = []
+
+        def readline(mm_obj):
+            for i in range(num_lines // NTHREADS):
+                line = mm_obj.readline()
+                lines_read_from_mmap.append(line)
+
+        # Allocate mmap enough for num_lines (max line 5 bytes including NL)
+        with mmap.mmap(ANONYMOUS_MEM, num_lines * 5) as mm_obj:
+            for i in range(num_lines):
+                line = b"%d\n" % i
+                mm_obj.write(line)
+                expected_lines.append(line)
+
+            mm_obj.seek(0)
+            run_concurrently(
+                worker_func=readline,
+                args=(mm_obj,),
+                nthreads=NTHREADS,
+            )
+
+        self.assertEqual(len(lines_read_from_mmap), num_lines)
+        # Every line should be read once by threads; order is non-deterministic
+        # Sort numerically by integer value
+        lines_read_from_mmap.sort(key=lambda x: int(x))
+        self.assertEqual(lines_read_from_mmap, expected_lines)
+
+    def test_write_and_write_byte(self):
+        thread_letters = list(string.ascii_uppercase)
+        self.assertLessEqual(NTHREADS, len(thread_letters))
+        per_thread_write_loop = 100
+
+        def write(mm_obj):
+            # Each thread picks a unique letter to write
+            thread_letter = thread_letters.pop(0)
+            thread_bytes = (thread_letter * 2).encode()
+            for _ in range(per_thread_write_loop):
+                mm_obj.write_byte(thread_bytes[0])
+                mm_obj.write(thread_bytes)
+
+        with mmap.mmap(
+            ANONYMOUS_MEM, per_thread_write_loop * 3 * NTHREADS
+        ) as mm_obj:
+            run_concurrently(
+                worker_func=write,
+                args=(mm_obj,),
+                nthreads=NTHREADS,
+            )
+            mm_obj.seek(0)
+            data = mm_obj.read()
+            self.assertEqual(len(data), NTHREADS * per_thread_write_loop * 3)
+            counter = Counter(data)
+            self.assertEqual(len(counter), NTHREADS)
+            # Each thread letter should be written `per_thread_write_loop` * 3
+            for letter in counter:
+                self.assertEqual(counter[letter], per_thread_write_loop * 3)
+
+    def test_move(self):
+        ascii_uppercase = string.ascii_uppercase.encode()
+        num_letters = len(ascii_uppercase)
+
+        def move(mm_obj):
+            for i in range(num_letters):
+                # Move 1 byte from the first half to the second half
+                mm_obj.move(0 + i, num_letters + i, 1)
+
+        with mmap.mmap(ANONYMOUS_MEM, 2 * num_letters) as mm_obj:
+            mm_obj.write(ascii_uppercase)
+            run_concurrently(
+                worker_func=move,
+                args=(mm_obj,),
+                nthreads=NTHREADS,
+            )
+
+    def test_seek_and_tell(self):
+        seek_per_thread = 10
+
+        def seek(mm_obj):
+            self.assertTrue(mm_obj.seekable())
+            for _ in range(seek_per_thread):
+                before_seek = mm_obj.tell()
+                mm_obj.seek(1, os.SEEK_CUR)
+                self.assertLess(before_seek, mm_obj.tell())
+
+        with mmap.mmap(ANONYMOUS_MEM, 1024) as mm_obj:
+            run_concurrently(
+                worker_func=seek,
+                args=(mm_obj,),
+                nthreads=NTHREADS,
+            )
+            # Each thread seeks from current position, the end position should
+            # be the sum of all seeks from all threads.
+            self.assertEqual(mm_obj.tell(), NTHREADS * seek_per_thread)
+
+    def test_slice_update_and_slice_read(self):
+        thread_letters = list(string.ascii_uppercase)
+        self.assertLessEqual(NTHREADS, len(thread_letters))
+
+        def slice_update_and_slice_read(mm_obj):
+            # Each thread picks a unique letter to write
+            thread_letter = thread_letters.pop(0)
+            thread_bytes = (thread_letter * 1024).encode()
+            for _ in range(100):
+                mm_obj[:] = thread_bytes
+                read_bytes = mm_obj[:]
+                # Read bytes should be all the same letter, showing no
+                # interleaving
+                self.assertTrue(all_same(read_bytes))
+
+        with mmap.mmap(ANONYMOUS_MEM, 1024) as mm_obj:
+            run_concurrently(
+                worker_func=slice_update_and_slice_read,
+                args=(mm_obj,),
+                nthreads=NTHREADS,
+            )
+
+    def test_item_update_and_item_read(self):
+        thread_indexes = [i for i in range(NTHREADS)]
+
+        def item_update_and_item_read(mm_obj):
+            # Each thread picks a unique index to write
+            thread_index = thread_indexes.pop()
+            for i in range(100):
+                mm_obj[thread_index] = i
+                self.assertEqual(mm_obj[thread_index], i)
+
+            # Read values set by other threads, all values
+            # should be less than '100'
+            for val in mm_obj:
+                self.assertLess(int.from_bytes(val), 100)
+
+        with mmap.mmap(ANONYMOUS_MEM, NTHREADS + 1) as mm_obj:
+            run_concurrently(
+                worker_func=item_update_and_item_read,
+                args=(mm_obj,),
+                nthreads=NTHREADS,
+            )
+
+    def test_close_and_closed(self):
+        def close_mmap(mm_obj):
+            mm_obj.close()
+            self.assertTrue(mm_obj.closed)
+
+        with mmap.mmap(ANONYMOUS_MEM, 1) as mm_obj:
+            run_concurrently(
+                worker_func=close_mmap,
+                args=(mm_obj,),
+                nthreads=NTHREADS,
+            )
+
+    def test_find_and_rfind(self):
+        per_thread_loop = 10
+
+        def find_and_rfind(mm_obj):
+            pattern = b'Thread-Ident:"%d"' % threading.get_ident()
+            mm_obj.write(pattern)
+            for _ in range(per_thread_loop):
+                found_at = mm_obj.find(pattern, 0)
+                self.assertNotEqual(found_at, -1)
+                # Should not find it after the `found_at`
+                self.assertEqual(mm_obj.find(pattern, found_at + 1), -1)
+                found_at_rev = mm_obj.rfind(pattern, 0)
+                self.assertEqual(found_at, found_at_rev)
+                # Should not find it after the `found_at`
+                self.assertEqual(mm_obj.rfind(pattern, found_at + 1), -1)
+
+        with mmap.mmap(ANONYMOUS_MEM, 1024) as mm_obj:
+            run_concurrently(
+                worker_func=find_and_rfind,
+                args=(mm_obj,),
+                nthreads=NTHREADS,
+            )
+
+    def test_mmap_export_as_memoryview(self):
+        """
+        Each thread creates a memoryview and updates the internal state of the
+        mmap object.
+        """
+        buffer_size = 42
+
+        def create_memoryview_from_mmap(mm_obj):
+            memoryviews = []
+            for _ in range(100):
+                mv = memoryview(mm_obj)
+                memoryviews.append(mv)
+                self.assertEqual(len(mv), buffer_size)
+                self.assertEqual(mv[:7], b"CPython")
+
+            # Cannot close the mmap while it is exported as buffers
+            with self.assertRaisesRegex(
+                BufferError, "cannot close exported pointers exist"
+            ):
+                mm_obj.close()
+
+        with mmap.mmap(ANONYMOUS_MEM, 42) as mm_obj:
+            mm_obj.write(b"CPython")
+            run_concurrently(
+                worker_func=create_memoryview_from_mmap,
+                args=(mm_obj,),
+                nthreads=NTHREADS,
+            )
+            # Implicit mm_obj.close() verifies all exports (memoryviews) are
+            # properly freed.
+
+
+def all_same(lst):
+    return all(item == lst[0] for item in lst)
+
+
+if __name__ == "__main__":
+    unittest.main()
index 38d1a496c30e213b6b90c4d2f16e661251f8cfb7..f468dda1d163c478333926e2259319e58d1c87fe 100644 (file)
@@ -887,6 +887,10 @@ class MmapTests(unittest.TestCase):
         size = 2 * PAGESIZE
         m = mmap.mmap(-1, size)
 
+        class Number:
+            def __index__(self):
+                return 2
+
         with self.assertRaisesRegex(ValueError, "madvise start out of bounds"):
             m.madvise(mmap.MADV_NORMAL, size)
         with self.assertRaisesRegex(ValueError, "madvise start out of bounds"):
@@ -895,10 +899,14 @@ class MmapTests(unittest.TestCase):
             m.madvise(mmap.MADV_NORMAL, 0, -1)
         with self.assertRaisesRegex(OverflowError, "madvise length too large"):
             m.madvise(mmap.MADV_NORMAL, PAGESIZE, sys.maxsize)
+        with self.assertRaisesRegex(
+                TypeError, "'str' object cannot be interpreted as an integer"):
+            m.madvise(mmap.MADV_NORMAL, PAGESIZE, "Not a Number")
         self.assertEqual(m.madvise(mmap.MADV_NORMAL), None)
         self.assertEqual(m.madvise(mmap.MADV_NORMAL, PAGESIZE), None)
         self.assertEqual(m.madvise(mmap.MADV_NORMAL, PAGESIZE, size), None)
         self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, 2), None)
+        self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, Number()), None)
         self.assertEqual(m.madvise(mmap.MADV_NORMAL, 0, size), None)
 
     def test_resize_up_anonymous_mapping(self):
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-21-14-33-17.gh-issue-116738.vNaI4h.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-21-14-33-17.gh-issue-116738.vNaI4h.rst
new file mode 100644 (file)
index 0000000..0668d57
--- /dev/null
@@ -0,0 +1,2 @@
+Make :mod:`mmap` thread-safe on the :term:`free threaded <free threading>`
+build.
diff --git a/Modules/clinic/mmapmodule.c.h b/Modules/clinic/mmapmodule.c.h
new file mode 100644 (file)
index 0000000..e00f82f
--- /dev/null
@@ -0,0 +1,769 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+#include "pycore_abstract.h"      // _PyNumber_Index()
+#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
+#include "pycore_modsupport.h"    // _PyArg_CheckPositional()
+
+PyDoc_STRVAR(mmap_mmap_close__doc__,
+"close($self, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_CLOSE_METHODDEF    \
+    {"close", (PyCFunction)mmap_mmap_close, METH_NOARGS, mmap_mmap_close__doc__},
+
+static PyObject *
+mmap_mmap_close_impl(mmap_object *self);
+
+static PyObject *
+mmap_mmap_close(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    PyObject *return_value = NULL;
+
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_close_impl((mmap_object *)self);
+    Py_END_CRITICAL_SECTION();
+
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_read_byte__doc__,
+"read_byte($self, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_READ_BYTE_METHODDEF    \
+    {"read_byte", (PyCFunction)mmap_mmap_read_byte, METH_NOARGS, mmap_mmap_read_byte__doc__},
+
+static PyObject *
+mmap_mmap_read_byte_impl(mmap_object *self);
+
+static PyObject *
+mmap_mmap_read_byte(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    PyObject *return_value = NULL;
+
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_read_byte_impl((mmap_object *)self);
+    Py_END_CRITICAL_SECTION();
+
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_readline__doc__,
+"readline($self, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_READLINE_METHODDEF    \
+    {"readline", (PyCFunction)mmap_mmap_readline, METH_NOARGS, mmap_mmap_readline__doc__},
+
+static PyObject *
+mmap_mmap_readline_impl(mmap_object *self);
+
+static PyObject *
+mmap_mmap_readline(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    PyObject *return_value = NULL;
+
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_readline_impl((mmap_object *)self);
+    Py_END_CRITICAL_SECTION();
+
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_read__doc__,
+"read($self, n=None, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_READ_METHODDEF    \
+    {"read", _PyCFunction_CAST(mmap_mmap_read), METH_FASTCALL, mmap_mmap_read__doc__},
+
+static PyObject *
+mmap_mmap_read_impl(mmap_object *self, Py_ssize_t num_bytes);
+
+static PyObject *
+mmap_mmap_read(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    Py_ssize_t num_bytes = PY_SSIZE_T_MAX;
+
+    if (!_PyArg_CheckPositional("read", nargs, 0, 1)) {
+        goto exit;
+    }
+    if (nargs < 1) {
+        goto skip_optional;
+    }
+    if (!_Py_convert_optional_to_ssize_t(args[0], &num_bytes)) {
+        goto exit;
+    }
+skip_optional:
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_read_impl((mmap_object *)self, num_bytes);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_find__doc__,
+"find($self, view, start=None, end=None, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_FIND_METHODDEF    \
+    {"find", _PyCFunction_CAST(mmap_mmap_find), METH_FASTCALL, mmap_mmap_find__doc__},
+
+static PyObject *
+mmap_mmap_find_impl(mmap_object *self, Py_buffer *view, PyObject *start,
+                    PyObject *end);
+
+static PyObject *
+mmap_mmap_find(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    Py_buffer view = {NULL, NULL};
+    PyObject *start = Py_None;
+    PyObject *end = Py_None;
+
+    if (!_PyArg_CheckPositional("find", nargs, 1, 3)) {
+        goto exit;
+    }
+    if (PyObject_GetBuffer(args[0], &view, PyBUF_SIMPLE) != 0) {
+        goto exit;
+    }
+    if (nargs < 2) {
+        goto skip_optional;
+    }
+    start = args[1];
+    if (nargs < 3) {
+        goto skip_optional;
+    }
+    end = args[2];
+skip_optional:
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_find_impl((mmap_object *)self, &view, start, end);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    /* Cleanup for view */
+    if (view.obj) {
+       PyBuffer_Release(&view);
+    }
+
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_rfind__doc__,
+"rfind($self, view, start=None, end=None, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_RFIND_METHODDEF    \
+    {"rfind", _PyCFunction_CAST(mmap_mmap_rfind), METH_FASTCALL, mmap_mmap_rfind__doc__},
+
+static PyObject *
+mmap_mmap_rfind_impl(mmap_object *self, Py_buffer *view, PyObject *start,
+                     PyObject *end);
+
+static PyObject *
+mmap_mmap_rfind(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    Py_buffer view = {NULL, NULL};
+    PyObject *start = Py_None;
+    PyObject *end = Py_None;
+
+    if (!_PyArg_CheckPositional("rfind", nargs, 1, 3)) {
+        goto exit;
+    }
+    if (PyObject_GetBuffer(args[0], &view, PyBUF_SIMPLE) != 0) {
+        goto exit;
+    }
+    if (nargs < 2) {
+        goto skip_optional;
+    }
+    start = args[1];
+    if (nargs < 3) {
+        goto skip_optional;
+    }
+    end = args[2];
+skip_optional:
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_rfind_impl((mmap_object *)self, &view, start, end);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    /* Cleanup for view */
+    if (view.obj) {
+       PyBuffer_Release(&view);
+    }
+
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_write__doc__,
+"write($self, bytes, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_WRITE_METHODDEF    \
+    {"write", (PyCFunction)mmap_mmap_write, METH_O, mmap_mmap_write__doc__},
+
+static PyObject *
+mmap_mmap_write_impl(mmap_object *self, Py_buffer *data);
+
+static PyObject *
+mmap_mmap_write(PyObject *self, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    Py_buffer data = {NULL, NULL};
+
+    if (PyObject_GetBuffer(arg, &data, PyBUF_SIMPLE) != 0) {
+        goto exit;
+    }
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_write_impl((mmap_object *)self, &data);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    /* Cleanup for data */
+    if (data.obj) {
+       PyBuffer_Release(&data);
+    }
+
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_write_byte__doc__,
+"write_byte($self, byte, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_WRITE_BYTE_METHODDEF    \
+    {"write_byte", (PyCFunction)mmap_mmap_write_byte, METH_O, mmap_mmap_write_byte__doc__},
+
+static PyObject *
+mmap_mmap_write_byte_impl(mmap_object *self, unsigned char value);
+
+static PyObject *
+mmap_mmap_write_byte(PyObject *self, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    unsigned char value;
+
+    {
+        long ival = PyLong_AsLong(arg);
+        if (ival == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        else if (ival < 0) {
+            PyErr_SetString(PyExc_OverflowError,
+                            "unsigned byte integer is less than minimum");
+            goto exit;
+        }
+        else if (ival > UCHAR_MAX) {
+            PyErr_SetString(PyExc_OverflowError,
+                            "unsigned byte integer is greater than maximum");
+            goto exit;
+        }
+        else {
+            value = (unsigned char) ival;
+        }
+    }
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_write_byte_impl((mmap_object *)self, value);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_size__doc__,
+"size($self, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_SIZE_METHODDEF    \
+    {"size", (PyCFunction)mmap_mmap_size, METH_NOARGS, mmap_mmap_size__doc__},
+
+static PyObject *
+mmap_mmap_size_impl(mmap_object *self);
+
+static PyObject *
+mmap_mmap_size(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    PyObject *return_value = NULL;
+
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_size_impl((mmap_object *)self);
+    Py_END_CRITICAL_SECTION();
+
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_resize__doc__,
+"resize($self, newsize, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_RESIZE_METHODDEF    \
+    {"resize", (PyCFunction)mmap_mmap_resize, METH_O, mmap_mmap_resize__doc__},
+
+static PyObject *
+mmap_mmap_resize_impl(mmap_object *self, Py_ssize_t new_size);
+
+static PyObject *
+mmap_mmap_resize(PyObject *self, PyObject *arg)
+{
+    PyObject *return_value = NULL;
+    Py_ssize_t new_size;
+
+    {
+        Py_ssize_t ival = -1;
+        PyObject *iobj = _PyNumber_Index(arg);
+        if (iobj != NULL) {
+            ival = PyLong_AsSsize_t(iobj);
+            Py_DECREF(iobj);
+        }
+        if (ival == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        new_size = ival;
+    }
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_resize_impl((mmap_object *)self, new_size);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_tell__doc__,
+"tell($self, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_TELL_METHODDEF    \
+    {"tell", (PyCFunction)mmap_mmap_tell, METH_NOARGS, mmap_mmap_tell__doc__},
+
+static PyObject *
+mmap_mmap_tell_impl(mmap_object *self);
+
+static PyObject *
+mmap_mmap_tell(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    PyObject *return_value = NULL;
+
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_tell_impl((mmap_object *)self);
+    Py_END_CRITICAL_SECTION();
+
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_flush__doc__,
+"flush($self, offset=0, size=None, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_FLUSH_METHODDEF    \
+    {"flush", _PyCFunction_CAST(mmap_mmap_flush), METH_FASTCALL, mmap_mmap_flush__doc__},
+
+static PyObject *
+mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset,
+                     PyObject *size_obj);
+
+static PyObject *
+mmap_mmap_flush(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    Py_ssize_t offset = 0;
+    PyObject *size_obj = Py_None;
+
+    if (!_PyArg_CheckPositional("flush", nargs, 0, 2)) {
+        goto exit;
+    }
+    if (nargs < 1) {
+        goto skip_optional;
+    }
+    {
+        Py_ssize_t ival = -1;
+        PyObject *iobj = _PyNumber_Index(args[0]);
+        if (iobj != NULL) {
+            ival = PyLong_AsSsize_t(iobj);
+            Py_DECREF(iobj);
+        }
+        if (ival == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        offset = ival;
+    }
+    if (nargs < 2) {
+        goto skip_optional;
+    }
+    size_obj = args[1];
+skip_optional:
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_flush_impl((mmap_object *)self, offset, size_obj);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_seek__doc__,
+"seek($self, pos, whence=0, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_SEEK_METHODDEF    \
+    {"seek", _PyCFunction_CAST(mmap_mmap_seek), METH_FASTCALL, mmap_mmap_seek__doc__},
+
+static PyObject *
+mmap_mmap_seek_impl(mmap_object *self, Py_ssize_t dist, int how);
+
+static PyObject *
+mmap_mmap_seek(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    Py_ssize_t dist;
+    int how = 0;
+
+    if (!_PyArg_CheckPositional("seek", nargs, 1, 2)) {
+        goto exit;
+    }
+    {
+        Py_ssize_t ival = -1;
+        PyObject *iobj = _PyNumber_Index(args[0]);
+        if (iobj != NULL) {
+            ival = PyLong_AsSsize_t(iobj);
+            Py_DECREF(iobj);
+        }
+        if (ival == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        dist = ival;
+    }
+    if (nargs < 2) {
+        goto skip_optional;
+    }
+    how = PyLong_AsInt(args[1]);
+    if (how == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+skip_optional:
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_seek_impl((mmap_object *)self, dist, how);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap_seekable__doc__,
+"seekable($self, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_SEEKABLE_METHODDEF    \
+    {"seekable", (PyCFunction)mmap_mmap_seekable, METH_NOARGS, mmap_mmap_seekable__doc__},
+
+static PyObject *
+mmap_mmap_seekable_impl(mmap_object *self);
+
+static PyObject *
+mmap_mmap_seekable(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    return mmap_mmap_seekable_impl((mmap_object *)self);
+}
+
+PyDoc_STRVAR(mmap_mmap_move__doc__,
+"move($self, dest, src, count, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_MOVE_METHODDEF    \
+    {"move", _PyCFunction_CAST(mmap_mmap_move), METH_FASTCALL, mmap_mmap_move__doc__},
+
+static PyObject *
+mmap_mmap_move_impl(mmap_object *self, Py_ssize_t dest, Py_ssize_t src,
+                    Py_ssize_t cnt);
+
+static PyObject *
+mmap_mmap_move(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    Py_ssize_t dest;
+    Py_ssize_t src;
+    Py_ssize_t cnt;
+
+    if (!_PyArg_CheckPositional("move", nargs, 3, 3)) {
+        goto exit;
+    }
+    {
+        Py_ssize_t ival = -1;
+        PyObject *iobj = _PyNumber_Index(args[0]);
+        if (iobj != NULL) {
+            ival = PyLong_AsSsize_t(iobj);
+            Py_DECREF(iobj);
+        }
+        if (ival == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        dest = ival;
+    }
+    {
+        Py_ssize_t ival = -1;
+        PyObject *iobj = _PyNumber_Index(args[1]);
+        if (iobj != NULL) {
+            ival = PyLong_AsSsize_t(iobj);
+            Py_DECREF(iobj);
+        }
+        if (ival == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        src = ival;
+    }
+    {
+        Py_ssize_t ival = -1;
+        PyObject *iobj = _PyNumber_Index(args[2]);
+        if (iobj != NULL) {
+            ival = PyLong_AsSsize_t(iobj);
+            Py_DECREF(iobj);
+        }
+        if (ival == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        cnt = ival;
+    }
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_move_impl((mmap_object *)self, dest, src, cnt);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap___enter____doc__,
+"__enter__($self, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP___ENTER___METHODDEF    \
+    {"__enter__", (PyCFunction)mmap_mmap___enter__, METH_NOARGS, mmap_mmap___enter____doc__},
+
+static PyObject *
+mmap_mmap___enter___impl(mmap_object *self);
+
+static PyObject *
+mmap_mmap___enter__(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    PyObject *return_value = NULL;
+
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap___enter___impl((mmap_object *)self);
+    Py_END_CRITICAL_SECTION();
+
+    return return_value;
+}
+
+PyDoc_STRVAR(mmap_mmap___exit____doc__,
+"__exit__($self, exc_type, exc_value, traceback, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP___EXIT___METHODDEF    \
+    {"__exit__", _PyCFunction_CAST(mmap_mmap___exit__), METH_FASTCALL, mmap_mmap___exit____doc__},
+
+static PyObject *
+mmap_mmap___exit___impl(mmap_object *self, PyObject *exc_type,
+                        PyObject *exc_value, PyObject *traceback);
+
+static PyObject *
+mmap_mmap___exit__(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    PyObject *exc_type;
+    PyObject *exc_value;
+    PyObject *traceback;
+
+    if (!_PyArg_CheckPositional("__exit__", nargs, 3, 3)) {
+        goto exit;
+    }
+    exc_type = args[0];
+    exc_value = args[1];
+    traceback = args[2];
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap___exit___impl((mmap_object *)self, exc_type, exc_value, traceback);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    return return_value;
+}
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(mmap_mmap___sizeof____doc__,
+"__sizeof__($self, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP___SIZEOF___METHODDEF    \
+    {"__sizeof__", (PyCFunction)mmap_mmap___sizeof__, METH_NOARGS, mmap_mmap___sizeof____doc__},
+
+static PyObject *
+mmap_mmap___sizeof___impl(mmap_object *self);
+
+static PyObject *
+mmap_mmap___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+    PyObject *return_value = NULL;
+
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap___sizeof___impl((mmap_object *)self);
+    Py_END_CRITICAL_SECTION();
+
+    return return_value;
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if (defined(MS_WINDOWS) && defined(Py_DEBUG))
+
+PyDoc_STRVAR(mmap_mmap__protect__doc__,
+"_protect($self, flNewProtect, start, length, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP__PROTECT_METHODDEF    \
+    {"_protect", _PyCFunction_CAST(mmap_mmap__protect), METH_FASTCALL, mmap_mmap__protect__doc__},
+
+static PyObject *
+mmap_mmap__protect_impl(mmap_object *self, unsigned int flNewProtect,
+                        Py_ssize_t start, Py_ssize_t length);
+
+static PyObject *
+mmap_mmap__protect(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    unsigned int flNewProtect;
+    Py_ssize_t start;
+    Py_ssize_t length;
+
+    if (!_PyArg_CheckPositional("_protect", nargs, 3, 3)) {
+        goto exit;
+    }
+    flNewProtect = (unsigned int)PyLong_AsUnsignedLongMask(args[0]);
+    if (flNewProtect == (unsigned int)-1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    {
+        Py_ssize_t ival = -1;
+        PyObject *iobj = _PyNumber_Index(args[1]);
+        if (iobj != NULL) {
+            ival = PyLong_AsSsize_t(iobj);
+            Py_DECREF(iobj);
+        }
+        if (ival == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        start = ival;
+    }
+    {
+        Py_ssize_t ival = -1;
+        PyObject *iobj = _PyNumber_Index(args[2]);
+        if (iobj != NULL) {
+            ival = PyLong_AsSsize_t(iobj);
+            Py_DECREF(iobj);
+        }
+        if (ival == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        length = ival;
+    }
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap__protect_impl((mmap_object *)self, flNewProtect, start, length);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    return return_value;
+}
+
+#endif /* (defined(MS_WINDOWS) && defined(Py_DEBUG)) */
+
+#if defined(HAVE_MADVISE)
+
+PyDoc_STRVAR(mmap_mmap_madvise__doc__,
+"madvise($self, option, start=0, length=None, /)\n"
+"--\n"
+"\n");
+
+#define MMAP_MMAP_MADVISE_METHODDEF    \
+    {"madvise", _PyCFunction_CAST(mmap_mmap_madvise), METH_FASTCALL, mmap_mmap_madvise__doc__},
+
+static PyObject *
+mmap_mmap_madvise_impl(mmap_object *self, int option, Py_ssize_t start,
+                       PyObject *length_obj);
+
+static PyObject *
+mmap_mmap_madvise(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    int option;
+    Py_ssize_t start = 0;
+    PyObject *length_obj = Py_None;
+
+    if (!_PyArg_CheckPositional("madvise", nargs, 1, 3)) {
+        goto exit;
+    }
+    option = PyLong_AsInt(args[0]);
+    if (option == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    if (nargs < 2) {
+        goto skip_optional;
+    }
+    {
+        Py_ssize_t ival = -1;
+        PyObject *iobj = _PyNumber_Index(args[1]);
+        if (iobj != NULL) {
+            ival = PyLong_AsSsize_t(iobj);
+            Py_DECREF(iobj);
+        }
+        if (ival == -1 && PyErr_Occurred()) {
+            goto exit;
+        }
+        start = ival;
+    }
+    if (nargs < 3) {
+        goto skip_optional;
+    }
+    length_obj = args[2];
+skip_optional:
+    Py_BEGIN_CRITICAL_SECTION(self);
+    return_value = mmap_mmap_madvise_impl((mmap_object *)self, option, start, length_obj);
+    Py_END_CRITICAL_SECTION();
+
+exit:
+    return return_value;
+}
+
+#endif /* defined(HAVE_MADVISE) */
+
+#ifndef MMAP_MMAP___SIZEOF___METHODDEF
+    #define MMAP_MMAP___SIZEOF___METHODDEF
+#endif /* !defined(MMAP_MMAP___SIZEOF___METHODDEF) */
+
+#ifndef MMAP_MMAP__PROTECT_METHODDEF
+    #define MMAP_MMAP__PROTECT_METHODDEF
+#endif /* !defined(MMAP_MMAP__PROTECT_METHODDEF) */
+
+#ifndef MMAP_MMAP_MADVISE_METHODDEF
+    #define MMAP_MMAP_MADVISE_METHODDEF
+#endif /* !defined(MMAP_MMAP_MADVISE_METHODDEF) */
+/*[clinic end generated code: output=fe4e2131abc1e971 input=a9049054013a1b77]*/
index f58a48900bf6d773b502a74f282bc0f70251fc98..43f4763c4fdc0d329c9857718321e16807db9a96 100644 (file)
@@ -91,6 +91,12 @@ my_getpagesize(void)
 #  define MAP_ANONYMOUS MAP_ANON
 #endif
 
+/*[clinic input]
+module mmap
+class mmap.mmap "mmap_object *" ""
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=82a9f8a529905b9b]*/
+
 typedef enum
 {
     ACCESS_DEFAULT,
@@ -129,6 +135,27 @@ typedef struct {
 
 #define mmap_object_CAST(op)    ((mmap_object *)(op))
 
+#include "clinic/mmapmodule.c.h"
+
+
+/* Return a Py_ssize_t from the object arg. This conversion logic is similar
+   to what AC uses for `Py_ssize_t` arguments.
+
+   Returns -1 on error. Use PyErr_Occurred() to disambiguate.
+*/
+static Py_ssize_t
+_As_Py_ssize_t(PyObject *arg) {
+    assert(arg != NULL);
+    Py_ssize_t ival = -1;
+    PyObject *iobj = _PyNumber_Index(arg);
+    if (iobj != NULL) {
+        ival = PyLong_AsSsize_t(iobj);
+        Py_DECREF(iobj);
+    }
+    return ival;
+}
+
+
 static int
 mmap_object_traverse(PyObject *op, visitproc visit, void *arg)
 {
@@ -172,10 +199,16 @@ mmap_object_dealloc(PyObject *op)
     Py_DECREF(tp);
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.close
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_close_method(PyObject *op, PyObject *Py_UNUSED(ignored))
+mmap_mmap_close_impl(mmap_object *self)
+/*[clinic end generated code: output=a1ae0c727546f78d input=25020035f047eae1]*/
 {
-    mmap_object *self = mmap_object_CAST(op);
     if (self->exports > 0) {
         PyErr_SetString(PyExc_BufferError, "cannot close "\
                         "exported pointers exist");
@@ -463,10 +496,16 @@ _safe_PyBytes_FromStringAndSize(char *start, size_t num_bytes) {
     }
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.read_byte
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_read_byte_method(PyObject *op, PyObject *Py_UNUSED(ignored))
+mmap_mmap_read_byte_impl(mmap_object *self)
+/*[clinic end generated code: output=d931da1319f3869b input=5b8c6a904bdddda9]*/
 {
-    mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(NULL);
     if (self->pos >= self->size) {
         PyErr_SetString(PyExc_ValueError, "read byte out of range");
@@ -480,12 +519,18 @@ mmap_read_byte_method(PyObject *op, PyObject *Py_UNUSED(ignored))
     return PyLong_FromLong((unsigned char) dest);
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.readline
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_read_line_method(PyObject *op, PyObject *Py_UNUSED(ignored))
+mmap_mmap_readline_impl(mmap_object *self)
+/*[clinic end generated code: output=b9d2bf9999283311 input=2c4efd1d06e1cdd1]*/
 {
     Py_ssize_t remaining;
     char *start, *eol;
-    mmap_object *self = mmap_object_CAST(op);
 
     CHECK_VALID(NULL);
 
@@ -510,16 +555,22 @@ mmap_read_line_method(PyObject *op, PyObject *Py_UNUSED(ignored))
     return result;
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.read
+
+  n as num_bytes: object(converter='_Py_convert_optional_to_ssize_t', type='Py_ssize_t', c_default='PY_SSIZE_T_MAX') = None
+  /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_read_method(PyObject *op, PyObject *args)
+mmap_mmap_read_impl(mmap_object *self, Py_ssize_t num_bytes)
+/*[clinic end generated code: output=3b4d4f3704ed0969 input=8f97f361d435e357]*/
 {
-    Py_ssize_t num_bytes = PY_SSIZE_T_MAX, remaining;
-    mmap_object *self = mmap_object_CAST(op);
+    Py_ssize_t remaining;
 
     CHECK_VALID(NULL);
-    if (!PyArg_ParseTuple(args, "|O&:read", _Py_convert_optional_to_ssize_t, &num_bytes))
-        return NULL;
-    CHECK_VALID(NULL);
 
     /* silently 'adjust' out-of-range requests */
     remaining = (self->pos < self->size) ? self->size - self->pos : 0;
@@ -535,81 +586,105 @@ mmap_read_method(PyObject *op, PyObject *args)
 }
 
 static PyObject *
-mmap_gfind(mmap_object *self,
-           PyObject *args,
-           int reverse)
+mmap_gfind_lock_held(mmap_object *self, Py_buffer *view, PyObject *start_obj,
+                    PyObject *end_obj, int reverse)
 {
     Py_ssize_t start = self->pos;
     Py_ssize_t end = self->size;
-    Py_buffer view;
 
     CHECK_VALID(NULL);
-    if (!PyArg_ParseTuple(args, reverse ? "y*|nn:rfind" : "y*|nn:find",
-                          &view, &start, &end)) {
-        return NULL;
-    }
-    else {
-        if (start < 0)
-            start += self->size;
-        if (start < 0)
-            start = 0;
-        else if (start > self->size)
-            start = self->size;
-
-        if (end < 0)
-            end += self->size;
-        if (end < 0)
-            end = 0;
-        else if (end > self->size)
-            end = self->size;
-
-        Py_ssize_t index;
-        PyObject *result;
-        CHECK_VALID_OR_RELEASE(NULL, view);
-        if (end < start) {
-            result = PyLong_FromSsize_t(-1);
+    if (start_obj != Py_None) {
+        start = _As_Py_ssize_t(start_obj);
+        if (start == -1 && PyErr_Occurred()) {
+            return NULL;
         }
-        else if (reverse) {
-            assert(0 <= start && start <= end && end <= self->size);
-            if (_safe_PyBytes_ReverseFind(&index, self,
-                self->data + start, end - start,
-                view.buf, view.len, start) < 0)
-            {
-                result = NULL;
-            }
-            else {
-                result = PyLong_FromSsize_t(index);
+
+        if (end_obj != Py_None) {
+            end = _As_Py_ssize_t(end_obj);
+            if (end == -1 && PyErr_Occurred()) {
+                return NULL;
             }
         }
+    }
+
+    if (start < 0)
+        start += self->size;
+    if (start < 0)
+        start = 0;
+    else if (start > self->size)
+        start = self->size;
+
+    if (end < 0)
+        end += self->size;
+    if (end < 0)
+        end = 0;
+    else if (end > self->size)
+        end = self->size;
+
+    Py_ssize_t index;
+    PyObject *result;
+    CHECK_VALID(NULL);
+    if (end < start) {
+        result = PyLong_FromSsize_t(-1);
+    }
+    else if (reverse) {
+        assert(0 <= start && start <= end && end <= self->size);
+        if (_safe_PyBytes_ReverseFind(&index, self,
+            self->data + start, end - start,
+            view->buf, view->len, start) < 0)
+        {
+            result = NULL;
+        }
         else {
-            assert(0 <= start && start <= end && end <= self->size);
-            if (_safe_PyBytes_Find(&index, self,
-                self->data + start, end - start,
-                view.buf, view.len, start) < 0)
-            {
-                result = NULL;
-            }
-            else {
-                result = PyLong_FromSsize_t(index);
-            }
+            result = PyLong_FromSsize_t(index);
         }
-        PyBuffer_Release(&view);
-        return result;
     }
+    else {
+        assert(0 <= start && start <= end && end <= self->size);
+        if (_safe_PyBytes_Find(&index, self,
+            self->data + start, end - start,
+            view->buf, view->len, start) < 0)
+        {
+            result = NULL;
+        }
+        else {
+            result = PyLong_FromSsize_t(index);
+        }
+    }
+    return result;
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.find
+
+  view: Py_buffer
+  start: object = None
+  end: object = None
+  /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_find_method(PyObject *op, PyObject *args)
+mmap_mmap_find_impl(mmap_object *self, Py_buffer *view, PyObject *start,
+                    PyObject *end)
+/*[clinic end generated code: output=ef8878a322f00192 input=0135504494b52c2b]*/
 {
-    mmap_object *self = mmap_object_CAST(op);
-    return mmap_gfind(self, args, 0);
+    return mmap_gfind_lock_held(self, view, start, end, 0);
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.rfind = mmap.mmap.find
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_rfind_method(PyObject *op, PyObject *args)
+mmap_mmap_rfind_impl(mmap_object *self, Py_buffer *view, PyObject *start,
+                     PyObject *end)
+/*[clinic end generated code: output=73b918940d67c2b8 input=8aecdd1f70c06c62]*/
 {
-    mmap_object *self = mmap_object_CAST(op);
-    return mmap_gfind(self, args, 1);
+    return mmap_gfind_lock_held(self, view, start, end, 1);
 }
 
 static int
@@ -645,50 +720,55 @@ is_resizeable(mmap_object *self)
 }
 
 
+/*[clinic input]
+@critical_section
+mmap.mmap.write
+
+    bytes as data: Py_buffer
+    /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_write_method(PyObject *op, PyObject *args)
+mmap_mmap_write_impl(mmap_object *self, Py_buffer *data)
+/*[clinic end generated code: output=9e97063efb6fb27b input=3f16fa79aa89d6f7]*/
 {
-    Py_buffer data;
-    mmap_object *self = mmap_object_CAST(op);
-
     CHECK_VALID(NULL);
-    if (!PyArg_ParseTuple(args, "y*:write", &data))
-        return NULL;
-
     if (!is_writable(self)) {
-        PyBuffer_Release(&data);
         return NULL;
     }
 
-    if (self->pos > self->size || self->size - self->pos < data.len) {
-        PyBuffer_Release(&data);
+    if (self->pos > self->size || self->size - self->pos < data->len) {
         PyErr_SetString(PyExc_ValueError, "data out of range");
         return NULL;
     }
 
-    CHECK_VALID_OR_RELEASE(NULL, data);
+    CHECK_VALID(NULL);
     PyObject *result;
-    if (safe_memcpy(self->data + self->pos, data.buf, data.len) < 0) {
+    if (safe_memcpy(self->data + self->pos, data->buf, data->len) < 0) {
         result = NULL;
     }
     else {
-        self->pos += data.len;
-        result = PyLong_FromSsize_t(data.len);
+        self->pos += data->len;
+        result = PyLong_FromSsize_t(data->len);
     }
-    PyBuffer_Release(&data);
     return result;
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.write_byte
+
+    byte as value: unsigned_char
+    /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_write_byte_method(PyObject *op, PyObject *args)
+mmap_mmap_write_byte_impl(mmap_object *self, unsigned char value)
+/*[clinic end generated code: output=aa11adada9b17510 input=32740bfa174f0991]*/
 {
-    char value;
-    mmap_object *self = mmap_object_CAST(op);
-
     CHECK_VALID(NULL);
-    if (!PyArg_ParseTuple(args, "b:write_byte", &value))
-        return(NULL);
-
     if (!is_writable(self))
         return NULL;
 
@@ -698,17 +778,23 @@ mmap_write_byte_method(PyObject *op, PyObject *args)
         return NULL;
     }
 
-    if (safe_byte_copy(self->data + self->pos, &value) < 0) {
+    if (safe_byte_copy(self->data + self->pos, (const char*)&value) < 0) {
         return NULL;
     }
     self->pos++;
     Py_RETURN_NONE;
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.size
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored))
+mmap_mmap_size_impl(mmap_object *self)
+/*[clinic end generated code: output=c177e65e83a648ff input=f69c072efd2e1595]*/
 {
-    mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(NULL);
 
 #ifdef MS_WINDOWS
@@ -755,14 +841,21 @@ mmap_size_method(PyObject *op, PyObject *Py_UNUSED(ignored))
  / new size?
  */
 
+/*[clinic input]
+@critical_section
+mmap.mmap.resize
+
+    newsize as new_size: Py_ssize_t
+    /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_resize_method(PyObject *op, PyObject *args)
+mmap_mmap_resize_impl(mmap_object *self, Py_ssize_t new_size)
+/*[clinic end generated code: output=6f262537ce9c2dcc input=b6b5dee52a41b79f]*/
 {
-    Py_ssize_t new_size;
-    mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(NULL);
-    if (!PyArg_ParseTuple(args, "n:resize", &new_size) ||
-        !is_resizeable(self)) {
+    if (!is_resizeable(self)) {
         return NULL;
     }
     if (new_size < 0 || PY_SSIZE_T_MAX - new_size < self->offset) {
@@ -910,23 +1003,45 @@ mmap_resize_method(PyObject *op, PyObject *args)
     }
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.tell
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_tell_method(PyObject *op, PyObject *Py_UNUSED(ignored))
+mmap_mmap_tell_impl(mmap_object *self)
+/*[clinic end generated code: output=6034958630e1b1d1 input=fd163acacf45c3a5]*/
 {
-    mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(NULL);
     return PyLong_FromSize_t(self->pos);
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.flush
+
+    offset: Py_ssize_t = 0
+    size as size_obj: object = None
+    /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_flush_method(PyObject *op, PyObject *args)
+mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset,
+                     PyObject *size_obj)
+/*[clinic end generated code: output=41a10c349ed1608a input=7e6bb8f9462f53e6]*/
 {
-    Py_ssize_t offset = 0;
-    mmap_object *self = mmap_object_CAST(op);
     Py_ssize_t size = self->size;
     CHECK_VALID(NULL);
-    if (!PyArg_ParseTuple(args, "|nn:flush", &offset, &size))
-        return NULL;
+
+    if (size_obj != Py_None) {
+        size = _As_Py_ssize_t(size_obj);
+        if (size == -1 && PyErr_Occurred()) {
+            return NULL;
+        }
+    }
+
     if (size < 0 || offset < 0 || self->size - offset < size) {
         PyErr_SetString(PyExc_ValueError, "flush values out of range");
         return NULL;
@@ -954,60 +1069,80 @@ mmap_flush_method(PyObject *op, PyObject *args)
 #endif
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.seek
+
+    pos as dist: Py_ssize_t
+    whence as how: int = 0
+    /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_seek_method(PyObject *op, PyObject *args)
+mmap_mmap_seek_impl(mmap_object *self, Py_ssize_t dist, int how)
+/*[clinic end generated code: output=00310494e8b8c592 input=e2fda5d081c3db22]*/
 {
-    Py_ssize_t dist;
-    mmap_object *self = mmap_object_CAST(op);
-    int how=0;
     CHECK_VALID(NULL);
-    if (!PyArg_ParseTuple(args, "n|i:seek", &dist, &how))
-        return NULL;
-    else {
-        Py_ssize_t where;
-        switch (how) {
-        case 0: /* relative to start */
-            where = dist;
-            break;
-        case 1: /* relative to current position */
-            if (PY_SSIZE_T_MAX - self->pos < dist)
-                goto onoutofrange;
-            where = self->pos + dist;
-            break;
-        case 2: /* relative to end */
-            if (PY_SSIZE_T_MAX - self->size < dist)
-                goto onoutofrange;
-            where = self->size + dist;
-            break;
-        default:
-            PyErr_SetString(PyExc_ValueError, "unknown seek type");
-            return NULL;
-        }
-        if (where > self->size || where < 0)
+    Py_ssize_t where;
+    switch (how) {
+    case 0: /* relative to start */
+        where = dist;
+        break;
+    case 1: /* relative to current position */
+        if (PY_SSIZE_T_MAX - self->pos < dist)
             goto onoutofrange;
-        self->pos = where;
-        return PyLong_FromSsize_t(self->pos);
+        where = self->pos + dist;
+        break;
+    case 2: /* relative to end */
+        if (PY_SSIZE_T_MAX - self->size < dist)
+            goto onoutofrange;
+        where = self->size + dist;
+        break;
+    default:
+        PyErr_SetString(PyExc_ValueError, "unknown seek type");
+        return NULL;
     }
+    if (where > self->size || where < 0)
+        goto onoutofrange;
+    self->pos = where;
+    return PyLong_FromSsize_t(self->pos);
 
   onoutofrange:
     PyErr_SetString(PyExc_ValueError, "seek out of range");
     return NULL;
 }
 
+/*[clinic input]
+mmap.mmap.seekable
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_seekable_method(PyObject *op, PyObject *Py_UNUSED(ignored))
+mmap_mmap_seekable_impl(mmap_object *self)
+/*[clinic end generated code: output=6311dc3ea300fa38 input=5132505f6e259001]*/
 {
     Py_RETURN_TRUE;
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.move
+
+    dest: Py_ssize_t
+    src: Py_ssize_t
+    count as cnt: Py_ssize_t
+    /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_move_method(PyObject *op, PyObject *args)
+mmap_mmap_move_impl(mmap_object *self, Py_ssize_t dest, Py_ssize_t src,
+                    Py_ssize_t cnt)
+/*[clinic end generated code: output=391f549a44181793 input=cf8cfe10d9f6b448]*/
 {
-    Py_ssize_t dest, src, cnt;
-    mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(NULL);
-    if (!PyArg_ParseTuple(args, "nnn:move", &dest, &src, &cnt) ||
-        !is_writable(self)) {
+    if (!is_writable(self)) {
         return NULL;
     } else {
         /* bounds check the values */
@@ -1033,30 +1168,53 @@ static PyObject *
 mmap_closed_get(PyObject *op, void *Py_UNUSED(closure))
 {
     mmap_object *self = mmap_object_CAST(op);
+    PyObject *result;
+    Py_BEGIN_CRITICAL_SECTION(op);
 #ifdef MS_WINDOWS
-    return PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
+    result = PyBool_FromLong(self->map_handle == NULL ? 1 : 0);
 #elif defined(UNIX)
-    return PyBool_FromLong(self->data == NULL ? 1 : 0);
+    result = PyBool_FromLong(self->data == NULL ? 1 : 0);
 #endif
+    Py_END_CRITICAL_SECTION();
+    return result;
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.__enter__
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap__enter__method(PyObject *op, PyObject *Py_UNUSED(ignored))
+mmap_mmap___enter___impl(mmap_object *self)
+/*[clinic end generated code: output=92cfc59f4c4e2d26 input=a446541fbfe0b890]*/
 {
-    mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(NULL);
 
     return Py_NewRef(self);
 }
 
+/*[clinic input]
+@critical_section
+mmap.mmap.__exit__
+
+    exc_type: object
+    exc_value: object
+    traceback: object
+    /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap__exit__method(PyObject *op, PyObject *Py_UNUSED(args))
+mmap_mmap___exit___impl(mmap_object *self, PyObject *exc_type,
+                        PyObject *exc_value, PyObject *traceback)
+/*[clinic end generated code: output=bec7e3e319c1f07e input=5f28e91cf752bc64]*/
 {
-    return mmap_close_method(op, NULL);
+    return mmap_mmap_close_impl(self);
 }
 
 static PyObject *
-mmap__repr__method(PyObject *op)
+mmap__repr__method_lock_held(PyObject *op)
 {
     mmap_object *mobj = mmap_object_CAST(op);
 
@@ -1100,11 +1258,27 @@ mmap__repr__method(PyObject *op)
     }
 }
 
+static PyObject *
+mmap__repr__method(PyObject *op)
+{
+    PyObject *result;
+    Py_BEGIN_CRITICAL_SECTION(op);
+    result = mmap__repr__method_lock_held(op);
+    Py_END_CRITICAL_SECTION();
+    return result;
+}
+
 #ifdef MS_WINDOWS
+/*[clinic input]
+@critical_section
+mmap.mmap.__sizeof__
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap__sizeof__method(PyObject *op, PyObject *Py_UNUSED(dummy))
+mmap_mmap___sizeof___impl(mmap_object *self)
+/*[clinic end generated code: output=1aed30daff807d09 input=8a648868a089553c]*/
 {
-    mmap_object *self = mmap_object_CAST(op);
     size_t res = _PyObject_SIZE(Py_TYPE(self));
     if (self->tagname) {
         res += (wcslen(self->tagname) + 1) * sizeof(self->tagname[0]);
@@ -1114,18 +1288,26 @@ mmap__sizeof__method(PyObject *op, PyObject *Py_UNUSED(dummy))
 #endif
 
 #if defined(MS_WINDOWS) && defined(Py_DEBUG)
+/*[clinic input]
+@critical_section
+mmap.mmap._protect
+
+    flNewProtect: unsigned_int(bitwise=True)
+    start: Py_ssize_t
+    length: Py_ssize_t
+    /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_protect_method(PyObject *op, PyObject *args) {
-    DWORD flNewProtect, flOldProtect;
-    Py_ssize_t start, length;
-    mmap_object *self = mmap_object_CAST(op);
+mmap_mmap__protect_impl(mmap_object *self, unsigned int flNewProtect,
+                        Py_ssize_t start, Py_ssize_t length)
+/*[clinic end generated code: output=a87271a34d1ad6cf input=9170498c5e1482da]*/
+{
+    DWORD flOldProtect;
 
     CHECK_VALID(NULL);
 
-    if (!PyArg_ParseTuple(args, "Inn:protect", &flNewProtect, &start, &length)) {
-        return NULL;
-    }
-
     if (!VirtualProtect((void *) (self->data + start), length, flNewProtect,
                         &flOldProtect))
     {
@@ -1138,18 +1320,32 @@ mmap_protect_method(PyObject *op, PyObject *args) {
 #endif
 
 #ifdef HAVE_MADVISE
+/*[clinic input]
+@critical_section
+mmap.mmap.madvise
+
+  option: int
+  start: Py_ssize_t = 0
+  length as length_obj: object = None
+  /
+
+[clinic start generated code]*/
+
 static PyObject *
-mmap_madvise_method(PyObject *op, PyObject *args)
+mmap_mmap_madvise_impl(mmap_object *self, int option, Py_ssize_t start,
+                       PyObject *length_obj)
+/*[clinic end generated code: output=816be656f08c0e3c input=2d37f7a4c87f1053]*/
 {
-    int option;
-    Py_ssize_t start = 0, length;
-    mmap_object *self = mmap_object_CAST(op);
+    Py_ssize_t length;
 
     CHECK_VALID(NULL);
-    length = self->size;
-
-    if (!PyArg_ParseTuple(args, "i|nn:madvise", &option, &start, &length)) {
-        return NULL;
+    if (length_obj == Py_None) {
+        length = self->size;
+    } else {
+        length = _As_Py_ssize_t(length_obj);
+        if (length == -1 && PyErr_Occurred()) {
+            return NULL;
+        }
     }
 
     if (start < 0 || start >= self->size) {
@@ -1185,32 +1381,26 @@ static struct PyMemberDef mmap_object_members[] = {
 };
 
 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},
-#ifdef HAVE_MADVISE
-    {"madvise",         mmap_madvise_method,      METH_VARARGS},
-#endif
-    {"move",            mmap_move_method,         METH_VARARGS},
-    {"read",            mmap_read_method,         METH_VARARGS},
-    {"read_byte",       mmap_read_byte_method,    METH_NOARGS},
-    {"readline",        mmap_read_line_method,    METH_NOARGS},
-    {"resize",          mmap_resize_method,       METH_VARARGS},
-    {"seek",            mmap_seek_method,         METH_VARARGS},
-    {"seekable",        mmap_seekable_method,     METH_NOARGS},
-    {"size",            mmap_size_method,         METH_NOARGS},
-    {"tell",            mmap_tell_method,         METH_NOARGS},
-    {"write",           mmap_write_method,        METH_VARARGS},
-    {"write_byte",      mmap_write_byte_method,   METH_VARARGS},
-    {"__enter__",       mmap__enter__method,      METH_NOARGS},
-    {"__exit__",        mmap__exit__method,       METH_VARARGS},
-#ifdef MS_WINDOWS
-    {"__sizeof__",      mmap__sizeof__method,     METH_NOARGS},
-#ifdef Py_DEBUG
-    {"_protect",        mmap_protect_method,      METH_VARARGS},
-#endif // Py_DEBUG
-#endif // MS_WINDOWS
+    MMAP_MMAP_CLOSE_METHODDEF
+    MMAP_MMAP_FIND_METHODDEF
+    MMAP_MMAP_RFIND_METHODDEF
+    MMAP_MMAP_FLUSH_METHODDEF
+    MMAP_MMAP_MADVISE_METHODDEF
+    MMAP_MMAP_MOVE_METHODDEF
+    MMAP_MMAP_READ_METHODDEF
+    MMAP_MMAP_READ_BYTE_METHODDEF
+    MMAP_MMAP_READLINE_METHODDEF
+    MMAP_MMAP_RESIZE_METHODDEF
+    MMAP_MMAP_SEEK_METHODDEF
+    MMAP_MMAP_SEEKABLE_METHODDEF
+    MMAP_MMAP_SIZE_METHODDEF
+    MMAP_MMAP_TELL_METHODDEF
+    MMAP_MMAP_WRITE_METHODDEF
+    MMAP_MMAP_WRITE_BYTE_METHODDEF
+    MMAP_MMAP___ENTER___METHODDEF
+    MMAP_MMAP___EXIT___METHODDEF
+    MMAP_MMAP___SIZEOF___METHODDEF
+    MMAP_MMAP__PROTECT_METHODDEF
     {NULL,         NULL}       /* sentinel */
 };
 
@@ -1223,7 +1413,7 @@ static PyGetSetDef mmap_object_getset[] = {
 /* Functions for treating an mmap'ed file as a buffer */
 
 static int
-mmap_buffer_getbuf(PyObject *op, Py_buffer *view, int flags)
+mmap_buffer_getbuf_lock_held(PyObject *op, Py_buffer *view, int flags)
 {
     mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(-1);
@@ -1234,23 +1424,45 @@ mmap_buffer_getbuf(PyObject *op, Py_buffer *view, int flags)
     return 0;
 }
 
+static int
+mmap_buffer_getbuf(PyObject *op, Py_buffer *view, int flags)
+{
+    int result;
+    Py_BEGIN_CRITICAL_SECTION(op);
+    result = mmap_buffer_getbuf_lock_held(op, view, flags);
+    Py_END_CRITICAL_SECTION();
+    return result;
+}
+
 static void
 mmap_buffer_releasebuf(PyObject *op, Py_buffer *Py_UNUSED(view))
 {
     mmap_object *self = mmap_object_CAST(op);
+    Py_BEGIN_CRITICAL_SECTION(self);
     self->exports--;
+    Py_END_CRITICAL_SECTION();
 }
 
 static Py_ssize_t
-mmap_length(PyObject *op)
+mmap_length_lock_held(PyObject *op)
 {
     mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(-1);
     return self->size;
 }
 
+static Py_ssize_t
+mmap_length(PyObject *op)
+{
+    Py_ssize_t result;
+    Py_BEGIN_CRITICAL_SECTION(op);
+    result = mmap_length_lock_held(op);
+    Py_END_CRITICAL_SECTION();
+    return result;
+}
+
 static PyObject *
-mmap_item(PyObject *op, Py_ssize_t i)
+mmap_item_lock_held(PyObject *op, Py_ssize_t i)
 {
     mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(NULL);
@@ -1267,7 +1479,16 @@ mmap_item(PyObject *op, Py_ssize_t i)
 }
 
 static PyObject *
-mmap_subscript(PyObject *op, PyObject *item)
+mmap_item(PyObject *op, Py_ssize_t i) {
+    PyObject *result;
+    Py_BEGIN_CRITICAL_SECTION(op);
+    result = mmap_item_lock_held(op, i);
+    Py_END_CRITICAL_SECTION();
+    return result;
+}
+
+static PyObject *
+mmap_subscript_lock_held(PyObject *op, PyObject *item)
 {
     mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(NULL);
@@ -1329,8 +1550,18 @@ mmap_subscript(PyObject *op, PyObject *item)
     }
 }
 
+static PyObject *
+mmap_subscript(PyObject *op, PyObject *item)
+{
+    PyObject *result;
+    Py_BEGIN_CRITICAL_SECTION(op);
+    result = mmap_subscript_lock_held(op, item);
+    Py_END_CRITICAL_SECTION();
+    return result;
+}
+
 static int
-mmap_ass_item(PyObject *op, Py_ssize_t i, PyObject *v)
+mmap_ass_item_lock_held(PyObject *op, Py_ssize_t i, PyObject *v)
 {
     const char *buf;
     mmap_object *self = mmap_object_CAST(op);
@@ -1361,7 +1592,17 @@ mmap_ass_item(PyObject *op, Py_ssize_t i, PyObject *v)
 }
 
 static int
-mmap_ass_subscript(PyObject *op, PyObject *item, PyObject *value)
+mmap_ass_item(PyObject *op, Py_ssize_t i, PyObject *v)
+{
+    int result;
+    Py_BEGIN_CRITICAL_SECTION(op);
+    result = mmap_ass_item_lock_held(op, i, v);
+    Py_END_CRITICAL_SECTION();
+    return result;
+}
+
+static int
+mmap_ass_subscript_lock_held(PyObject *op, PyObject *item, PyObject *value)
 {
     mmap_object *self = mmap_object_CAST(op);
     CHECK_VALID(-1);
@@ -1457,6 +1698,16 @@ mmap_ass_subscript(PyObject *op, PyObject *item, PyObject *value)
     }
 }
 
+static int
+mmap_ass_subscript(PyObject *op, PyObject *item, PyObject *value)
+{
+    int result;
+    Py_BEGIN_CRITICAL_SECTION(op);
+    result = mmap_ass_subscript_lock_held(op, item, value);
+    Py_END_CRITICAL_SECTION();
+    return result;
+}
+
 static PyObject *
 new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict);