Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/python/cpython into linked-…
Browse files Browse the repository at this point in the history
…list
  • Loading branch information
kumaraditya303 committed Jun 22, 2024
2 parents c7b604c + 974a978 commit 56beb04
Show file tree
Hide file tree
Showing 79 changed files with 3,942 additions and 1,839 deletions.
117 changes: 114 additions & 3 deletions Doc/c-api/init.rst
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,16 @@ Initializing and finalizing the interpreter
:c:func:`Py_NewInterpreter` below) that were created and not yet destroyed since
the last call to :c:func:`Py_Initialize`. Ideally, this frees all memory
allocated by the Python interpreter. This is a no-op when called for a second
time (without calling :c:func:`Py_Initialize` again first). Normally the
return value is ``0``. If there were errors during finalization
(flushing buffered data), ``-1`` is returned.
time (without calling :c:func:`Py_Initialize` again first).
Since this is the reverse of :c:func:`Py_Initialize`, it should be called
in the same thread with the same interpreter active. That means
the main thread and the main interpreter.
This should never be called while :c:func:`Py_RunMain` is running.
Normally the return value is ``0``.
If there were errors during finalization (flushing buffered data),
``-1`` is returned.
This function is provided for a number of reasons. An embedding application
might want to restart Python without having to restart the application itself.
Expand Down Expand Up @@ -2195,3 +2202,107 @@ The C-API provides a basic mutual exclusion lock.
issue a fatal error.
.. versionadded:: 3.13
.. _python-critical-section-api:
Python Critical Section API
---------------------------
The critical section API provides a deadlock avoidance layer on top of
per-object locks for :term:`free-threaded <free threading>` CPython. They are
intended to replace reliance on the :term:`global interpreter lock`, and are
no-ops in versions of Python with the global interpreter lock.
Critical sections avoid deadlocks by implicitly suspending active critical
sections and releasing the locks during calls to :c:func:`PyEval_SaveThread`.
When :c:func:`PyEval_RestoreThread` is called, the most recent critical section
is resumed, and its locks reacquired. This means the critical section API
provides weaker guarantees than traditional locks -- they are useful because
their behavior is similar to the :term:`GIL`.
The functions and structs used by the macros are exposed for cases
where C macros are not available. They should only be used as in the
given macro expansions. Note that the sizes and contents of the structures may
change in future Python versions.
.. note::
Operations that need to lock two objects at once must use
:c:macro:`Py_BEGIN_CRITICAL_SECTION2`. You *cannot* use nested critical
sections to lock more than one object at once, because the inner critical
section may suspend the outer critical sections. This API does not provide
a way to lock more than two objects at once.
Example usage::
static PyObject *
set_field(MyObject *self, PyObject *value)
{
Py_BEGIN_CRITICAL_SECTION(self);
Py_SETREF(self->field, Py_XNewRef(value));
Py_END_CRITICAL_SECTION();
Py_RETURN_NONE;
}
In the above example, :c:macro:`Py_SETREF` calls :c:macro:`Py_DECREF`, which
can call arbitrary code through an object's deallocation function. The critical
section API avoids potentital deadlocks due to reentrancy and lock ordering
by allowing the runtime to temporarily suspend the critical section if the
code triggered by the finalizer blocks and calls :c:func:`PyEval_SaveThread`.
.. c:macro:: Py_BEGIN_CRITICAL_SECTION(op)
Acquires the per-object lock for the object *op* and begins a
critical section.
In the free-threaded build, this macro expands to::
{
PyCriticalSection _py_cs;
PyCriticalSection_Begin(&_py_cs, (PyObject*)(op))
In the default build, this macro expands to ``{``.
.. versionadded:: 3.13
.. c:macro:: Py_END_CRITICAL_SECTION()
Ends the critical section and releases the per-object lock.
In the free-threaded build, this macro expands to::
PyCriticalSection_End(&_py_cs);
}
In the default build, this macro expands to ``}``.
.. versionadded:: 3.13
.. c:macro:: Py_BEGIN_CRITICAL_SECTION2(a, b)
Acquires the per-objects locks for the objects *a* and *b* and begins a
critical section. The locks are acquired in a consistent order (lowest
address first) to avoid lock ordering deadlocks.
In the free-threaded build, this macro expands to::
{
PyCriticalSection2 _py_cs2;
PyCriticalSection_Begin2(&_py_cs2, (PyObject*)(a), (PyObject*)(b))
In the default build, this macro expands to ``{``.
.. versionadded:: 3.13
.. c:macro:: Py_END_CRITICAL_SECTION2()
Ends the critical section and releases the per-object locks.
In the free-threaded build, this macro expands to::
PyCriticalSection_End2(&_py_cs2);
}
In the default build, this macro expands to ``}``.
.. versionadded:: 3.13
35 changes: 32 additions & 3 deletions Doc/c-api/unicode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1551,9 +1551,17 @@ object.
On success, return ``0``.
On error, set an exception, leave the writer unchanged, and return ``-1``.
To use a different error handler than ``strict``,
:c:func:`PyUnicode_DecodeUTF8` can be used with
:c:func:`PyUnicodeWriter_WriteStr`.
See also :c:func:`PyUnicodeWriter_DecodeUTF8Stateful`.
.. c:function:: int PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, const wchar_t *str, Py_ssize_t size)
Writer the wide string *str* into *writer*.
*size* is a number of wide characters. If *size* is equal to ``-1``, call
``wcslen(str)`` to get the string length.
On success, return ``0``.
On error, set an exception, leave the writer unchanged, and return ``-1``.
.. c:function:: int PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj)
Expand Down Expand Up @@ -1586,3 +1594,24 @@ object.
On success, return ``0``.
On error, set an exception, leave the writer unchanged, and return ``-1``.
.. c:function:: int PyUnicodeWriter_DecodeUTF8Stateful(PyUnicodeWriter *writer, const char *string, Py_ssize_t length, const char *errors, Py_ssize_t *consumed)
Decode the string *str* from UTF-8 with *errors* error handler and write the
output into *writer*.
*size* is the string length in bytes. If *size* is equal to ``-1``, call
``strlen(str)`` to get the string length.
*errors* is an error handler name, such as ``"replace"``. If *errors* is
``NULL``, use the strict error handler.
If *consumed* is not ``NULL``, set *\*consumed* to the number of decoded
bytes on success.
If *consumed* is ``NULL``, treat trailing incomplete UTF-8 byte sequences
as an error.
On success, return ``0``.
On error, set an exception, leave the writer unchanged, and return ``-1``.
See also :c:func:`PyUnicodeWriter_WriteUTF8`.
2 changes: 1 addition & 1 deletion Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,7 @@ accepts integers that meet the value restriction ``0 <= x <= 255``).
Notes:

