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