@@ -187,6 +187,100 @@ call to :c:func:`PyErr_SetString` as shown below::
187187 }
188188
189189
190+ .. _extending-interruptible :
191+
192+ Making long-running operations interruptible
193+ ============================================
194+
195+ An extension that spends a long time in C code should call
196+ :c:func: `PyErr_CheckSignals ` periodically so that users can interrupt it with
197+ :kbd: `Ctrl - C `. Python defers running signal handlers until it is safe to execute
198+ Python code. Without these checks, a signal handler may not run until the
199+ operation returns to the interpreter, even if the extension releases the
200+ :term: `global interpreter lock ` (GIL). See :mod: `signal ` for details.
201+
202+ When :c:func: `PyErr_CheckSignals ` returns ``-1 ``, an exception is already set.
203+ Stop the operation, release any resources it owns, and propagate the exception
204+ to the caller, as described in :ref: `extending-errors `. Do not clear or replace
205+ the exception: although the default handler for :py:data: `signal.SIGINT ` raises
206+ :exc: `KeyboardInterrupt `, a user-defined handler can raise any exception.
207+ A return value of ``0 `` means that execution can continue, including when a
208+ handler ran without raising an exception.
209+
210+ Choosing safe places to check
211+ -----------------------------
212+
213+ :c:func: `PyErr_CheckSignals ` can execute arbitrary Python code before returning.
214+ That code may call back into your extension or modify objects it uses. Before
215+ each check, put shared state into a consistent state and release locks that a
216+ callback might acquire. Keep strong references to objects needed after the
217+ check, and revalidate assumptions about mutable data before using it again.
218+ See :ref: `thinice ` for related pitfalls with borrowed references.
219+
220+ The calling thread must have an :term: `attached thread state `. On builds with
221+ the GIL, this also means holding the GIL. When processing data with the thread
222+ state detached, divide the work into chunks and reattach it before each check.
223+ In particular, call :c:func: `PyErr_CheckSignals ` after
224+ :c:macro: `Py_END_ALLOW_THREADS `, not between :c:macro: `Py_BEGIN_ALLOW_THREADS `
225+ and :c:macro: `Py_END_ALLOW_THREADS `. This requirement also applies to
226+ :term: `free-threaded <free threading> ` builds; see
227+ :ref: `detaching-thread-state `.
228+
229+ For example, this function inverts the bits in a :class: `bytes ` object and
230+ returns a new :class: `bytes ` object. It checks for interruptions between chunks
231+ and discards the partial result if an exception is raised::
232+
233+ static PyObject *
234+ spam_invert(PyObject *self, PyObject *arg)
235+ {
236+ char *source;
237+ Py_ssize_t length;
238+ if (PyBytes_AsStringAndSize(arg, &source, &length) < 0) {
239+ return NULL;
240+ }
241+ PyObject *result = PyBytes_FromStringAndSize(NULL, length);
242+ if (result == NULL) {
243+ return NULL;
244+ }
245+ unsigned char *dest = (unsigned char *)PyBytes_AS_STRING(result);
246+ const Py_ssize_t chunk_size = 65536;
247+ Py_ssize_t offset = 0;
248+ while (offset < length) {
249+ Py_ssize_t count = Py_MIN(chunk_size, length - offset);
250+ Py_BEGIN_ALLOW_THREADS
251+ for (Py_ssize_t i = 0; i < count; i++) {
252+ dest[offset + i] = (unsigned char)source[offset + i] ^ 0xff;
253+ }
254+ Py_END_ALLOW_THREADS
255+ offset += count;
256+ if (PyErr_CheckSignals() < 0) {
257+ Py_DECREF(result);
258+ return NULL;
259+ }
260+ }
261+ return result;
262+ }
263+
264+ The input is immutable and the caller keeps it alive for the duration of the
265+ call. The output is private to this function until it returns, so callbacks
266+ cannot observe a partially filled result. No Python C API calls are made while
267+ the thread state is detached.
268+
269+ Choose a chunk size that balances the overhead of checking and reattaching the
270+ thread state against the time a user must wait for an interruption. The size
271+ above is illustrative; the appropriate interval depends on how much work each
272+ iteration performs. A check outside a long-running inner loop does not make
273+ that loop interruptible. Likewise, a single blocking call to an external
274+ library may need a timeout or a cancellation mechanism provided by that library
275+ so that control returns to a place where you can check for interruptions.
276+
277+ Python signal handlers run only in the main thread of the main interpreter.
278+ Calling :c:func: `PyErr_CheckSignals ` in a worker thread does not run those
279+ handlers there. To cancel work in other threads, arrange for the main thread
280+ to handle the signal and communicate cancellation to the workers using a
281+ thread-safe mechanism. See :ref: `signals-and-threads `.
282+
283+
190284.. _compilation :
191285
192286Embedding an extension
0 commit comments