-
Notifications
You must be signed in to change notification settings - Fork 61
Add support for PEP 567 context variables to tasklets #243
Add support for PEP 567 context variables to tasklets #243
Conversation
… function Use a Py_LOCAL_INLINE function instead of a complicated macro. It is much simpler to debug. In the next commit, we will context switching to this function.
Add a private context attribute and appropriate methods to class tasklet. Document the changes in the manual. New methods: tasklet.set_context(context), tasklet.context_run(...) New readonly attribute: tasklet.context_id
Add/improve pickling of the context of tasklets. New pickle flag "PICKLEFLAGS_PICKLE_CONTEXT", new undocumented function stackless._tasklet_get_unpicklable_state()
I'm now fairly happy with this pull request. |
I'll try to give it a good read tonight. |
Include/slp_structs.h
Outdated
@@ -134,6 +134,7 @@ typedef struct _tasklet { | |||
int recursion_depth; | |||
PyObject *def_globals; | |||
PyObject *tsk_weakreflist; | |||
PyObject *context; /* if running: the saved context, otherwise the context for the tasklet */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is a saved context? If it is the context of a previous tasklet, surely that tasklet's context is stored in that tasklet's
context
member.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In C-Python the thread state (PyThreadState
or short ts
for the current thread state) contains a counted reference to a Context object: ts->context
. This reference may be NULL.
In Stackless Python each tasklet has its own counted reference to a Context object: tasklet->context
.
Now, if you switch from one tasklet to another the reference count of the involved context objects must not change. (During a tasklet switch a recursion into the interpreter is not allowed and therefore Py_DECREF is not allowed.) Therefore its is only possible to "exchange" context between variables.
- exchange the values of old_tasklet->context and ts->context
- exchange the values of new_tasklet->context and ts->context
If you create the main tasklet in slp_initialize_main_and_current()
only one exchange happens:
- exchange the values of main_tasklet->context and ts->context
Same on exit of the main tasklet in schedule_task_destruct()
.
- exchange the values of main_tasklet->context and ts->context
The effect of this schema is, that the value of tasklet->context depends on the state of the tasklet.
- if tasklet is current: tasklet->context is the value of ts->context before the main tasklet was created. ts->context is the context of the current tasklet.
- if tasklet is not current: tasklet->context is the context of the tasklet.
To answer your question: the "saved" context is the value of ts->context
when the main-tasklet was created.
Of course I have to improve the comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to create a context when the main tasklet is created? how about this:
- main tasklet is created and entered. it inherits the threads context.
ts->stackless->current == ts->stackless->main_tasklet,
andts->context
is possibly non null andmain_tasklet->context == NULL
. (sorry if my recollection of stackless ts is a bit rusty) - create a new tasklet. It gets a copy of the ts->context as described.
- switch to another tasklet. .
old_tasklet->context = ts->context
, andts->context = new_tasklet->context
I'm probably missing some fine detail, though, but it still seems to me that the main tasklet's context should be the same as the thread's context that created it. That way, a runnint tasklet has a context of NULL, and a sleeping context has a possibly non-null context. I don't see why we need to keep the context of the main thread before initializing the main tasklet as a separate thing, because the main tasklet is the entry context of the thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are perfectly right. Keeping the context of the main thread is just a side effect of the implementation. And the implementation - the exchange values between thread-state and tasklet logic - was just an attempt to keep the reference counting correct without too much thinking about it: the exchange operation simply does not affect reference counts and there was already suitable macro/inline-function (SLP_EXCHANGE_EXCINFO
). But your suggestion most likely also works an is much simpler. See 71d8e0e
Stackless/module/scheduling.c
Outdated
c = ts_->context; \ | ||
ts_->context = t_->context; \ | ||
t_->context = c; \ | ||
ts_->context_ver++; \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See previous comment about why the currently runnint tasklet has a "saved" context? Also, what is context_ver
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Abaut context_ver:
A context is a Mapping from ContextVar objects to their values. A ContextVar caches its current value and the current context_ver. To invalidate the cache context_ver must be incremented on each change of ts->context. See
Line 759 in 4c7f020
var->var_cached_tsver = ts->context_ver; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow. This is a major addition, probably the biggest change in many years.
I didn't review the C code in detail or the tests, I trust these run fine. Also, my C python eyes have become a bit rusty. But I did have some questions about context and tasklets.
Also, great work, Anselm! |
Thank's. But your remarks make it even better. Many thanks for your review. It is really hard to write good quality code, if you're alone. |
- better documentation of the concept - fix a left-over from the time, when setting the context of a main- tasklet was not supported - fix the context handling, when the main tasklet starts/ends. Copy the context from/to the thread state.
- fix the context handling, when the main tasklet starts/ends. Copy the context from/to the thread state. Fix the test case.
I fixed the issues found by Kristján and one other issue. |
Use stackless._stackless._test_outside instead of stackless.test_outside.
Couple of things I'd like explained.
contexts managed by stackless are great, by the way, since we (probably) don't need to manipulate them, or have these "run in context" functions like a async hub has to do (i.e. manually switching context). ... Maybe this can be added to the doc, explainin that there really needs to be no manual management of contexts when using tasklets. |
Simplify context switching as sugested by Kristján.
Good point. I simplified the code in 71d8e0e. (When I coded the first version I just copied the approach used for the exception
I already have a valid use case for tasklet.context_run(). This method is required, if you have to execute something on behalf of a tasklet. In the example at https://github.com/akruis/slp_coroutines/blob/03333e72356a06835b742b4e78b88c544a3a00bb/src/slp_coroutine.py#L109-L112 the tasklet wants you to await a a coroutine. I can imagine a requirement for tasklet.set_context()
For tasklet.context_id probably debugging is the only valid use case. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great stuff. sorry for my late review, Busy with things :)
I'm busy too, during the day. Stackless is an evening project. It gets me away from watching youtube all the time. There isn't much you can do in the evenings these days. |
A possible implementation for #239.