blob: 5c30e47f35dd7896f099711cffac547045237800 [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 *
Miss Islington (bot)960fca12019-12-08 04:49:07 -08001013contextvar_cls_getitem(PyObject *self, PyObject *arg)
Yury Selivanovf23746a2018-01-22 19:11:18 -05001014{
Miss Islington (bot)960fca12019-12-08 04:49:07 -08001015 Py_INCREF(self);
1016 return self;
Yury Selivanovf23746a2018-01-22 19:11:18 -05001017}
1018
Yury Selivanov41cb0ba2018-06-28 13:20:29 -04001019static PyMemberDef PyContextVar_members[] = {
1020 {"name", T_OBJECT, offsetof(PyContextVar, var_name), READONLY},
1021 {NULL}
1022};
Yury Selivanovf23746a2018-01-22 19:11:18 -05001023
1024static PyMethodDef PyContextVar_methods[] = {
1025 _CONTEXTVARS_CONTEXTVAR_GET_METHODDEF
1026 _CONTEXTVARS_CONTEXTVAR_SET_METHODDEF
1027 _CONTEXTVARS_CONTEXTVAR_RESET_METHODDEF
1028 {"__class_getitem__", contextvar_cls_getitem,
Miss Islington (bot)960fca12019-12-08 04:49:07 -08001029 METH_O | METH_CLASS, NULL},
Yury Selivanovf23746a2018-01-22 19:11:18 -05001030 {NULL, NULL}
1031};
1032
1033PyTypeObject PyContextVar_Type = {
1034 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1035 "ContextVar",
1036 sizeof(PyContextVar),
1037 .tp_methods = PyContextVar_methods,
Yury Selivanov41cb0ba2018-06-28 13:20:29 -04001038 .tp_members = PyContextVar_members,
Yury Selivanovf23746a2018-01-22 19:11:18 -05001039 .tp_dealloc = (destructor)contextvar_tp_dealloc,
1040 .tp_getattro = PyObject_GenericGetAttr,
1041 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1042 .tp_traverse = (traverseproc)contextvar_tp_traverse,
1043 .tp_clear = (inquiry)contextvar_tp_clear,
1044 .tp_new = contextvar_tp_new,
1045 .tp_free = PyObject_GC_Del,
1046 .tp_hash = (hashfunc)contextvar_tp_hash,
1047 .tp_repr = (reprfunc)contextvar_tp_repr,
1048};
1049
1050
1051/////////////////////////// Token
1052
1053static PyObject * get_token_missing(void);
1054
1055
1056/*[clinic input]
1057class _contextvars.Token "PyContextToken *" "&PyContextToken_Type"
1058[clinic start generated code]*/
1059/*[clinic end generated code: output=da39a3ee5e6b4b0d input=338a5e2db13d3f5b]*/
1060
1061
1062static PyObject *
1063token_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1064{
1065 PyErr_SetString(PyExc_RuntimeError,
1066 "Tokens can only be created by ContextVars");
1067 return NULL;
1068}
1069
1070static int
1071token_tp_clear(PyContextToken *self)
1072{
1073 Py_CLEAR(self->tok_ctx);
1074 Py_CLEAR(self->tok_var);
1075 Py_CLEAR(self->tok_oldval);
1076 return 0;
1077}
1078
1079static int
1080token_tp_traverse(PyContextToken *self, visitproc visit, void *arg)
1081{
1082 Py_VISIT(self->tok_ctx);
1083 Py_VISIT(self->tok_var);
1084 Py_VISIT(self->tok_oldval);
1085 return 0;
1086}
1087
1088static void
1089token_tp_dealloc(PyContextToken *self)
1090{
1091 PyObject_GC_UnTrack(self);
1092 (void)token_tp_clear(self);
1093 Py_TYPE(self)->tp_free(self);
1094}
1095
1096static PyObject *
1097token_tp_repr(PyContextToken *self)
1098{
1099 _PyUnicodeWriter writer;
1100
1101 _PyUnicodeWriter_Init(&writer);
1102
1103 if (_PyUnicodeWriter_WriteASCIIString(&writer, "<Token", 6) < 0) {
1104 goto error;
1105 }
1106
1107 if (self->tok_used) {
1108 if (_PyUnicodeWriter_WriteASCIIString(&writer, " used", 5) < 0) {
1109 goto error;
1110 }
1111 }
1112
1113 if (_PyUnicodeWriter_WriteASCIIString(&writer, " var=", 5) < 0) {
1114 goto error;
1115 }
1116
1117 PyObject *var = PyObject_Repr((PyObject *)self->tok_var);
1118 if (var == NULL) {
1119 goto error;
1120 }
1121 if (_PyUnicodeWriter_WriteStr(&writer, var) < 0) {
1122 Py_DECREF(var);
1123 goto error;
1124 }
1125 Py_DECREF(var);
1126
1127 PyObject *addr = PyUnicode_FromFormat(" at %p>", self);
1128 if (addr == NULL) {
1129 goto error;
1130 }
1131 if (_PyUnicodeWriter_WriteStr(&writer, addr) < 0) {
1132 Py_DECREF(addr);
1133 goto error;
1134 }
1135 Py_DECREF(addr);
1136
1137 return _PyUnicodeWriter_Finish(&writer);
1138
1139error:
1140 _PyUnicodeWriter_Dealloc(&writer);
1141 return NULL;
1142}
1143
1144static PyObject *
Serhiy Storchakad4f9cf52018-11-27 19:34:35 +02001145token_get_var(PyContextToken *self, void *Py_UNUSED(ignored))
Yury Selivanovf23746a2018-01-22 19:11:18 -05001146{
1147 Py_INCREF(self->tok_var);
1148 return (PyObject *)self->tok_var;
1149}
1150
1151static PyObject *
Serhiy Storchakad4f9cf52018-11-27 19:34:35 +02001152token_get_old_value(PyContextToken *self, void *Py_UNUSED(ignored))
Yury Selivanovf23746a2018-01-22 19:11:18 -05001153{
1154 if (self->tok_oldval == NULL) {
1155 return get_token_missing();
1156 }
1157
1158 Py_INCREF(self->tok_oldval);
1159 return self->tok_oldval;
1160}
1161
1162static PyGetSetDef PyContextTokenType_getsetlist[] = {
1163 {"var", (getter)token_get_var, NULL, NULL},
1164 {"old_value", (getter)token_get_old_value, NULL, NULL},
1165 {NULL}
1166};
1167
1168PyTypeObject PyContextToken_Type = {
1169 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1170 "Token",
1171 sizeof(PyContextToken),
1172 .tp_getset = PyContextTokenType_getsetlist,
1173 .tp_dealloc = (destructor)token_tp_dealloc,
1174 .tp_getattro = PyObject_GenericGetAttr,
1175 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1176 .tp_traverse = (traverseproc)token_tp_traverse,
1177 .tp_clear = (inquiry)token_tp_clear,
1178 .tp_new = token_tp_new,
1179 .tp_free = PyObject_GC_Del,
1180 .tp_hash = PyObject_HashNotImplemented,
1181 .tp_repr = (reprfunc)token_tp_repr,
1182};
1183
1184static PyContextToken *
1185token_new(PyContext *ctx, PyContextVar *var, PyObject *val)
1186{
1187 PyContextToken *tok = PyObject_GC_New(PyContextToken, &PyContextToken_Type);
1188 if (tok == NULL) {
1189 return NULL;
1190 }
1191
1192 Py_INCREF(ctx);
1193 tok->tok_ctx = ctx;
1194
1195 Py_INCREF(var);
1196 tok->tok_var = var;
1197
1198 Py_XINCREF(val);
1199 tok->tok_oldval = val;
1200
1201 tok->tok_used = 0;
1202
1203 PyObject_GC_Track(tok);
1204 return tok;
1205}
1206
1207
1208/////////////////////////// Token.MISSING
1209
1210
1211static PyObject *_token_missing;
1212
1213
1214typedef struct {
1215 PyObject_HEAD
1216} PyContextTokenMissing;
1217
1218
1219static PyObject *
1220context_token_missing_tp_repr(PyObject *self)
1221{
1222 return PyUnicode_FromString("<Token.MISSING>");
1223}
1224
1225
1226PyTypeObject PyContextTokenMissing_Type = {
1227 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1228 "Token.MISSING",
1229 sizeof(PyContextTokenMissing),
1230 .tp_getattro = PyObject_GenericGetAttr,
1231 .tp_flags = Py_TPFLAGS_DEFAULT,
1232 .tp_repr = context_token_missing_tp_repr,
1233};
1234
1235
1236static PyObject *
1237get_token_missing(void)
1238{
1239 if (_token_missing != NULL) {
1240 Py_INCREF(_token_missing);
1241 return _token_missing;
1242 }
1243
1244 _token_missing = (PyObject *)PyObject_New(
1245 PyContextTokenMissing, &PyContextTokenMissing_Type);
1246 if (_token_missing == NULL) {
1247 return NULL;
1248 }
1249
1250 Py_INCREF(_token_missing);
1251 return _token_missing;
1252}
1253
1254
1255///////////////////////////
1256
1257
1258int
1259PyContext_ClearFreeList(void)
1260{
Serhiy Storchakabfe4fd52018-02-09 17:31:26 +02001261 int size = ctx_freelist_len;
Yury Selivanovf23746a2018-01-22 19:11:18 -05001262 while (ctx_freelist_len) {
1263 PyContext *ctx = ctx_freelist;
1264 ctx_freelist = (PyContext *)ctx->ctx_weakreflist;
1265 ctx->ctx_weakreflist = NULL;
1266 PyObject_GC_Del(ctx);
1267 ctx_freelist_len--;
1268 }
1269 return size;
1270}
1271
1272
1273void
1274_PyContext_Fini(void)
1275{
1276 Py_CLEAR(_token_missing);
1277 (void)PyContext_ClearFreeList();
1278 (void)_PyHamt_Fini();
1279}
1280
1281
1282int
1283_PyContext_Init(void)
1284{
1285 if (!_PyHamt_Init()) {
1286 return 0;
1287 }
1288
1289 if ((PyType_Ready(&PyContext_Type) < 0) ||
1290 (PyType_Ready(&PyContextVar_Type) < 0) ||
1291 (PyType_Ready(&PyContextToken_Type) < 0) ||
1292 (PyType_Ready(&PyContextTokenMissing_Type) < 0))
1293 {
1294 return 0;
1295 }
1296
1297 PyObject *missing = get_token_missing();
1298 if (PyDict_SetItemString(
1299 PyContextToken_Type.tp_dict, "MISSING", missing))
1300 {
1301 Py_DECREF(missing);
1302 return 0;
1303 }
1304 Py_DECREF(missing);
1305
1306 return 1;
1307}