gh-108724: Add PyMutex and _PyParkingLot APIs (gh-109344)

PyMutex is a one byte lock with fast, inlineable lock and unlock functions for the common uncontended case.  The design is based on WebKit's WTF::Lock.

PyMutex is built using the _PyParkingLot APIs, which provides a cross-platform futex-like API (based on WebKit's WTF::ParkingLot).  This internal API will be used for building other synchronization primitives used to implement PEP 703, such as one-time initialization and events.

This also includes tests and a mini benchmark in Tools/lockbench/lockbench.py to compare with the existing PyThread_type_lock.

Uncontended acquisition + release:
* Linux (x86-64): PyMutex: 11 ns, PyThread_type_lock: 44 ns
* macOS (arm64): PyMutex: 13 ns, PyThread_type_lock: 18 ns
* Windows (x86-64): PyMutex: 13 ns, PyThread_type_lock: 38 ns

PR Overview:

The primary purpose of this PR is to implement PyMutex, but there are a number of support pieces (described below).

* PyMutex:  A 1-byte lock that doesn't require memory allocation to initialize and is generally faster than the existing PyThread_type_lock.  The API is internal only for now.
* _PyParking_Lot:  A futex-like API based on the API of the same name in WebKit.  Used to implement PyMutex.
* _PyRawMutex:  A word sized lock used to implement _PyParking_Lot.
* PyEvent:  A one time event.  This was used a bunch in the "nogil" fork and is useful for testing the PyMutex implementation, so I've included it as part of the PR.
* pycore_llist.h:  Defines common operations on doubly-linked list.  Not strictly necessary (could do the list operations manually), but they come up frequently in the "nogil" fork. ( Similar to https://man.freebsd.org/cgi/man.cgi?queue)

---------

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
This commit is contained in:
Sam Gross 2023-09-19 11:54:29 -04:00 committed by GitHub
parent 0a31ff0050
commit 0c89056fe5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 1665 additions and 21 deletions

View file

@ -6970,7 +6970,7 @@ fi
#
# Avoid #include <Python.h> or #include <pyport.h>. The <Python.h> header
# requires <pyconfig.h> header which is only written below by AC_OUTPUT below.
# If the check is done after AC_OUTPUT, modifying LIBATOMIC has no effect
# If the check is done after AC_OUTPUT, modifying LIBS has no effect
# anymore. <pyport.h> cannot be included alone, it's designed to be included
# by <Python.h>: it expects other includes and macros to be defined.
_SAVE_VAR([CPPFLAGS])
@ -6993,7 +6993,7 @@ typedef intptr_t Py_ssize_t;
# error "unable to define Py_ssize_t"
#endif
#include "cpython/pyatomic.h"
#include "pyatomic.h"
int main()
{
@ -7014,7 +7014,7 @@ int main()
])
AS_VAR_IF([ac_cv_libatomic_needed], [yes],
[LIBATOMIC=${LIBATOMIC-"-latomic"}])
[LIBS="${LIBS} -latomic"])
_RESTORE_VAR([CPPFLAGS])
@ -7286,10 +7286,7 @@ PY_STDLIB_MOD([_hashlib], [], [test "$ac_cv_working_openssl_hashlib" = yes],
[$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $LIBCRYPTO_LIBS])
dnl test modules
PY_STDLIB_MOD([_testcapi],
[test "$TEST_MODULES" = yes], []
dnl Modules/_testcapi/pyatomic.c uses <cpython/pyatomic.h> header
[], [], [$LIBATOMIC])
PY_STDLIB_MOD([_testcapi], [test "$TEST_MODULES" = yes])
PY_STDLIB_MOD([_testclinic], [test "$TEST_MODULES" = yes])
PY_STDLIB_MOD([_testclinic_limited], [test "$TEST_MODULES" = yes])
PY_STDLIB_MOD([_testinternalcapi], [test "$TEST_MODULES" = yes])