(1)
*t* must have the same length as the slice it is replacing.
If *k* is not equal to ``1``, *t* must have the same length as the slice it is replacing.

(2)
The optional argument *i* defaults to ``-1``, so that by default the last
Expand Down
2 changes: 2 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -291,10 +291,12 @@ New Features
* :c:func:`PyUnicodeWriter_Finish`.
* :c:func:`PyUnicodeWriter_WriteChar`.
* :c:func:`PyUnicodeWriter_WriteUTF8`.
* :c:func:`PyUnicodeWriter_WriteWideChar`.
* :c:func:`PyUnicodeWriter_WriteStr`.
* :c:func:`PyUnicodeWriter_WriteRepr`.
* :c:func:`PyUnicodeWriter_WriteSubstring`.
* :c:func:`PyUnicodeWriter_Format`.
* :c:func:`PyUnicodeWriter_DecodeUTF8Stateful`.

(Contributed by Victor Stinner in :gh:`119182`.)

Expand Down
1 change: 1 addition & 0 deletions Include/Python.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
#include "import.h"
#include "abstract.h"
#include "bltinmodule.h"
#include "critical_section.h"
#include "cpython/pyctype.h"
#include "pystrtod.h"
#include "pystrcmp.h"
Expand Down
134 changes: 134 additions & 0 deletions Include/cpython/critical_section.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#ifndef Py_CPYTHON_CRITICAL_SECTION_H
# error "this header file must not be included directly"
#endif

// Python critical sections
//
// Conceptually, critical sections are a deadlock avoidance layer on top of
// per-object locks. These helpers, in combination with those locks, replace
// our usage of the global interpreter lock to provide thread-safety for
// otherwise thread-unsafe objects, such as dict.
//
// NOTE: These APIs are no-ops in non-free-threaded builds.
//
// Straightforward per-object locking could introduce deadlocks that were not
// present when running with the GIL. Threads may hold locks for multiple
// objects simultaneously because Python operations can nest. If threads were
// to acquire the same locks in different orders, they would deadlock.
//
// One way to avoid deadlocks is to allow threads to hold only the lock (or
// locks) for a single operation at a time (typically a single lock, but some
// operations involve two locks). When a thread begins a nested operation it
// could suspend the locks for any outer operation: before beginning the nested
// operation, the locks for the outer operation are released and when the
// nested operation completes, the locks for the outer operation are
// reacquired.
//
// To improve performance, this API uses a variation of the above scheme.
// Instead of immediately suspending locks any time a nested operation begins,
// locks are only suspended if the thread would block. This reduces the number
// of lock acquisitions and releases for nested operations, while still
// avoiding deadlocks.
//
// Additionally, the locks for any active operation are suspended around
// other potentially blocking operations, such as I/O. This is because the
// interaction between locks and blocking operations can lead to deadlocks in
// the same way as the interaction between multiple locks.
//
// Each thread's critical sections and their corresponding locks are tracked in
// a stack in `PyThreadState.critical_section`. When a thread calls
// `_PyThreadState_Detach()`, such as before a blocking I/O operation or when
// waiting to acquire a lock, the thread suspends all of its active critical
// sections, temporarily releasing the associated locks. When the thread calls
// `_PyThreadState_Attach()`, it resumes the top-most (i.e., most recent)
// critical section by reacquiring the associated lock or locks. See
// `_PyCriticalSection_Resume()`.
//
// NOTE: Only the top-most critical section is guaranteed to be active.
// Operations that need to lock two objects at once must use
// `Py_BEGIN_CRITICAL_SECTION2()`. You *CANNOT* use nested critical sections
// to lock more than one object at once, because the inner critical section
// may suspend the outer critical sections. This API does not provide a way
// to lock more than two objects at once (though it could be added later
// if actually needed).
//
// NOTE: Critical sections implicitly behave like reentrant locks because
// attempting to acquire the same lock will suspend any outer (earlier)
// critical sections. However, they are less efficient for this use case than
// purposefully designed reentrant locks.
//
// Example usage:
// Py_BEGIN_CRITICAL_SECTION(op);
// ...
// Py_END_CRITICAL_SECTION();
//
// To lock two objects at once:
// Py_BEGIN_CRITICAL_SECTION2(op1, op2);
// ...
// Py_END_CRITICAL_SECTION2();

