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