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

Commit 2e959be

Browse files
author
Anselm Kruis
authored
Stackless issue #197: stackless call method "contextvars.Context.run"
Enable stackless calls of method "contextvars.Context.run", if soft-switching is enabled.
1 parent bb9dc7a commit 2e959be

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

Python/context.c

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "internal/pystate.h"
55
#include "internal/context.h"
66
#include "internal/hamt.h"
7+
#include "core/stackless_impl.h"
8+
#include "pickling/prickelpit.h"
79

810

911
#define CONTEXT_FREELIST_MAXLEN 255
@@ -562,10 +564,33 @@ _contextvars_Context_copy_impl(PyContext *self)
562564
}
563565

564566

567+
#ifdef STACKLESS
568+
static PyObject* context_run_callback(PyFrameObject *f, int exc, PyObject *result)
569+
{
570+
PyCFrameObject *cf = (PyCFrameObject *)f;
571+
assert(PyContext_CheckExact(cf->ob1));
572+
PyContext *context = (PyContext *)cf->ob1;
573+
cf->ob1 = NULL;
574+
575+
if (PyContext_Exit(context)) {
576+
Py_CLEAR(result);
577+
}
578+
579+
Py_DECREF(context);
580+
SLP_STORE_NEXT_FRAME(PyThreadState_GET(), cf->f_back);
581+
return result;
582+
}
583+
584+
SLP_DEF_INVALID_EXEC(context_run_callback)
585+
#endif
586+
587+
565588
static PyObject *
566589
context_run(PyContext *self, PyObject *const *args,
567590
Py_ssize_t nargs, PyObject *kwnames)
568591
{
592+
STACKLESS_GETARG();
593+
569594
if (nargs < 1) {
570595
PyErr_SetString(PyExc_TypeError,
571596
"run() missing 1 required positional argument");
@@ -576,9 +601,42 @@ context_run(PyContext *self, PyObject *const *args,
576601
return NULL;
577602
}
578603

604+
#ifdef STACKLESS
605+
PyThreadState *ts = PyThreadState_GET();
606+
PyCFrameObject *f = NULL;
607+
if (stackless) {
608+
f = slp_cframe_new(context_run_callback, 1);
609+
if (f == NULL)
610+
return NULL;
611+
Py_INCREF(self);
612+
f->ob1 = (PyObject *)self;
613+
SLP_SET_CURRENT_FRAME(ts, (PyFrameObject *)f);
614+
/* f contains the only counted reference to current frame. This reference
615+
* keeps the fame alive during the following _PyObject_FastCallKeywords().
616+
*/
617+
}
618+
#endif
619+
STACKLESS_PROMOTE_ALL();
620+
579621
PyObject *call_result = _PyObject_FastCallKeywords(
580622
args[0], args + 1, nargs - 1, kwnames);
581623

624+
STACKLESS_ASSERT();
625+
#ifdef STACKLESS
626+
if (stackless && !STACKLESS_UNWINDING(call_result)) {
627+
/* required, because we added a C-frame */
628+
assert(f);
629+
assert((PyFrameObject *)f == SLP_CURRENT_FRAME(ts));
630+
SLP_STORE_NEXT_FRAME(ts, (PyFrameObject *)f);
631+
Py_DECREF(f);
632+
return STACKLESS_PACK(ts, call_result);
633+
}
634+
Py_XDECREF(f);
635+
if (STACKLESS_UNWINDING(call_result)) {
636+
return call_result;
637+
}
638+
#endif
639+
582640
if (PyContext_Exit(self)) {
583641
return NULL;
584642
}
@@ -593,7 +651,7 @@ static PyMethodDef PyContext_methods[] = {
593651
_CONTEXTVARS_CONTEXT_KEYS_METHODDEF
594652
_CONTEXTVARS_CONTEXT_VALUES_METHODDEF
595653
_CONTEXTVARS_CONTEXT_COPY_METHODDEF
596-
{"run", (PyCFunction)context_run, METH_FASTCALL | METH_KEYWORDS, NULL},
654+
{"run", (PyCFunction)context_run, METH_FASTCALL | METH_KEYWORDS | METH_STACKLESS, NULL},
597655
{NULL, NULL}
598656
};
599657

@@ -1222,5 +1280,13 @@ _PyContext_Init(void)
12221280
}
12231281
Py_DECREF(missing);
12241282

1283+
#ifdef STACKLESS
1284+
if (slp_register_execute(&PyCFrame_Type, "context_run_callback",
1285+
context_run_callback, SLP_REF_INVALID_EXEC(context_run_callback)) != 0)
1286+
{
1287+
return 0;
1288+
}
1289+
#endif
1290+
12251291
return 1;
12261292
}

Stackless/changelog.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ What's New in Stackless 3.X.X?
99

1010
*Release date: 20XX-XX-XX*
1111

12+
- https://github.com/stackless-dev/stackless/issues/197
13+
Enable stackless calls of method "contextvars.Context.run", if soft-switching
14+
is enabled.
15+
1216
- https://github.com/stackless-dev/stackless/issues/188
1317
Enable stackless calls of sub-iterators and coroutines (technically: the
1418
YIELD_FROM opcode) and of the following methods, if soft-switching is

Stackless/unittests/test_generator.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import pickle
77
import contextlib
88
import sys
9+
import contextvars
10+
import asyncio
911

1012
from support import test_main # @UnusedImport
1113
from support import StacklessTestCase, captured_stderr
@@ -361,6 +363,27 @@ async def test():
361363
n = self.run_until_complete(t)
362364
self.assertEqual(n, 0)
363365

366+
def test_context_run(self):
367+
contextvars.Context().run(self.assertLevel)
368+
369+
# needs Stackless pull request #188
370+
def xx_test_asyncio(self):
371+
async def test():
372+
try:
373+
await self.coro()
374+
finally:
375+
loop.stop()
376+
377+
asyncio.set_event_loop(asyncio.new_event_loop())
378+
self.addCleanup(asyncio.set_event_loop, None)
379+
loop = asyncio.get_event_loop()
380+
task = asyncio.tasks._PyTask(test())
381+
asyncio.ensure_future(task)
382+
try:
383+
loop.run_forever()
384+
finally:
385+
loop.close()
386+
364387

365388
if __name__ == '__main__':
366389
unittest.main()

0 commit comments

Comments
 (0)