typedef struct PyCriticalSection PyCriticalSection;
typedef struct PyCriticalSection2 PyCriticalSection2;

PyAPI_FUNC(void)
PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op);

PyAPI_FUNC(void)
PyCriticalSection_End(PyCriticalSection *c);

PyAPI_FUNC(void)
PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b);

PyAPI_FUNC(void)
PyCriticalSection2_End(PyCriticalSection2 *c);

#ifndef Py_GIL_DISABLED
# define Py_BEGIN_CRITICAL_SECTION(op) \
{
# define Py_END_CRITICAL_SECTION() \
}
# define Py_BEGIN_CRITICAL_SECTION2(a, b) \
{
# define Py_END_CRITICAL_SECTION2() \
}
#else /* !Py_GIL_DISABLED */

// NOTE: the contents of this struct are private and may change betweeen
// Python releases without a deprecation period.
struct PyCriticalSection {
// Tagged pointer to an outer active critical section (or 0).
uintptr_t _cs_prev;

// Mutex used to protect critical section
PyMutex *_cs_mutex;
};

// A critical section protected by two mutexes. Use
// Py_BEGIN_CRITICAL_SECTION2 and Py_END_CRITICAL_SECTION2.
// NOTE: the contents of this struct are private and may change betweeen
// Python releases without a deprecation period.
struct PyCriticalSection2 {
PyCriticalSection _cs_base;

PyMutex *_cs_mutex2;
};

# define Py_BEGIN_CRITICAL_SECTION(op) \
{ \
PyCriticalSection _py_cs; \
PyCriticalSection_Begin(&_py_cs, _PyObject_CAST(op))

# define Py_END_CRITICAL_SECTION() \
PyCriticalSection_End(&_py_cs); \
}

# define Py_BEGIN_CRITICAL_SECTION2(a, b) \
{ \
PyCriticalSection2 _py_cs2; \
PyCriticalSection2_Begin(&_py_cs2, _PyObject_CAST(a), _PyObject_CAST(b))

# define Py_END_CRITICAL_SECTION2() \
PyCriticalSection2_End(&_py_cs2); \
}

#endif
10 changes: 10 additions & 0 deletions Include/cpython/unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,10 @@ PyAPI_FUNC(int) PyUnicodeWriter_WriteUTF8(
PyUnicodeWriter *writer,
const char *str,
Py_ssize_t size);
PyAPI_FUNC(int) PyUnicodeWriter_WriteWideChar(
PyUnicodeWriter *writer,
const wchar_t *str,
Py_ssize_t size);

PyAPI_FUNC(int) PyUnicodeWriter_WriteStr(
PyUnicodeWriter *writer,
Expand All @@ -475,6 +479,12 @@ PyAPI_FUNC(int) PyUnicodeWriter_Format(
PyUnicodeWriter *writer,
const char *format,
...);
PyAPI_FUNC(int) PyUnicodeWriter_DecodeUTF8Stateful(
PyUnicodeWriter *writer,
const char *string, /* UTF-8 encoded string */
Py_ssize_t length, /* size of string */
const char *errors, /* error handling */
Py_ssize_t *consumed); /* bytes consumed */


/* --- Private _PyUnicodeWriter API --------------------------------------- */
Expand Down
16 changes: 16 additions & 0 deletions Include/critical_section.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef Py_CRITICAL_SECTION_H
#define Py_CRITICAL_SECTION_H
#ifdef __cplusplus
extern "C" {
#endif

#ifndef Py_LIMITED_API
# define Py_CPYTHON_CRITICAL_SECTION_H
# include "cpython/critical_section.h"
# undef Py_CPYTHON_CRITICAL_SECTION_H
#endif

#ifdef __cplusplus
}
#endif
#endif /* !Py_CRITICAL_SECTION_H */
Loading

0 comments on commit 56beb04

Please sign in to comment.