Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 91ee09b

Browse files
authored
Stackless issue #270: Remove field PyFrameObject.f_execute
Stackless now uses an unmodified PyFrameObject structure. The field PyFrameObject.f_executing now stores the information how to evaluate a frame. Additional consequences: - all frame execution functions now take a "PyCFrameObject *" as first argument. - Pickled frames no contain f_executing instead of the name of the execution function.
1 parent 49cfcf9 commit 91ee09b

21 files changed

+200
-348
lines changed

Include/cpython/slp_structs.h

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ extern "C" {
1313

1414
#ifdef SLP_BUILD_CORE
1515

16-
#include "frameobject.h"
1716
#if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86)
1817
#define SLP_SEH32
1918
#endif
@@ -237,22 +236,39 @@ typedef struct _channel {
237236
PyObject *chan_weakreflist;
238237
} PyChannelObject;
239238

239+
struct _cframe;
240+
typedef PyObject *(PyFrame_ExecFunc) (struct _cframe *, int, PyObject *);
241+
/*
242+
* How to write frame execution functions:
243+
*
244+
* Special rule for frame execution functions: the function owns a reference to retval!
245+
*
246+
* PyObject * example(PyCFrameObject *f, int exc, PyObject *retval)
247+
* {
248+
* PyThreadState *ts = _PyThreadState_GET();
249+
*
250+
* do something ....
251+
* If you change retval, use Py_SETREF(retval, new_value) or Py_CLEAR(retval).
252+
*
253+
* SLP_STORE_NEXT_FRAME(ts, f->f_back);
254+
* return retval;
255+
* }
256+
*
257+
*/
240258

241259
/*** important stuctures: cframe ***/
242260

243261
typedef struct _cframe {
244262
PyObject_VAR_HEAD
245263
struct _frame *f_back; /* previous frame, or NULL */
246-
PyFrame_ExecFunc *f_execute;
247264

248265
/*
249-
* the above part is compatible with frames.
250-
* Note that I have re-arranged some fields in the frames
251-
* to keep cframes as small as possible.
266+
* the above part is compatible with regular frames.
252267
*/
253268

254-
/* these can be used as the CFrame likes to */
269+
PyFrame_ExecFunc *f_execute;
255270

271+
/* these can be used as the CFrame likes to */
256272
PyObject *ob1;
257273
PyObject *ob2;
258274
PyObject *ob3;

Include/frameobject.h

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,6 @@
88
extern "C" {
99
#endif
1010

11-
#ifdef STACKLESS
12-
typedef PyObject *(PyFrame_ExecFunc) (struct _frame *, int, PyObject *);
13-
/*
14-
* How to write frame execution functions:
15-
*
16-
* Special rule for frame execution functions: the function owns a reference to retval!
17-
*
18-
* PyObject * example(PyFrameObject *f, int exc, PyObject *retval)
19-
* {
20-
* PyThreadState *ts = _PyThreadState_GET();
21-
*
22-
* do something ....
23-
* if you change retval, use Py_SETREF(retval, new_value) or
24-
* Py_CLEAR(retval)
25-
*
26-
* SLP_STORE_NEXT_FRAME(ts, f->f_back);
27-
* return retval;
28-
* }
29-
*
30-
*/
31-
#endif
32-
3311
typedef struct {
3412
int b_type; /* what kind of block this is */
3513
int b_handler; /* where to jump to find handler */
@@ -39,11 +17,7 @@ typedef struct {
3917
typedef struct _frame {
4018
PyObject_VAR_HEAD
4119
struct _frame *f_back; /* previous frame, or NULL */
42-
#ifdef STACKLESS
43-
PyFrame_ExecFunc *f_execute;/* support for soft stackless */
44-
#else
4520
PyCodeObject *f_code; /* code segment */
46-
#endif
4721
PyObject *f_builtins; /* builtin symbol table (PyDictObject) */
4822
PyObject *f_globals; /* global symbol table (PyDictObject) */
4923
PyObject *f_locals; /* local symbol table (any mapping) */
@@ -69,9 +43,6 @@ typedef struct _frame {
6943
int f_iblock; /* index in f_blockstack */
7044
char f_executing; /* whether the frame is still executing */
7145
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
72-
#ifdef STACKLESS
73-
PyCodeObject *f_code; /* code segment */
74-
#endif
7546
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
7647
} PyFrameObject;
7748

Include/internal/pycore_slp_prickelpit.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ int slp_find_execfuncs(PyTypeObject *type, PyObject *exec_name,
1919
PyFrame_ExecFunc **good,
2020
PyFrame_ExecFunc **bad);
2121

22-
PyObject * slp_find_execname(PyFrameObject *f, int *valid);
22+
PyObject * slp_find_execname(PyCFrameObject *f, int *valid);
2323

24-
PyObject * slp_cannot_execute(PyFrameObject *f, const char *exec_name, PyObject *retval);
24+
PyObject * slp_cannot_execute(PyCFrameObject *f, const char *exec_name, PyObject *retval);
2525

2626
/* macros to define and use an invalid frame executor */
2727

2828
#define SLP_DEF_INVALID_EXEC(procname) \
2929
static PyObject *\
30-
cannot_##procname(PyFrameObject *f, int exc, PyObject *retval) \
30+
cannot_##procname(PyCFrameObject *f, int exc, PyObject *retval) \
3131
{ \
3232
return slp_cannot_execute(f, #procname, retval); \
3333
}

Include/internal/pycore_stackless.h

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ extern "C" {
1717

1818
#ifdef SLP_BUILD_CORE
1919

20+
#include "frameobject.h"
2021
#ifdef Py_BUILD_CORE
2122
#include "pycore_pystate.h" /* for _PyRuntime */
2223
#endif
@@ -39,7 +40,7 @@ extern "C" {
3940
* were created with an older Cython compiled with regular C-Python.
4041
* See Stackless issue #168
4142
*/
42-
#define SLP_END_OF_OLD_CYTHON_HACK_VERSION (0x030800b1)
43+
#define SLP_END_OF_OLD_CYTHON_HACK_VERSION (0x030800a1)
4344
#endif
4445

4546
/*
@@ -325,8 +326,9 @@ PyObject * slp_wrap_call_frame(PyFrameObject *frame, int exc, PyObject *retval);
325326
} while (0)
326327

327328
#define CALL_FRAME_FUNCTION(frame_, exc, retval) \
328-
(assert((frame_) && (frame_)->f_execute), \
329-
((frame_)->f_execute((frame_), (exc), (retval))))
329+
(assert((frame_) && (!PyCFrame_Check(frame_) || ((PyCFrameObject *)(frame_))->f_execute)), \
330+
(PyCFrame_Check(frame_) ? (((PyCFrameObject *)(frame_))->f_execute(((PyCFrameObject *)(frame_)), (exc), (retval))) : \
331+
PyEval_EvalFrameEx_slp((frame_), (exc), (retval))))
330332

331333
#endif
332334

@@ -378,18 +380,10 @@ PyAPI_FUNC(PyObject *) PyEval_EvalFrameEx_slp(struct _frame *, int, PyObject *);
378380
/* eval_frame with stack overflow, triggered there with a macro */
379381
PyObject * slp_eval_frame_newstack(struct _frame *f, int throwflag, PyObject *retval);
380382

381-
/* the new eval_frame loop with or without value or resuming an iterator
382-
or setting up or cleaning up a with block */
383-
PyObject * slp_eval_frame_value(struct _frame *f, int throwflag, PyObject *retval);
384-
PyObject * slp_eval_frame_noval(struct _frame *f, int throwflag, PyObject *retval);
385-
PyObject * slp_eval_frame_iter(struct _frame *f, int throwflag, PyObject *retval);
386-
PyObject * slp_eval_frame_setup_with(struct _frame *f, int throwflag, PyObject *retval);
387-
PyObject * slp_eval_frame_with_cleanup(struct _frame *f, int throwflag, PyObject *retval);
388-
PyObject * slp_eval_frame_yield_from(struct _frame *f, int throwflag, PyObject *retval);
389383
/* other eval_frame functions from module/scheduling.c */
390-
PyObject * slp_restore_tracing(PyFrameObject *f, int exc, PyObject *retval);
384+
PyObject * slp_restore_tracing(PyCFrameObject *cf, int exc, PyObject *retval);
391385
/* other eval_frame functions from Objects/typeobject.c */
392-
PyObject * slp_tp_init_callback(PyFrameObject *f, int exc, PyObject *retval);
386+
PyObject * slp_tp_init_callback(PyCFrameObject *cf, int exc, PyObject *retval);
393387
/* functions related to pickling */
394388
PyObject * slp_reduce_frame(PyFrameObject * frame);
395389

@@ -765,13 +759,13 @@ PyTaskletTStateStruc * slp_get_saved_tstate(PyTaskletObject *task);
765759
/*
766760
* Channel related prototypes
767761
*/
768-
PyObject * slp_channel_seq_callback(struct _frame *f, int throwflag, PyObject *retval);
762+
PyObject * slp_channel_seq_callback(PyCFrameObject *f, int throwflag, PyObject *retval);
769763
PyObject * slp_get_channel_callback(void);
770764

771765
/*
772766
* contextvars related prototypes
773767
*/
774-
PyObject* slp_context_run_callback(PyFrameObject *f, int exc, PyObject *result);
768+
PyObject* slp_context_run_callback(PyCFrameObject *f, int exc, PyObject *result);
775769

776770
/* macro for use when interrupting tasklets from watchdog */
777771
#define TASKLET_NESTING_OK(task) \
@@ -788,6 +782,48 @@ void slp_head_unlock(void);
788782

789783
long slp_parse_thread_id(PyObject *thread_id, unsigned long *id);
790784

785+
/*
786+
* Symbolic names for values stored in PyFrameObject.f_executing
787+
*
788+
* Regular C-Python only uses two values
789+
* 0: the frame is not executing
790+
* 1: the frame is executing
791+
*
792+
* Stackless Python extends the range of values to indicate, what to do
793+
* upon the next invocation of PyEval_EvalFrameEx_slp.
794+
*/
795+
796+
/* Frame is invalid and PyEval_EvalFrameEx_slp must raise an exception.
797+
* Only set, if the frame was unpickled and had C-state on the stack.
798+
*/
799+
#define SLP_FRAME_EXECUTING_INVALID (-1)
800+
801+
/* Frame is new or completely executed. */
802+
#define SLP_FRAME_EXECUTING_NO 0
803+
804+
/* Frame is executing, value in retval is valid and must be pushed onto the stack. */
805+
#define SLP_FRAME_EXECUTING_VALUE 1
806+
807+
/* Frame is executing, ignore value in retval.
808+
* This is used at the start of a frame or after an interrupt. */
809+
#define SLP_FRAME_EXECUTING_NOVAL 2
810+
811+
/* Frame is executing, continue opcode ITER */
812+
#define SLP_FRAME_EXECUTING_ITER 3
813+
814+
/* Frame is executing, continue opcode SETUP_WITH */
815+
#define SLP_FRAME_EXECUTING_SETUP_WITH 4
816+
817+
/* Frame is executing, continue opcode WITH_CLEANUP */
818+
#define SLP_FRAME_EXECUTING_WITH_CLEANUP 5
819+
820+
/* Frame is executing, continue opcode YIELD_FROM */
821+
#define SLP_FRAME_EXECUTING_YIELD_FROM 6
822+
823+
/* Test, if the frame is executing */
824+
#define SLP_FRAME_IS_EXECUTING(frame_) \
825+
((frame_)->f_executing >= SLP_FRAME_EXECUTING_VALUE && \
826+
(frame_)->f_executing <= SLP_FRAME_EXECUTING_YIELD_FROM)
791827

792828

793829
#endif /* #ifdef SLP_BUILD_CORE */

Objects/call.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,6 @@ function_code_fastcall(PyCodeObject *co, PyObject *const *args, Py_ssize_t nargs
354354
}
355355

356356
#ifdef STACKLESS
357-
f->f_execute = PyEval_EvalFrameEx_slp;
358357
if (stackless) {
359358
Py_INCREF(Py_None);
360359
result = Py_None;

Objects/frameobject.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -751,9 +751,6 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
751751
f->f_trace_opcodes = 0;
752752
f->f_trace_lines = 1;
753753

754-
#ifdef STACKLESS
755-
f->f_execute = NULL;
756-
#endif
757754
return f;
758755
}
759756

Objects/genobject.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, int exc, int closing, PyObject* ob
166166

167167
/* callback for (async) generators and coroutines. */
168168
static PyObject *
169-
gen_iternext_callback(PyFrameObject *f, int exc, PyObject *result);
169+
gen_iternext_callback(PyCFrameObject *f, int exc, PyObject *result);
170170

171171
/* Additional callback-code for async generators. */
172172
static PyObject *
@@ -310,8 +310,6 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, int exc, int closing, PyObject * o
310310
gen->gi_exc_state.previous_item = tstate->exc_info;
311311
tstate->exc_info = &gen->gi_exc_state;
312312

313-
f->f_execute = PyEval_EvalFrameEx_slp;
314-
315313
Py_INCREF(gen);
316314
Py_XINCREF(arg);
317315
Py_XINCREF(ob3);
@@ -341,10 +339,10 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, int exc, int closing, PyObject * o
341339
}
342340

343341
static PyObject*
344-
gen_iternext_callback(PyFrameObject *f, int exc, PyObject *result)
342+
gen_iternext_callback(PyCFrameObject *cf, int exc, PyObject *result)
345343
{
346344
PyThreadState *ts = _PyThreadState_GET();
347-
PyCFrameObject *cf = (PyCFrameObject *) f;
345+
PyFrameObject *f = (PyFrameObject *) cf;
348346
PyGenObject *gen = (PyGenObject *) cf->ob1;
349347
PyObject *arg = cf->ob2;
350348
PyObject *ob3 = cf->ob3;

Objects/typeobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6781,10 +6781,10 @@ slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value)
67816781

67826782
#ifdef STACKLESS
67836783
PyObject *
6784-
slp_tp_init_callback(PyFrameObject *f, int exc, PyObject *retval)
6784+
slp_tp_init_callback(PyCFrameObject *cf, int exc, PyObject *retval)
67856785
{
67866786
PyThreadState *ts = _PyThreadState_GET();
6787-
PyCFrameObject *cf = (PyCFrameObject *) f;
6787+
PyFrameObject *f = (PyFrameObject *) cf;
67886788

67896789
f = cf->f_back;
67906790
if (retval != NULL) {

0 commit comments

Comments
 (0)