3
3
|SLP | C-API
4
4
===========
5
5
6
+ |SLP | uses two fundamentally different methods to switch control
7
+ flow from one tasklet to another. One method, called *hard-switching *,
8
+ manipulates the C-stack with hardware-dependent assembly code. This is always
9
+ possible, but somewhat costly. The other method, called *soft-switching *, is
10
+ only possible under special conditions, but is cheap. Moreover, soft-switching
11
+ allows the storage (pickling) and recovery (unpickling) of active tasklets.
12
+
13
+ Soft-switching avoids recursive calls to the |PY | interpreter, such as those
14
+ that occur when calling a |PY | function, by maintaining a chained list of
15
+ tasks that are processed sequentially. This list consists of
16
+ :c:type: `PyFrameObject ` and :c:type: `PyCFrameObject `
17
+ objects chained by their :c:member: `PyFrameObject.f_back ` pointer. In the C-function
18
+ :c:func: `slp_dispatch ` (and :c:func: `slp_dispatch_top `) the list is processed
19
+ in a loop. In order to proceed
20
+ to the processing of the next (C)frame, all C-functions involved in the
21
+ processing of the current (C)frame must return. A special return value
22
+ Unwind-Token is used here. If a C-function returns the value :c:data: `Py_UnwindToken `,
23
+ its caller must add any unfinished tasks to the (C)frame list and return
24
+ :c:data: `Py_UnwindToken ` itself. It follows that *soft-switching * is only possible if
25
+ it is supported by all functions just called. If this is not the case,
26
+ *hard-switching * remains as a fallback.
27
+
6
28
.. note ::
7
29
8
30
Some switching functions have a variant with the
@@ -383,21 +405,21 @@ Soft-switchable extension functions
383
405
The API for soft-switchable extension function has been added on a
384
406
provisional basis (see :pep: `411 ` for details.)
385
407
386
- A soft switchable extension function or method is a function or method defined
408
+ A soft- switchable extension function or method is a function or method defined
387
409
by an extension module written in C. In contrast to an normal C-function you
388
410
can soft-switch tasklets while this function executes. Soft-switchable functions
389
- obey the stackless -protocol. At the C-language level
411
+ obey the Stackless -protocol. At the C-language level
390
412
such a function or method is made from 3 C-definitions:
391
413
392
414
1. A declaration object of type :c:type:`PyStacklessFunctionDeclaration_Type`.
393
415
It declares the soft-switchable function and must be declared as a global
394
416
variable.
395
417
2. A conventional extension function, that uses
396
- :c:func:`PyStackless_CallFunction` to call the soft switchable function.
418
+ :c:func:`PyStackless_CallFunction` to call the soft- switchable function.
397
419
3. A C-function of type ``slp_softswitchablefunc``. This function provides the
398
- implemantation of the soft switchable function.
420
+ implemantation of the soft- switchable function.
399
421
400
- To create a soft switchable function declaration simply define it as a static
422
+ To create a soft- switchable function declaration simply define it as a static
401
423
variable and call :c:func:`PyStackless_InitFunctionDeclaration` from your
402
424
module init code to initialise it. See the example code in the source
403
425
of the extension module `_teststackless <https:// github.com/stackless-dev/stackless/blob/master-slp/Stackless/module/_teststackless.c>`_.
@@ -410,7 +432,7 @@ Typedef ``slp_softswitchablefunc``::
410
432
411
433
.. c :type :: PyStacklessFunctionDeclarationObject
412
434
413
- This subtype of :c:type: `PyObject ` represents a Stackless soft switchable
435
+ This subtype of :c:type: `PyObject ` represents a Stackless soft- switchable
414
436
extension function declaration object.
415
437
416
438
Here is the structure definition::
@@ -437,7 +459,7 @@ Typedef ``slp_softswitchablefunc``::
437
459
.. c :var :: PyTypeObject PyStacklessFunctionDeclaration_Type
438
460
439
461
This instance of :c:type: `PyTypeObject ` represents the Stackless
440
- soft switchable extension function declaration type.
462
+ soft- switchable extension function declaration type.
441
463
442
464
.. c :function :: int PyStacklessFunctionDeclarationType_CheckExact (PyObject *p)
443
465
@@ -446,7 +468,7 @@ Typedef ``slp_softswitchablefunc``::
446
468
447
469
.. c :function :: PyObject* PyStackless_CallFunction (PyStacklessFunctionDeclarationObject *sfd, PyObject *arg, PyObject *ob1, PyObject *ob2, PyObject *ob3, long n, void *any)
448
470
449
- Invoke the soft switchable extension, which is represented by *sfd *.
471
+ Invoke the soft- switchable extension, which is represented by *sfd *.
450
472
Pass *arg * as initial value for argument *retval * and *ob1 *, *ob2 *, *ob3 *,
451
473
*n * and *any * as general purpose in-out-arguments.
452
474
@@ -457,42 +479,67 @@ Typedef ``slp_softswitchablefunc``::
457
479
Initialize the fields :c:member: `PyStacklessFunctionDeclarationObject.name ` and
458
480
:c:member: `PyStacklessFunctionDeclarationObject.module_name ` of *sfd *.
459
481
460
- Within the body of a soft switchable extension function (or any other C-function, that obyes the stackless-protocol)
482
+ Within the body of a soft- switchable extension function (or any other C-function, that obyes the stackless-protocol)
461
483
you need the following macros.
462
484
463
- Macros for the "stackless -protocol"
485
+ Macros for the "Stackless -protocol"
464
486
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
465
487
466
- How to does Stackless Python decide, if a function may return an unwind-token?
467
- There is one global variable "_PyStackless_TRY_STACKLESS"[#]_ which is used
468
- like an implicit parameter. Since we don't have a real parameter,
469
- the flag is copied into the local variable "stackless" and cleared.
470
- This is done by the STACKLESS_GETARG() macro, which should be added to
471
- the top of the function's declarations.
472
488
473
- The idea is to keep the chances to introduce error to the minimum.
474
- A function can safely do some tests and return before calling
475
- anything, since the flag is in a local variable.
476
- Depending on context, this flag is propagated to other called
477
- functions. They *must * obey the protocol. To make this sure,
478
- the STACKLESS_ASSERT() macro has to be called after every such call.
489
+ How does a C-function in |SLP| decide whether it may return
490
+ :c:data:`Py_UnwindToken`? (After all, this is only allowed if the caller can handle
491
+ :c:data: `Py_UnwindToken `). The obvious thing would be to use your own function
492
+ argument, but that would change the function prototypes and thus
493
+ Python's C-API. This is not practical. Instead, the global variable
494
+ "_PyStackless_TRY_STACKLESS"[#f1]_ is used as an implicit parameter.
495
+
496
+ The content of this variable is moved to the local variable "stackless"
497
+ at the beginning of a C function. In the process, "_PyStackless_TRY_STACKLESS"
498
+ is set to 0, indicating that no unwind-token may be returned.
499
+ This is done with the macro :c:func: `STACKLESS_GETARG ` or, for vectorcall [#f2 ]_ functions,
500
+ with the macro :c:func: `STACKLESS_VECTORCALL_GETARG `, which should be added at the
501
+ beginning of the function declaration.
502
+
503
+ This design minimizes the possibility of introducing errors due to improper
504
+ return of :c:data: `Py_UnwindToken `. The function can contain arbitrary code because the
505
+ flag is hidden in a local variable. If the function is to support
506
+ *soft-switching *, it must be further adapted. The flag may only be passed to
507
+ other called functions if they adhere to the Stackless-protocol. The macros
508
+ STACKLESS_PROMOTExxx() serve this purpose. To ensure compliance with the
509
+ protocol, the macro :c:func: `STACKLESS_ASSERT ` must be called after each such call.
510
+ An exception is the call of vectorcall functions. The call of a vectorcall
511
+ function must be framed with the macros :c:func: `STACKLESS_VECTORCALL_BEFORE ` and
512
+ :c:func: `STACKLESS_VECTORCALL_AFTER ` or - more simply - performed with the macro
513
+ :c:func: `STACKLESS_VECTORCALL `.
479
514
480
515
Many internal functions have been patched to support this protocol.
481
516
Their first action is a direct or indirect call of the macro
482
- :c:func: `STACKLESS_GETARG `.
517
+ :c:func: `STACKLESS_GETARG ` or :c:func: ` STACKLESS_VECTORCALL_GETARG ` .
483
518
484
519
.. c :function :: STACKLESS_GETARG()
485
520
486
- Define the local variable ``int stackless `` and move the global
487
- "_PyStackless_TRY_STACKLESS" flag into the local variable "stackless".
488
- After a call to :c:func: `STACKLESS_GETARG ` the value of
521
+ Define and initialize the local variable ``int stackless ``.
522
+ The value of *stackless * is non-zero, if the function may return
523
+ :c:data: `Py_UnwindToken `.
524
+ After a call to :c:func: `STACKLESS_GETARG ` the value of the global variable
489
525
"_PyStackless_TRY_STACKLESS" is 0.
490
526
527
+ .. c :function :: STACKLESS_VECTORCALL_GETARG(func)
528
+
529
+ .. versionadded :: 3.8.0
530
+
531
+ Vectorcall variant of the macro :c:func: `STACKLESS_GETARG `. Functions of type
532
+ :c:type: `vectorcallfunc ` must use :c:func: `STACKLESS_VECTORCALL_GETARG ` instead
533
+ of :c:func: `STACKLESS_GETARG `. The argument *func * must be set to the vectorcall
534
+ function itself. See function :c:func: `_PyCFunction_FastCallKeywords ` for an example.
535
+
491
536
.. c :function :: STACKLESS_PROMOTE_ALL()
492
537
493
- All STACKLESS_PROMOTE_xxx macros are used to propagate the stackless-flag
538
+ All STACKLESS_PROMOTExxx() macros are used to propagate the stackless-flag
494
539
from the local variable "stackless" to the global variable
495
- "_PyStackless_TRY_STACKLESS". The macro :c:func: `STACKLESS_PROMOTE_ALL ` does
540
+ "_PyStackless_TRY_STACKLESS". These macros can't be used to call a vectorcall [#f2 ]_ function.
541
+
542
+ The macro :c:func: `STACKLESS_PROMOTE_ALL ` does
496
543
this unconditionally. It is used for cases where we know that the called
497
544
function will take care of our object, and we need no test. For example,
498
545
:c:func: `PyObject_Call ` and all other Py{Object,Function,CFunction}_*Call*
@@ -539,6 +586,40 @@ Their first action is a direct or indirect call of the macro
539
586
Set the global variable "_PyStackless_TRY_STACKLESS" unconditionally to 0.
540
587
Rarely used.
541
588
589
+ .. c :function :: STACKLESS_VECTORCALL_BEFORE(func)
590
+
591
+ .. c :function :: STACKLESS_VECTORCALL_AFTER(func)
592
+
593
+ .. versionadded :: 3.8.0
594
+
595
+ If a C-function needs to propagate the stackless-flag
596
+ from the local variable "stackless" to the global variable
597
+ "_PyStackless_TRY_STACKLESS" in order to call a vectorcall [#f2 ]_ function, it
598
+ must frame the call with these macros. Set the argument *func * to the called
599
+ function. The called function *func * is not required to support the
600
+ Stackless-protocol. [#f3 ]_ Example:
601
+
602
+ .. code-block :: C
603
+
604
+ STACKLESS_GETARG();
605
+ vectorcallfunc func = a_vectorcal_function;
606
+
607
+ /* other code */
608
+
609
+ STACKLESS_VECTORCALL_BEFORE(func);
610
+ PyObject * result = func(callable, args, nargsf, kwnames);
611
+ STACKLESS_VECTORCALL_AFTER(func);
612
+ return result;
613
+
614
+ .. c :function :: STACKLESS_VECTORCALL(func, callable, args, nargsf, kwnames)
615
+
616
+ .. versionadded :: 3.8.0
617
+
618
+ Call the vectorcall function *func * with the given arguments and return
619
+ the result. It is a convenient alternative to the macros
620
+ :c:func: `STACKLESS_VECTORCALL_BEFORE ` and :c:func: `STACKLESS_VECTORCALL_AFTER `.
621
+ The called function *func * is not required to support the Stackless-protocol.
622
+
542
623
Examples
543
624
~~~~~~~~
544
625
@@ -557,9 +638,18 @@ Another, more realistic example is :py:const:`_asyncio._task_step_impl_stackless
557
638
"Modules/_asynciomodules.c".
558
639
559
640
560
- .. [# ] Actually "_PyStackless_TRY_STACKLESS" is a macro that expands to a C L-value. As long as
641
+ .. [#f1 ] Actually "_PyStackless_TRY_STACKLESS" is a macro that expands to a C L-value. As long as
561
642
|CPY | uses the GIL, this L-value is a global variable.
562
643
644
+ .. [#f2 ] See :pep: `590 ` Vectorcall: a fast calling protocol for CPython
645
+
646
+ .. [#f3 ] If a |PY | type supports the :pep: `590 ` Vectorcall-protocol the actual :c:type: `vectorcallfunc `
647
+ C-function is a per object property. This speeds up calling vectorcall functions on classes,
648
+ but the consequence is, that it is no longer possible to use a flag in the type to indicate,
649
+ if the vectorcall slot supports the Stackless-protocol. Therefore |SLP |
650
+ has special macros to deal with vectorcall functions.
651
+
652
+
563
653
Debugging and monitoring Functions
564
654
----------------------------------
565
655
0 commit comments