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