blob: f48c376b4ffaa98b705d8d8ff336586e0097a061 [file] [log] [blame]
Yury Selivanovf23746a2018-01-22 19:11:18 -05001#include "Python.h"
2
Victor Stinner27e2d1f2018-11-01 00:52:28 +01003#include "pycore_context.h"
4#include "pycore_hamt.h"
Victor Stinnerbcda8f12018-11-21 22:27:47 +01005#include "pycore_object.h"
6#include "pycore_pystate.h"
7#include "structmember.h"
Yury Selivanovf23746a2018-01-22 19:11:18 -05008
9
10#define CONTEXT_FREELIST_MAXLEN 255
11static PyContext *ctx_freelist = NULL;
Serhiy Storchakabfe4fd52018-02-09 17:31:26 +020012static int ctx_freelist_len = 0;
Yury Selivanovf23746a2018-01-22 19:11:18 -050013
14
15#include "clinic/context.c.h"
16/*[clinic input]
17module _contextvars
18[clinic start generated code]*/
19/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a0955718c8b8cea6]*/
20
21
Yury Selivanov2ec872b2018-09-21 15:33:56 -040022#define ENSURE_Context(o, err_ret) \
23 if (!PyContext_CheckExact(o)) { \
24 PyErr_SetString(PyExc_TypeError, \
25 "an instance of Context was expected"); \
26 return err_ret; \
27 }
28
29#define ENSURE_ContextVar(o, err_ret) \
30 if (!PyContextVar_CheckExact(o)) { \
31 PyErr_SetString(PyExc_TypeError, \
32 "an instance of ContextVar was expected"); \
33 return err_ret; \
34 }
35
36#define ENSURE_ContextToken(o, err_ret) \
37 if (!PyContextToken_CheckExact(o)) { \
38 PyErr_SetString(PyExc_TypeError, \
39 "an instance of Token was expected"); \
40 return err_ret; \
41 }
42
43
Yury Selivanovf23746a2018-01-22 19:11:18 -050044/////////////////////////// Context API
45
46
47static PyContext *
48context_new_empty(void);
49
50static PyContext *
51context_new_from_vars(PyHamtObject *vars);
52
53static inline PyContext *
54context_get(void);
55
56static PyContextToken *
57token_new(PyContext *ctx, PyContextVar *var, PyObject *val);
58
59static PyContextVar *
60contextvar_new(PyObject *name, PyObject *def);
61
62static int
63contextvar_set(PyContextVar *var, PyObject *val);
64
65static int
66contextvar_del(PyContextVar *var);
67
68
69PyObject *
70_PyContext_NewHamtForTests(void)
71{
72 return (PyObject *)_PyHamt_New();
73}
74
75
Yury Selivanov2ec872b2018-09-21 15:33:56 -040076PyObject *
Yury Selivanovf23746a2018-01-22 19:11:18 -050077PyContext_New(void)
78{
Yury Selivanov2ec872b2018-09-21 15:33:56 -040079 return (PyObject *)context_new_empty();
Yury Selivanovf23746a2018-01-22 19:11:18 -050080}
81
82
Yury Selivanov2ec872b2018-09-21 15:33:56 -040083PyObject *
84PyContext_Copy(PyObject * octx)
Yury Selivanovf23746a2018-01-22 19:11:18 -050085{
Yury Selivanov2ec872b2018-09-21 15:33:56 -040086 ENSURE_Context(octx, NULL)
87 PyContext *ctx = (PyContext *)octx;
88 return (PyObject *)context_new_from_vars(ctx->ctx_vars);
Yury Selivanovf23746a2018-01-22 19:11:18 -050089}
90
91
Yury Selivanov2ec872b2018-09-21 15:33:56 -040092PyObject *
Yury Selivanovf23746a2018-01-22 19:11:18 -050093PyContext_CopyCurrent(void)
94{
95 PyContext *ctx = context_get();
96 if (ctx == NULL) {
97 return NULL;
98 }
99
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400100 return (PyObject *)context_new_from_vars(ctx->ctx_vars);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500101}
102
103
104int
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400105PyContext_Enter(PyObject *octx)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500106{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400107 ENSURE_Context(octx, -1)
108 PyContext *ctx = (PyContext *)octx;
109
Yury Selivanovf23746a2018-01-22 19:11:18 -0500110 if (ctx->ctx_entered) {
111 PyErr_Format(PyExc_RuntimeError,
112 "cannot enter context: %R is already entered", ctx);
113 return -1;
114 }
115
Victor Stinner50b48572018-11-01 01:51:40 +0100116 PyThreadState *ts = _PyThreadState_GET();
Yury Selivanovbc4123b2018-01-27 13:24:20 -0500117 assert(ts != NULL);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500118
119 ctx->ctx_prev = (PyContext *)ts->context; /* borrow */
120 ctx->ctx_entered = 1;
121
122 Py_INCREF(ctx);
123 ts->context = (PyObject *)ctx;
124 ts->context_ver++;
125
126 return 0;
127}
128
129
130int
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400131PyContext_Exit(PyObject *octx)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500132{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400133 ENSURE_Context(octx, -1)
134 PyContext *ctx = (PyContext *)octx;
135
Yury Selivanovf23746a2018-01-22 19:11:18 -0500136 if (!ctx->ctx_entered) {
137 PyErr_Format(PyExc_RuntimeError,
138 "cannot exit context: %R has not been entered", ctx);
139 return -1;
140 }
141
Victor Stinner50b48572018-11-01 01:51:40 +0100142 PyThreadState *ts = _PyThreadState_GET();
Yury Selivanovbc4123b2018-01-27 13:24:20 -0500143 assert(ts != NULL);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500144
145 if (ts->context != (PyObject *)ctx) {
146 /* Can only happen if someone misuses the C API */
147 PyErr_SetString(PyExc_RuntimeError,
148 "cannot exit context: thread state references "
149 "a different context object");
150 return -1;
151 }
152
153 Py_SETREF(ts->context, (PyObject *)ctx->ctx_prev);
154 ts->context_ver++;
155
156 ctx->ctx_prev = NULL;
157 ctx->ctx_entered = 0;
158
159 return 0;
160}
161
162
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400163PyObject *
Yury Selivanovf23746a2018-01-22 19:11:18 -0500164PyContextVar_New(const char *name, PyObject *def)
165{
166 PyObject *pyname = PyUnicode_FromString(name);
167 if (pyname == NULL) {
168 return NULL;
169 }
Yury Selivanov6ab62922018-01-25 14:18:55 -0500170 PyContextVar *var = contextvar_new(pyname, def);
171 Py_DECREF(pyname);
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400172 return (PyObject *)var;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500173}
174
175
176int
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400177PyContextVar_Get(PyObject *ovar, PyObject *def, PyObject **val)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500178{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400179 ENSURE_ContextVar(ovar, -1)
180 PyContextVar *var = (PyContextVar *)ovar;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500181
Victor Stinner50b48572018-11-01 01:51:40 +0100182 PyThreadState *ts = _PyThreadState_GET();
Yury Selivanov226e5002018-01-26 17:24:52 -0500183 assert(ts != NULL);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500184 if (ts->context == NULL) {
185 goto not_found;
186 }
187
188 if (var->var_cached != NULL &&
189 var->var_cached_tsid == ts->id &&
190 var->var_cached_tsver == ts->context_ver)
191 {
192 *val = var->var_cached;
193 goto found;
194 }
195
196 assert(PyContext_CheckExact(ts->context));
197 PyHamtObject *vars = ((PyContext *)ts->context)->ctx_vars;
198
199 PyObject *found = NULL;
200 int res = _PyHamt_Find(vars, (PyObject*)var, &found);
201 if (res < 0) {
202 goto error;
203 }
204 if (res == 1) {
205 assert(found != NULL);
206 var->var_cached = found; /* borrow */
207 var->var_cached_tsid = ts->id;
208 var->var_cached_tsver = ts->context_ver;
209
210 *val = found;
211 goto found;
212 }
213
214not_found:
215 if (def == NULL) {
216 if (var->var_default != NULL) {
217 *val = var->var_default;
218 goto found;
219 }
220
221 *val = NULL;
222 goto found;
223 }
224 else {
225 *val = def;
226 goto found;
227 }
228
229found:
230 Py_XINCREF(*val);
231 return 0;
232
233error:
234 *val = NULL;
235 return -1;
236}
237
238
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400239PyObject *
240PyContextVar_Set(PyObject *ovar, PyObject *val)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500241{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400242 ENSURE_ContextVar(ovar, NULL)
243 PyContextVar *var = (PyContextVar *)ovar;
244
Yury Selivanovf23746a2018-01-22 19:11:18 -0500245 if (!PyContextVar_CheckExact(var)) {
246 PyErr_SetString(
247 PyExc_TypeError, "an instance of ContextVar was expected");
248 return NULL;
249 }
250
251 PyContext *ctx = context_get();
252 if (ctx == NULL) {
253 return NULL;
254 }
255
256 PyObject *old_val = NULL;
257 int found = _PyHamt_Find(ctx->ctx_vars, (PyObject *)var, &old_val);
258 if (found < 0) {
259 return NULL;
260 }
261
262 Py_XINCREF(old_val);
263 PyContextToken *tok = token_new(ctx, var, old_val);
264 Py_XDECREF(old_val);
265
266 if (contextvar_set(var, val)) {
267 Py_DECREF(tok);
268 return NULL;
269 }
270
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400271 return (PyObject *)tok;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500272}
273
274
275int
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400276PyContextVar_Reset(PyObject *ovar, PyObject *otok)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500277{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400278 ENSURE_ContextVar(ovar, -1)
279 ENSURE_ContextToken(otok, -1)
280 PyContextVar *var = (PyContextVar *)ovar;
281 PyContextToken *tok = (PyContextToken *)otok;
282
Yury Selivanovf23746a2018-01-22 19:11:18 -0500283 if (tok->tok_used) {
284 PyErr_Format(PyExc_RuntimeError,
285 "%R has already been used once", tok);
286 return -1;
287 }
288
289 if (var != tok->tok_var) {
290 PyErr_Format(PyExc_ValueError,
291 "%R was created by a different ContextVar", tok);
292 return -1;
293 }
294
295 PyContext *ctx = context_get();
296 if (ctx != tok->tok_ctx) {
297 PyErr_Format(PyExc_ValueError,
298 "%R was created in a different Context", tok);
299 return -1;
300 }
301
302 tok->tok_used = 1;
303
304 if (tok->tok_oldval == NULL) {
305 return contextvar_del(var);
306 }
307 else {
308 return contextvar_set(var, tok->tok_oldval);
309 }
310}
311
312
313/////////////////////////// PyContext
314
315/*[clinic input]
316class _contextvars.Context "PyContext *" "&PyContext_Type"
317[clinic start generated code]*/
318/*[clinic end generated code: output=da39a3ee5e6b4b0d input=bdf87f8e0cb580e8]*/
319
320
321static inline PyContext *
322_context_alloc(void)
323{
324 PyContext *ctx;
325 if (ctx_freelist_len) {
326 ctx_freelist_len--;
327 ctx = ctx_freelist;
328 ctx_freelist = (PyContext *)ctx->ctx_weakreflist;
329 ctx->ctx_weakreflist = NULL;
330 _Py_NewReference((PyObject *)ctx);
331 }
332 else {
333 ctx = PyObject_GC_New(PyContext, &PyContext_Type);
334 if (ctx == NULL) {
335 return NULL;
336 }
337 }
338
339 ctx->ctx_vars = NULL;
340 ctx->ctx_prev = NULL;
341 ctx->ctx_entered = 0;
342 ctx->ctx_weakreflist = NULL;
343
344 return ctx;
345}
346
347
348static PyContext *
349context_new_empty(void)
350{
351 PyContext *ctx = _context_alloc();
352 if (ctx == NULL) {
353 return NULL;
354 }
355
356 ctx->ctx_vars = _PyHamt_New();
357 if (ctx->ctx_vars == NULL) {
358 Py_DECREF(ctx);
359 return NULL;
360 }
361
362 _PyObject_GC_TRACK(ctx);
363 return ctx;
364}
365
366
367static PyContext *
368context_new_from_vars(PyHamtObject *vars)
369{
370 PyContext *ctx = _context_alloc();
371 if (ctx == NULL) {
372 return NULL;
373 }
374
375 Py_INCREF(vars);
376 ctx->ctx_vars = vars;
377
378 _PyObject_GC_TRACK(ctx);
379 return ctx;
380}
381
382
383static inline PyContext *
384context_get(void)
385{
Victor Stinner50b48572018-11-01 01:51:40 +0100386 PyThreadState *ts = _PyThreadState_GET();
Yury Selivanovbc4123b2018-01-27 13:24:20 -0500387 assert(ts != NULL);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500388 PyContext *current_ctx = (PyContext *)ts->context;
389 if (current_ctx == NULL) {
390 current_ctx = context_new_empty();
391 if (current_ctx == NULL) {
392 return NULL;
393 }
394 ts->context = (PyObject *)current_ctx;
395 }
396 return current_ctx;
397}
398
399static int
400context_check_key_type(PyObject *key)
401{
402 if (!PyContextVar_CheckExact(key)) {
403 // abort();
404 PyErr_Format(PyExc_TypeError,
405 "a ContextVar key was expected, got %R", key);
406 return -1;
407 }
408 return 0;
409}
410
411static PyObject *
412context_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
413{
414 if (PyTuple_Size(args) || (kwds != NULL && PyDict_Size(kwds))) {
415 PyErr_SetString(
416 PyExc_TypeError, "Context() does not accept any arguments");
417 return NULL;
418 }
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400419 return PyContext_New();
Yury Selivanovf23746a2018-01-22 19:11:18 -0500420}
421
422static int
423context_tp_clear(PyContext *self)
424{
425 Py_CLEAR(self->ctx_prev);
426 Py_CLEAR(self->ctx_vars);
427 return 0;
428}
429
430static int
431context_tp_traverse(PyContext *self, visitproc visit, void *arg)
432{
433 Py_VISIT(self->ctx_prev);
434 Py_VISIT(self->ctx_vars);
435 return 0;
436}
437
438static void
439context_tp_dealloc(PyContext *self)
440{
441 _PyObject_GC_UNTRACK(self);
442
443 if (self->ctx_weakreflist != NULL) {
444 PyObject_ClearWeakRefs((PyObject*)self);
445 }
446 (void)context_tp_clear(self);
447
448 if (ctx_freelist_len < CONTEXT_FREELIST_MAXLEN) {
449 ctx_freelist_len++;
450 self->ctx_weakreflist = (PyObject *)ctx_freelist;
451 ctx_freelist = self;
452 }
453 else {
454 Py_TYPE(self)->tp_free(self);
455 }
456}
457
458static PyObject *
459context_tp_iter(PyContext *self)
460{
461 return _PyHamt_NewIterKeys(self->ctx_vars);
462}
463
464static PyObject *
465context_tp_richcompare(PyObject *v, PyObject *w, int op)
466{
467 if (!PyContext_CheckExact(v) || !PyContext_CheckExact(w) ||
468 (op != Py_EQ && op != Py_NE))
469 {
470 Py_RETURN_NOTIMPLEMENTED;
471 }
472
473 int res = _PyHamt_Eq(
474 ((PyContext *)v)->ctx_vars, ((PyContext *)w)->ctx_vars);
475 if (res < 0) {
476 return NULL;
477 }
478
479 if (op == Py_NE) {
480 res = !res;
481 }
482
483 if (res) {
484 Py_RETURN_TRUE;
485 }
486 else {
487 Py_RETURN_FALSE;
488 }
489}
490
491static Py_ssize_t
492context_tp_len(PyContext *self)
493{
494 return _PyHamt_Len(self->ctx_vars);
495}
496
497static PyObject *
498context_tp_subscript(PyContext *self, PyObject *key)
499{
500 if (context_check_key_type(key)) {
501 return NULL;
502 }
503 PyObject *val = NULL;
504 int found = _PyHamt_Find(self->ctx_vars, key, &val);
505 if (found < 0) {
506 return NULL;
507 }
508 if (found == 0) {
509 PyErr_SetObject(PyExc_KeyError, key);
510 return NULL;
511 }
512 Py_INCREF(val);
513 return val;
514}
515
516static int
517context_tp_contains(PyContext *self, PyObject *key)
518{
519 if (context_check_key_type(key)) {
520 return -1;
521 }
522 PyObject *val = NULL;
523 return _PyHamt_Find(self->ctx_vars, key, &val);
524}
525
526
527/*[clinic input]
528_contextvars.Context.get
529 key: object
530 default: object = None
531 /
Peter Lamut20678fd2018-07-30 16:15:44 +0100532
533Return the value for `key` if `key` has the value in the context object.
534
535If `key` does not exist, return `default`. If `default` is not given,
536return None.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500537[clinic start generated code]*/
538
539static PyObject *
540_contextvars_Context_get_impl(PyContext *self, PyObject *key,
541 PyObject *default_value)
Peter Lamut20678fd2018-07-30 16:15:44 +0100542/*[clinic end generated code: output=0c54aa7664268189 input=c8eeb81505023995]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500543{
544 if (context_check_key_type(key)) {
545 return NULL;
546 }
547
548 PyObject *val = NULL;
549 int found = _PyHamt_Find(self->ctx_vars, key, &val);
550 if (found < 0) {
551 return NULL;
552 }
553 if (found == 0) {
554 Py_INCREF(default_value);
555 return default_value;
556 }
557 Py_INCREF(val);
558 return val;
559}
560
561
562/*[clinic input]
563_contextvars.Context.items
Peter Lamut20678fd2018-07-30 16:15:44 +0100564
565Return all variables and their values in the context object.
566
567The result is returned as a list of 2-tuples (variable, value).
Yury Selivanovf23746a2018-01-22 19:11:18 -0500568[clinic start generated code]*/
569
570static PyObject *
571_contextvars_Context_items_impl(PyContext *self)
Peter Lamut20678fd2018-07-30 16:15:44 +0100572/*[clinic end generated code: output=fa1655c8a08502af input=00db64ae379f9f42]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500573{
574 return _PyHamt_NewIterItems(self->ctx_vars);
575}
576
577
578/*[clinic input]
579_contextvars.Context.keys
Peter Lamut20678fd2018-07-30 16:15:44 +0100580
581Return a list of all variables in the context object.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500582[clinic start generated code]*/
583
584static PyObject *
585_contextvars_Context_keys_impl(PyContext *self)
Peter Lamut20678fd2018-07-30 16:15:44 +0100586/*[clinic end generated code: output=177227c6b63ec0e2 input=114b53aebca3449c]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500587{
588 return _PyHamt_NewIterKeys(self->ctx_vars);
589}
590
591
592/*[clinic input]
593_contextvars.Context.values
Peter Lamut20678fd2018-07-30 16:15:44 +0100594
animalize463572c2019-02-25 07:18:48 +0800595Return a list of all variables' values in the context object.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500596[clinic start generated code]*/
597
598static PyObject *
599_contextvars_Context_values_impl(PyContext *self)
animalize463572c2019-02-25 07:18:48 +0800600/*[clinic end generated code: output=d286dabfc8db6dde input=ce8075d04a6ea526]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500601{
602 return _PyHamt_NewIterValues(self->ctx_vars);
603}
604
605
606/*[clinic input]
607_contextvars.Context.copy
Peter Lamut20678fd2018-07-30 16:15:44 +0100608
609Return a shallow copy of the context object.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500610[clinic start generated code]*/
611
612static PyObject *
613_contextvars_Context_copy_impl(PyContext *self)
Peter Lamut20678fd2018-07-30 16:15:44 +0100614/*[clinic end generated code: output=30ba8896c4707a15 input=ebafdbdd9c72d592]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500615{
616 return (PyObject *)context_new_from_vars(self->ctx_vars);
617}
618
619
620static PyObject *
621context_run(PyContext *self, PyObject *const *args,
622 Py_ssize_t nargs, PyObject *kwnames)
623{
624 if (nargs < 1) {
625 PyErr_SetString(PyExc_TypeError,
626 "run() missing 1 required positional argument");
627 return NULL;
628 }
629
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400630 if (PyContext_Enter((PyObject *)self)) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500631 return NULL;
632 }
633
Jeroen Demeyeraacc77f2019-05-29 20:31:52 +0200634 PyObject *call_result = _PyObject_Vectorcall(
Yury Selivanovf23746a2018-01-22 19:11:18 -0500635 args[0], args + 1, nargs - 1, kwnames);
636
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400637 if (PyContext_Exit((PyObject *)self)) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500638 return NULL;
639 }
640
641 return call_result;
642}
643
644
645static PyMethodDef PyContext_methods[] = {
646 _CONTEXTVARS_CONTEXT_GET_METHODDEF
647 _CONTEXTVARS_CONTEXT_ITEMS_METHODDEF
648 _CONTEXTVARS_CONTEXT_KEYS_METHODDEF
649 _CONTEXTVARS_CONTEXT_VALUES_METHODDEF
650 _CONTEXTVARS_CONTEXT_COPY_METHODDEF
Serhiy Storchaka62be7422018-11-27 13:27:31 +0200651 {"run", (PyCFunction)(void(*)(void))context_run, METH_FASTCALL | METH_KEYWORDS, NULL},
Yury Selivanovf23746a2018-01-22 19:11:18 -0500652 {NULL, NULL}
653};
654
655static PySequenceMethods PyContext_as_sequence = {
656 0, /* sq_length */
657 0, /* sq_concat */
658 0, /* sq_repeat */
659 0, /* sq_item */
660 0, /* sq_slice */
661 0, /* sq_ass_item */
662 0, /* sq_ass_slice */
663 (objobjproc)context_tp_contains, /* sq_contains */
664 0, /* sq_inplace_concat */
665 0, /* sq_inplace_repeat */
666};
667
668static PyMappingMethods PyContext_as_mapping = {
669 (lenfunc)context_tp_len, /* mp_length */
670 (binaryfunc)context_tp_subscript, /* mp_subscript */
671};
672
673PyTypeObject PyContext_Type = {
674 PyVarObject_HEAD_INIT(&PyType_Type, 0)
675 "Context",
676 sizeof(PyContext),
677 .tp_methods = PyContext_methods,
678 .tp_as_mapping = &PyContext_as_mapping,
679 .tp_as_sequence = &PyContext_as_sequence,
680 .tp_iter = (getiterfunc)context_tp_iter,
681 .tp_dealloc = (destructor)context_tp_dealloc,
682 .tp_getattro = PyObject_GenericGetAttr,
683 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
684 .tp_richcompare = context_tp_richcompare,
685 .tp_traverse = (traverseproc)context_tp_traverse,
686 .tp_clear = (inquiry)context_tp_clear,
687 .tp_new = context_tp_new,
688 .tp_weaklistoffset = offsetof(PyContext, ctx_weakreflist),
689 .tp_hash = PyObject_HashNotImplemented,
690};
691
692
693/////////////////////////// ContextVar
694
695
696static int
697contextvar_set(PyContextVar *var, PyObject *val)
698{
699 var->var_cached = NULL;
700 PyThreadState *ts = PyThreadState_Get();
701
702 PyContext *ctx = context_get();
703 if (ctx == NULL) {
704 return -1;
705 }
706
707 PyHamtObject *new_vars = _PyHamt_Assoc(
708 ctx->ctx_vars, (PyObject *)var, val);
709 if (new_vars == NULL) {
710 return -1;
711 }
712
713 Py_SETREF(ctx->ctx_vars, new_vars);
714
715 var->var_cached = val; /* borrow */
716 var->var_cached_tsid = ts->id;
717 var->var_cached_tsver = ts->context_ver;
718 return 0;
719}
720
721static int
722contextvar_del(PyContextVar *var)
723{
724 var->var_cached = NULL;
725
726 PyContext *ctx = context_get();
727 if (ctx == NULL) {
728 return -1;
729 }
730
731 PyHamtObject *vars = ctx->ctx_vars;
732 PyHamtObject *new_vars = _PyHamt_Without(vars, (PyObject *)var);
733 if (new_vars == NULL) {
734 return -1;
735 }
736
737 if (vars == new_vars) {
738 Py_DECREF(new_vars);
739 PyErr_SetObject(PyExc_LookupError, (PyObject *)var);
740 return -1;
741 }
742
743 Py_SETREF(ctx->ctx_vars, new_vars);
744 return 0;
745}
746
747static Py_hash_t
748contextvar_generate_hash(void *addr, PyObject *name)
749{
750 /* Take hash of `name` and XOR it with the object's addr.
751
752 The structure of the tree is encoded in objects' hashes, which
753 means that sufficiently similar hashes would result in tall trees
754 with many Collision nodes. Which would, in turn, result in slower
755 get and set operations.
756
757 The XORing helps to ensure that:
758
759 (1) sequentially allocated ContextVar objects have
760 different hashes;
761
762 (2) context variables with equal names have
763 different hashes.
764 */
765
766 Py_hash_t name_hash = PyObject_Hash(name);
767 if (name_hash == -1) {
768 return -1;
769 }
770
771 Py_hash_t res = _Py_HashPointer(addr) ^ name_hash;
772 return res == -1 ? -2 : res;
773}
774
775static PyContextVar *
776contextvar_new(PyObject *name, PyObject *def)
777{
778 if (!PyUnicode_Check(name)) {
779 PyErr_SetString(PyExc_TypeError,
780 "context variable name must be a str");
781 return NULL;
782 }
783
784 PyContextVar *var = PyObject_GC_New(PyContextVar, &PyContextVar_Type);
785 if (var == NULL) {
786 return NULL;
787 }
788
789 var->var_hash = contextvar_generate_hash(var, name);
790 if (var->var_hash == -1) {
791 Py_DECREF(var);
792 return NULL;
793 }
794
795 Py_INCREF(name);
796 var->var_name = name;
797
798 Py_XINCREF(def);
799 var->var_default = def;
800
801 var->var_cached = NULL;
802 var->var_cached_tsid = 0;
803 var->var_cached_tsver = 0;
804
Yury Selivanov6ab62922018-01-25 14:18:55 -0500805 if (_PyObject_GC_MAY_BE_TRACKED(name) ||
806 (def != NULL && _PyObject_GC_MAY_BE_TRACKED(def)))
Yury Selivanovf23746a2018-01-22 19:11:18 -0500807 {
808 PyObject_GC_Track(var);
809 }
810 return var;
811}
812
813
814/*[clinic input]
815class _contextvars.ContextVar "PyContextVar *" "&PyContextVar_Type"
816[clinic start generated code]*/
817/*[clinic end generated code: output=da39a3ee5e6b4b0d input=445da935fa8883c3]*/
818
819
820static PyObject *
821contextvar_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
822{
823 static char *kwlist[] = {"", "default", NULL};
824 PyObject *name;
825 PyObject *def = NULL;
826
827 if (!PyArg_ParseTupleAndKeywords(
828 args, kwds, "O|$O:ContextVar", kwlist, &name, &def))
829 {
830 return NULL;
831 }
832
833 return (PyObject *)contextvar_new(name, def);
834}
835
836static int
837contextvar_tp_clear(PyContextVar *self)
838{
839 Py_CLEAR(self->var_name);
840 Py_CLEAR(self->var_default);
841 self->var_cached = NULL;
842 self->var_cached_tsid = 0;
843 self->var_cached_tsver = 0;
844 return 0;
845}
846
847static int
848contextvar_tp_traverse(PyContextVar *self, visitproc visit, void *arg)
849{
850 Py_VISIT(self->var_name);
851 Py_VISIT(self->var_default);
852 return 0;
853}
854
855static void
856contextvar_tp_dealloc(PyContextVar *self)
857{
858 PyObject_GC_UnTrack(self);
859 (void)contextvar_tp_clear(self);
860 Py_TYPE(self)->tp_free(self);
861}
862
863static Py_hash_t
864contextvar_tp_hash(PyContextVar *self)
865{
866 return self->var_hash;
867}
868
869static PyObject *
870contextvar_tp_repr(PyContextVar *self)
871{
872 _PyUnicodeWriter writer;
873
874 _PyUnicodeWriter_Init(&writer);
875
876 if (_PyUnicodeWriter_WriteASCIIString(
877 &writer, "<ContextVar name=", 17) < 0)
878 {
879 goto error;
880 }
881
882 PyObject *name = PyObject_Repr(self->var_name);
883 if (name == NULL) {
884 goto error;
885 }
886 if (_PyUnicodeWriter_WriteStr(&writer, name) < 0) {
887 Py_DECREF(name);
888 goto error;
889 }
890 Py_DECREF(name);
891
892 if (self->var_default != NULL) {
893 if (_PyUnicodeWriter_WriteASCIIString(&writer, " default=", 9) < 0) {
894 goto error;
895 }
896
897 PyObject *def = PyObject_Repr(self->var_default);
898 if (def == NULL) {
899 goto error;
900 }
901 if (_PyUnicodeWriter_WriteStr(&writer, def) < 0) {
902 Py_DECREF(def);
903 goto error;
904 }
905 Py_DECREF(def);
906 }
907
908 PyObject *addr = PyUnicode_FromFormat(" at %p>", self);
909 if (addr == NULL) {
910 goto error;
911 }
912 if (_PyUnicodeWriter_WriteStr(&writer, addr) < 0) {
913 Py_DECREF(addr);
914 goto error;
915 }
916 Py_DECREF(addr);
917
918 return _PyUnicodeWriter_Finish(&writer);
919
920error:
921 _PyUnicodeWriter_Dealloc(&writer);
922 return NULL;
923}
924
925
926/*[clinic input]
927_contextvars.ContextVar.get
928 default: object = NULL
929 /
Peter Lamut20678fd2018-07-30 16:15:44 +0100930
931Return a value for the context variable for the current context.
932
933If there is no value for the variable in the current context, the method will:
934 * return the value of the default argument of the method, if provided; or
935 * return the default value for the context variable, if it was created
936 with one; or
937 * raise a LookupError.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500938[clinic start generated code]*/
939
940static PyObject *
941_contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value)
Peter Lamut20678fd2018-07-30 16:15:44 +0100942/*[clinic end generated code: output=0746bd0aa2ced7bf input=30aa2ab9e433e401]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500943{
944 if (!PyContextVar_CheckExact(self)) {
945 PyErr_SetString(
946 PyExc_TypeError, "an instance of ContextVar was expected");
947 return NULL;
948 }
949
950 PyObject *val;
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400951 if (PyContextVar_Get((PyObject *)self, default_value, &val) < 0) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500952 return NULL;
953 }
954
955 if (val == NULL) {
956 PyErr_SetObject(PyExc_LookupError, (PyObject *)self);
957 return NULL;
958 }
959
960 return val;
961}
962
963/*[clinic input]
964_contextvars.ContextVar.set
965 value: object
966 /
Peter Lamut20678fd2018-07-30 16:15:44 +0100967
968Call to set a new value for the context variable in the current context.
969
970The required value argument is the new value for the context variable.
971
972Returns a Token object that can be used to restore the variable to its previous
973value via the `ContextVar.reset()` method.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500974[clinic start generated code]*/
975
976static PyObject *
977_contextvars_ContextVar_set(PyContextVar *self, PyObject *value)
Peter Lamut20678fd2018-07-30 16:15:44 +0100978/*[clinic end generated code: output=446ed5e820d6d60b input=c0a6887154227453]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500979{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400980 return PyContextVar_Set((PyObject *)self, value);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500981}
982
983/*[clinic input]
984_contextvars.ContextVar.reset
985 token: object
986 /
Peter Lamut20678fd2018-07-30 16:15:44 +0100987
988Reset the context variable.
989
990The variable is reset to the value it had before the `ContextVar.set()` that
991created the token was used.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500992[clinic start generated code]*/
993
994static PyObject *
995_contextvars_ContextVar_reset(PyContextVar *self, PyObject *token)
Peter Lamut20678fd2018-07-30 16:15:44 +0100996/*[clinic end generated code: output=d4ee34d0742d62ee input=ebe2881e5af4ffda]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500997{
998 if (!PyContextToken_CheckExact(token)) {
999 PyErr_Format(PyExc_TypeError,
1000 "expected an instance of Token, got %R", token);
1001 return NULL;
1002 }
1003
Yury Selivanov2ec872b2018-09-21 15:33:56 -04001004 if (PyContextVar_Reset((PyObject *)self, token)) {
Yury Selivanovf23746a2018-01-22 19:11:18 -05001005 return NULL;
1006 }
1007
1008 Py_RETURN_NONE;
1009}
1010
1011
1012static PyObject *
1013contextvar_cls_getitem(PyObject *self, PyObject *args)
1014{
1015 Py_RETURN_NONE;
1016}
1017
Yury Selivanov41cb0ba2018-06-28 13:20:29 -04001018static PyMemberDef PyContextVar_members[] = {
1019 {"name", T_OBJECT, offsetof(PyContextVar, var_name), READONLY},
1020 {NULL}
1021};
Yury Selivanovf23746a2018-01-22 19:11:18 -05001022
1023static PyMethodDef PyContextVar_methods[] = {
1024 _CONTEXTVARS_CONTEXTVAR_GET_METHODDEF
1025 _CONTEXTVARS_CONTEXTVAR_SET_METHODDEF
1026 _CONTEXTVARS_CONTEXTVAR_RESET_METHODDEF
1027 {"__class_getitem__", contextvar_cls_getitem,
1028 METH_VARARGS | METH_STATIC, NULL},
1029 {NULL, NULL}
1030};
1031
1032PyTypeObject PyContextVar_Type = {
1033 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1034 "ContextVar",
1035 sizeof(PyContextVar),
1036 .tp_methods = PyContextVar_methods,
Yury Selivanov41cb0ba2018-06-28 13:20:29 -04001037 .tp_members = PyContextVar_members,
Yury Selivanovf23746a2018-01-22 19:11:18 -05001038 .tp_dealloc = (destructor)contextvar_tp_dealloc,
1039 .tp_getattro = PyObject_GenericGetAttr,
1040 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1041 .tp_traverse = (traverseproc)contextvar_tp_traverse,
1042 .tp_clear = (inquiry)contextvar_tp_clear,
1043 .tp_new = contextvar_tp_new,
1044 .tp_free = PyObject_GC_Del,
1045 .tp_hash = (hashfunc)contextvar_tp_hash,
1046 .tp_repr = (reprfunc)contextvar_tp_repr,
1047};
1048
1049
1050/////////////////////////// Token
1051
1052static PyObject * get_token_missing(void);
1053
1054
1055/*[clinic input]
1056class _contextvars.Token "PyContextToken *" "&PyContextToken_Type"
1057[clinic start generated code]*/
1058/*[clinic end generated code: output=da39a3ee5e6b4b0d input=338a5e2db13d3f5b]*/
1059
1060
1061static PyObject *
1062token_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1063{
1064 PyErr_SetString(PyExc_RuntimeError,
1065 "Tokens can only be created by ContextVars");
1066 return NULL;
1067}
1068
1069static int
1070token_tp_clear(PyContextToken *self)
1071{
1072 Py_CLEAR(self->tok_ctx);
1073 Py_CLEAR(self->tok_var);
1074 Py_CLEAR(self->tok_oldval);
1075 return 0;
1076}
1077
1078static int
1079token_tp_traverse(PyContextToken *self, visitproc visit, void *arg)
1080{
1081 Py_VISIT(self->tok_ctx);
1082 Py_VISIT(self->tok_var);
1083 Py_VISIT(self->tok_oldval);
1084 return 0;
1085}
1086
1087static void
1088token_tp_dealloc(PyContextToken *self)
1089{
1090 PyObject_GC_UnTrack(self);
1091 (void)token_tp_clear(self);
1092 Py_TYPE(self)->tp_free(self);
1093}
1094
1095static PyObject *
1096token_tp_repr(PyContextToken *self)
1097{
1098 _PyUnicodeWriter writer;
1099
1100 _PyUnicodeWriter_Init(&writer);
1101
1102 if (_PyUnicodeWriter_WriteASCIIString(&writer, "<Token", 6) < 0) {
1103 goto error;
1104 }
1105
1106 if (self->tok_used) {
1107 if (_PyUnicodeWriter_WriteASCIIString(&writer, " used", 5) < 0) {
1108 goto error;
1109 }
1110 }
1111
1112 if (_PyUnicodeWriter_WriteASCIIString(&writer, " var=", 5) < 0) {
1113 goto error;
1114 }
1115
1116 PyObject *var = PyObject_Repr((PyObject *)self->tok_var);
1117 if (var == NULL) {
1118 goto error;
1119 }
1120 if (_PyUnicodeWriter_WriteStr(&writer, var) < 0) {
1121 Py_DECREF(var);
1122 goto error;
1123 }
1124 Py_DECREF(var);
1125
1126 PyObject *addr = PyUnicode_FromFormat(" at %p>", self);
1127 if (addr == NULL) {
1128 goto error;
1129 }
1130 if (_PyUnicodeWriter_WriteStr(&writer, addr) < 0) {
1131 Py_DECREF(addr);
1132 goto error;
1133 }
1134 Py_DECREF(addr);
1135
1136 return _PyUnicodeWriter_Finish(&writer);
1137
1138error:
1139 _PyUnicodeWriter_Dealloc(&writer);
1140 return NULL;
1141}
1142
1143static PyObject *
Serhiy Storchakad4f9cf52018-11-27 19:34:35 +02001144token_get_var(PyContextToken *self, void *Py_UNUSED(ignored))
Yury Selivanovf23746a2018-01-22 19:11:18 -05001145{
1146 Py_INCREF(self->tok_var);
1147 return (PyObject *)self->tok_var;
1148}
1149
1150static PyObject *
Serhiy Storchakad4f9cf52018-11-27 19:34:35 +02001151token_get_old_value(PyContextToken *self, void *Py_UNUSED(ignored))
Yury Selivanovf23746a2018-01-22 19:11:18 -05001152{
1153 if (self->tok_oldval == NULL) {
1154 return get_token_missing();
1155 }
1156
1157 Py_INCREF(self->tok_oldval);
1158 return self->tok_oldval;
1159}
1160
1161static PyGetSetDef PyContextTokenType_getsetlist[] = {
1162 {"var", (getter)token_get_var, NULL, NULL},
1163 {"old_value", (getter)token_get_old_value, NULL, NULL},
1164 {NULL}
1165};
1166
1167PyTypeObject PyContextToken_Type = {
1168 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1169 "Token",
1170 sizeof(PyContextToken),
1171 .tp_getset = PyContextTokenType_getsetlist,
1172 .tp_dealloc = (destructor)token_tp_dealloc,
1173 .tp_getattro = PyObject_GenericGetAttr,
1174 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1175 .tp_traverse = (traverseproc)token_tp_traverse,
1176 .tp_clear = (inquiry)token_tp_clear,
1177 .tp_new = token_tp_new,
1178 .tp_free = PyObject_GC_Del,
1179 .tp_hash = PyObject_HashNotImplemented,
1180 .tp_repr = (reprfunc)token_tp_repr,
1181};
1182
1183static PyContextToken *
1184token_new(PyContext *ctx, PyContextVar *var, PyObject *val)
1185{
1186 PyContextToken *tok = PyObject_GC_New(PyContextToken, &PyContextToken_Type);
1187 if (tok == NULL) {
1188 return NULL;
1189 }
1190
1191 Py_INCREF(ctx);
1192 tok->tok_ctx = ctx;
1193
1194 Py_INCREF(var);
1195 tok->tok_var = var;
1196
1197 Py_XINCREF(val);
1198 tok->tok_oldval = val;
1199
1200 tok->tok_used = 0;
1201
1202 PyObject_GC_Track(tok);
1203 return tok;
1204}
1205
1206
1207/////////////////////////// Token.MISSING
1208
1209
1210static PyObject *_token_missing;
1211
1212
1213typedef struct {
1214 PyObject_HEAD
1215} PyContextTokenMissing;
1216
1217
1218static PyObject *
1219context_token_missing_tp_repr(PyObject *self)
1220{
1221 return PyUnicode_FromString("<Token.MISSING>");
1222}
1223
1224
1225PyTypeObject PyContextTokenMissing_Type = {
1226 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1227 "Token.MISSING",
1228 sizeof(PyContextTokenMissing),
1229 .tp_getattro = PyObject_GenericGetAttr,
1230 .tp_flags = Py_TPFLAGS_DEFAULT,
1231 .tp_repr = context_token_missing_tp_repr,
1232};
1233
1234
1235static PyObject *
1236get_token_missing(void)
1237{
1238 if (_token_missing != NULL) {
1239 Py_INCREF(_token_missing);
1240 return _token_missing;
1241 }
1242
1243 _token_missing = (PyObject *)PyObject_New(
1244 PyContextTokenMissing, &PyContextTokenMissing_Type);
1245 if (_token_missing == NULL) {
1246 return NULL;
1247 }
1248
1249 Py_INCREF(_token_missing);
1250 return _token_missing;
1251}
1252
1253
1254///////////////////////////
1255
1256
1257int
1258PyContext_ClearFreeList(void)
1259{
Serhiy Storchakabfe4fd52018-02-09 17:31:26 +02001260 int size = ctx_freelist_len;
Yury Selivanovf23746a2018-01-22 19:11:18 -05001261 while (ctx_freelist_len) {
1262 PyContext *ctx = ctx_freelist;
1263 ctx_freelist = (PyContext *)ctx->ctx_weakreflist;
1264 ctx->ctx_weakreflist = NULL;
1265 PyObject_GC_Del(ctx);
1266 ctx_freelist_len--;
1267 }
1268 return size;
1269}
1270
1271
1272void
1273_PyContext_Fini(void)
1274{
1275 Py_CLEAR(_token_missing);
1276 (void)PyContext_ClearFreeList();
1277 (void)_PyHamt_Fini();
1278}
1279
1280
1281int
1282_PyContext_Init(void)
1283{
1284 if (!_PyHamt_Init()) {
1285 return 0;
1286 }
1287
1288 if ((PyType_Ready(&PyContext_Type) < 0) ||
1289 (PyType_Ready(&PyContextVar_Type) < 0) ||
1290 (PyType_Ready(&PyContextToken_Type) < 0) ||
1291 (PyType_Ready(&PyContextTokenMissing_Type) < 0))
1292 {
1293 return 0;
1294 }
1295
1296 PyObject *missing = get_token_missing();
1297 if (PyDict_SetItemString(
1298 PyContextToken_Type.tp_dict, "MISSING", missing))
1299 {
1300 Py_DECREF(missing);
1301 return 0;
1302 }
1303 Py_DECREF(missing);
1304
1305 return 1;
1306}