blob: b727748ee73987b001ca61bb359e6610610f7bb5 [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 /
492[clinic start generated code]*/
493
494static PyObject *
495_contextvars_Context_get_impl(PyContext *self, PyObject *key,
496 PyObject *default_value)
497/*[clinic end generated code: output=0c54aa7664268189 input=8d4c33c8ecd6d769]*/
498{
499 if (context_check_key_type(key)) {
500 return NULL;
501 }
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 Py_INCREF(default_value);
510 return default_value;
511 }
512 Py_INCREF(val);
513 return val;
514}
515
516
517/*[clinic input]
518_contextvars.Context.items
519[clinic start generated code]*/
520
521static PyObject *
522_contextvars_Context_items_impl(PyContext *self)
523/*[clinic end generated code: output=fa1655c8a08502af input=2d570d1455004979]*/
524{
525 return _PyHamt_NewIterItems(self->ctx_vars);
526}
527
528
529/*[clinic input]
530_contextvars.Context.keys
531[clinic start generated code]*/
532
533static PyObject *
534_contextvars_Context_keys_impl(PyContext *self)
535/*[clinic end generated code: output=177227c6b63ec0e2 input=13005e142fbbf37d]*/
536{
537 return _PyHamt_NewIterKeys(self->ctx_vars);
538}
539
540
541/*[clinic input]
542_contextvars.Context.values
543[clinic start generated code]*/
544
545static PyObject *
546_contextvars_Context_values_impl(PyContext *self)
547/*[clinic end generated code: output=d286dabfc8db6dde input=c2cbc40a4470e905]*/
548{
549 return _PyHamt_NewIterValues(self->ctx_vars);
550}
551
552
553/*[clinic input]
554_contextvars.Context.copy
555[clinic start generated code]*/
556
557static PyObject *
558_contextvars_Context_copy_impl(PyContext *self)
559/*[clinic end generated code: output=30ba8896c4707a15 input=3e3fd72d598653ab]*/
560{
561 return (PyObject *)context_new_from_vars(self->ctx_vars);
562}
563
564
565static PyObject *
566context_run(PyContext *self, PyObject *const *args,
567 Py_ssize_t nargs, PyObject *kwnames)
568{
569 if (nargs < 1) {
570 PyErr_SetString(PyExc_TypeError,
571 "run() missing 1 required positional argument");
572 return NULL;
573 }
574
575 if (PyContext_Enter(self)) {
576 return NULL;
577 }
578
579 PyObject *call_result = _PyObject_FastCallKeywords(
580 args[0], args + 1, nargs - 1, kwnames);
581
582 if (PyContext_Exit(self)) {
583 return NULL;
584 }
585
586 return call_result;
587}
588
589
590static PyMethodDef PyContext_methods[] = {
591 _CONTEXTVARS_CONTEXT_GET_METHODDEF
592 _CONTEXTVARS_CONTEXT_ITEMS_METHODDEF
593 _CONTEXTVARS_CONTEXT_KEYS_METHODDEF
594 _CONTEXTVARS_CONTEXT_VALUES_METHODDEF
595 _CONTEXTVARS_CONTEXT_COPY_METHODDEF
596 {"run", (PyCFunction)context_run, METH_FASTCALL | METH_KEYWORDS, NULL},
597 {NULL, NULL}
598};
599
600static PySequenceMethods PyContext_as_sequence = {
601 0, /* sq_length */
602 0, /* sq_concat */
603 0, /* sq_repeat */
604 0, /* sq_item */
605 0, /* sq_slice */
606 0, /* sq_ass_item */
607 0, /* sq_ass_slice */
608 (objobjproc)context_tp_contains, /* sq_contains */
609 0, /* sq_inplace_concat */
610 0, /* sq_inplace_repeat */
611};
612
613static PyMappingMethods PyContext_as_mapping = {
614 (lenfunc)context_tp_len, /* mp_length */
615 (binaryfunc)context_tp_subscript, /* mp_subscript */
616};
617
618PyTypeObject PyContext_Type = {
619 PyVarObject_HEAD_INIT(&PyType_Type, 0)
620 "Context",
621 sizeof(PyContext),
622 .tp_methods = PyContext_methods,
623 .tp_as_mapping = &PyContext_as_mapping,
624 .tp_as_sequence = &PyContext_as_sequence,
625 .tp_iter = (getiterfunc)context_tp_iter,
626 .tp_dealloc = (destructor)context_tp_dealloc,
627 .tp_getattro = PyObject_GenericGetAttr,
628 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
629 .tp_richcompare = context_tp_richcompare,
630 .tp_traverse = (traverseproc)context_tp_traverse,
631 .tp_clear = (inquiry)context_tp_clear,
632 .tp_new = context_tp_new,
633 .tp_weaklistoffset = offsetof(PyContext, ctx_weakreflist),
634 .tp_hash = PyObject_HashNotImplemented,
635};
636
637
638/////////////////////////// ContextVar
639
640
641static int
642contextvar_set(PyContextVar *var, PyObject *val)
643{
644 var->var_cached = NULL;
645 PyThreadState *ts = PyThreadState_Get();
646
647 PyContext *ctx = context_get();
648 if (ctx == NULL) {
649 return -1;
650 }
651
652 PyHamtObject *new_vars = _PyHamt_Assoc(
653 ctx->ctx_vars, (PyObject *)var, val);
654 if (new_vars == NULL) {
655 return -1;
656 }
657
658 Py_SETREF(ctx->ctx_vars, new_vars);
659
660 var->var_cached = val; /* borrow */
661 var->var_cached_tsid = ts->id;
662 var->var_cached_tsver = ts->context_ver;
663 return 0;
664}
665
666static int
667contextvar_del(PyContextVar *var)
668{
669 var->var_cached = NULL;
670
671 PyContext *ctx = context_get();
672 if (ctx == NULL) {
673 return -1;
674 }
675
676 PyHamtObject *vars = ctx->ctx_vars;
677 PyHamtObject *new_vars = _PyHamt_Without(vars, (PyObject *)var);
678 if (new_vars == NULL) {
679 return -1;
680 }
681
682 if (vars == new_vars) {
683 Py_DECREF(new_vars);
684 PyErr_SetObject(PyExc_LookupError, (PyObject *)var);
685 return -1;
686 }
687
688 Py_SETREF(ctx->ctx_vars, new_vars);
689 return 0;
690}
691
692static Py_hash_t
693contextvar_generate_hash(void *addr, PyObject *name)
694{
695 /* Take hash of `name` and XOR it with the object's addr.
696
697 The structure of the tree is encoded in objects' hashes, which
698 means that sufficiently similar hashes would result in tall trees
699 with many Collision nodes. Which would, in turn, result in slower
700 get and set operations.
701
702 The XORing helps to ensure that:
703
704 (1) sequentially allocated ContextVar objects have
705 different hashes;
706
707 (2) context variables with equal names have
708 different hashes.
709 */
710
711 Py_hash_t name_hash = PyObject_Hash(name);
712 if (name_hash == -1) {
713 return -1;
714 }
715
716 Py_hash_t res = _Py_HashPointer(addr) ^ name_hash;
717 return res == -1 ? -2 : res;
718}
719
720static PyContextVar *
721contextvar_new(PyObject *name, PyObject *def)
722{
723 if (!PyUnicode_Check(name)) {
724 PyErr_SetString(PyExc_TypeError,
725 "context variable name must be a str");
726 return NULL;
727 }
728
729 PyContextVar *var = PyObject_GC_New(PyContextVar, &PyContextVar_Type);
730 if (var == NULL) {
731 return NULL;
732 }
733
734 var->var_hash = contextvar_generate_hash(var, name);
735 if (var->var_hash == -1) {
736 Py_DECREF(var);
737 return NULL;
738 }
739
740 Py_INCREF(name);
741 var->var_name = name;
742
743 Py_XINCREF(def);
744 var->var_default = def;
745
746 var->var_cached = NULL;
747 var->var_cached_tsid = 0;
748 var->var_cached_tsver = 0;
749
Yury Selivanov6ab62922018-01-25 14:18:55 -0500750 if (_PyObject_GC_MAY_BE_TRACKED(name) ||
751 (def != NULL && _PyObject_GC_MAY_BE_TRACKED(def)))
Yury Selivanovf23746a2018-01-22 19:11:18 -0500752 {
753 PyObject_GC_Track(var);
754 }
755 return var;
756}
757
758
759/*[clinic input]
760class _contextvars.ContextVar "PyContextVar *" "&PyContextVar_Type"
761[clinic start generated code]*/
762/*[clinic end generated code: output=da39a3ee5e6b4b0d input=445da935fa8883c3]*/
763
764
765static PyObject *
766contextvar_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
767{
768 static char *kwlist[] = {"", "default", NULL};
769 PyObject *name;
770 PyObject *def = NULL;
771
772 if (!PyArg_ParseTupleAndKeywords(
773 args, kwds, "O|$O:ContextVar", kwlist, &name, &def))
774 {
775 return NULL;
776 }
777
778 return (PyObject *)contextvar_new(name, def);
779}
780
781static int
782contextvar_tp_clear(PyContextVar *self)
783{
784 Py_CLEAR(self->var_name);
785 Py_CLEAR(self->var_default);
786 self->var_cached = NULL;
787 self->var_cached_tsid = 0;
788 self->var_cached_tsver = 0;
789 return 0;
790}
791
792static int
793contextvar_tp_traverse(PyContextVar *self, visitproc visit, void *arg)
794{
795 Py_VISIT(self->var_name);
796 Py_VISIT(self->var_default);
797 return 0;
798}
799
800static void
801contextvar_tp_dealloc(PyContextVar *self)
802{
803 PyObject_GC_UnTrack(self);
804 (void)contextvar_tp_clear(self);
805 Py_TYPE(self)->tp_free(self);
806}
807
808static Py_hash_t
809contextvar_tp_hash(PyContextVar *self)
810{
811 return self->var_hash;
812}
813
814static PyObject *
815contextvar_tp_repr(PyContextVar *self)
816{
817 _PyUnicodeWriter writer;
818
819 _PyUnicodeWriter_Init(&writer);
820
821 if (_PyUnicodeWriter_WriteASCIIString(
822 &writer, "<ContextVar name=", 17) < 0)
823 {
824 goto error;
825 }
826
827 PyObject *name = PyObject_Repr(self->var_name);
828 if (name == NULL) {
829 goto error;
830 }
831 if (_PyUnicodeWriter_WriteStr(&writer, name) < 0) {
832 Py_DECREF(name);
833 goto error;
834 }
835 Py_DECREF(name);
836
837 if (self->var_default != NULL) {
838 if (_PyUnicodeWriter_WriteASCIIString(&writer, " default=", 9) < 0) {
839 goto error;
840 }
841
842 PyObject *def = PyObject_Repr(self->var_default);
843 if (def == NULL) {
844 goto error;
845 }
846 if (_PyUnicodeWriter_WriteStr(&writer, def) < 0) {
847 Py_DECREF(def);
848 goto error;
849 }
850 Py_DECREF(def);
851 }
852
853 PyObject *addr = PyUnicode_FromFormat(" at %p>", self);
854 if (addr == NULL) {
855 goto error;
856 }
857 if (_PyUnicodeWriter_WriteStr(&writer, addr) < 0) {
858 Py_DECREF(addr);
859 goto error;
860 }
861 Py_DECREF(addr);
862
863 return _PyUnicodeWriter_Finish(&writer);
864
865error:
866 _PyUnicodeWriter_Dealloc(&writer);
867 return NULL;
868}
869
870
871/*[clinic input]
872_contextvars.ContextVar.get
873 default: object = NULL
874 /
875[clinic start generated code]*/
876
877static PyObject *
878_contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value)
879/*[clinic end generated code: output=0746bd0aa2ced7bf input=8d002b02eebbb247]*/
880{
881 if (!PyContextVar_CheckExact(self)) {
882 PyErr_SetString(
883 PyExc_TypeError, "an instance of ContextVar was expected");
884 return NULL;
885 }
886
887 PyObject *val;
888 if (PyContextVar_Get(self, default_value, &val) < 0) {
889 return NULL;
890 }
891
892 if (val == NULL) {
893 PyErr_SetObject(PyExc_LookupError, (PyObject *)self);
894 return NULL;
895 }
896
897 return val;
898}
899
900/*[clinic input]
901_contextvars.ContextVar.set
902 value: object
903 /
904[clinic start generated code]*/
905
906static PyObject *
907_contextvars_ContextVar_set(PyContextVar *self, PyObject *value)
908/*[clinic end generated code: output=446ed5e820d6d60b input=a2d88f57c6d86f7c]*/
909{
910 return (PyObject *)PyContextVar_Set(self, value);
911}
912
913/*[clinic input]
914_contextvars.ContextVar.reset
915 token: object
916 /
917[clinic start generated code]*/
918
919static PyObject *
920_contextvars_ContextVar_reset(PyContextVar *self, PyObject *token)
921/*[clinic end generated code: output=d4ee34d0742d62ee input=4c871b6f1f31a65f]*/
922{
923 if (!PyContextToken_CheckExact(token)) {
924 PyErr_Format(PyExc_TypeError,
925 "expected an instance of Token, got %R", token);
926 return NULL;
927 }
928
929 if (PyContextVar_Reset(self, (PyContextToken *)token)) {
930 return NULL;
931 }
932
933 Py_RETURN_NONE;
934}
935
936
937static PyObject *
938contextvar_cls_getitem(PyObject *self, PyObject *args)
939{
940 Py_RETURN_NONE;
941}
942
943
944static PyMethodDef PyContextVar_methods[] = {
945 _CONTEXTVARS_CONTEXTVAR_GET_METHODDEF
946 _CONTEXTVARS_CONTEXTVAR_SET_METHODDEF
947 _CONTEXTVARS_CONTEXTVAR_RESET_METHODDEF
948 {"__class_getitem__", contextvar_cls_getitem,
949 METH_VARARGS | METH_STATIC, NULL},
950 {NULL, NULL}
951};
952
953PyTypeObject PyContextVar_Type = {
954 PyVarObject_HEAD_INIT(&PyType_Type, 0)
955 "ContextVar",
956 sizeof(PyContextVar),
957 .tp_methods = PyContextVar_methods,
958 .tp_dealloc = (destructor)contextvar_tp_dealloc,
959 .tp_getattro = PyObject_GenericGetAttr,
960 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
961 .tp_traverse = (traverseproc)contextvar_tp_traverse,
962 .tp_clear = (inquiry)contextvar_tp_clear,
963 .tp_new = contextvar_tp_new,
964 .tp_free = PyObject_GC_Del,
965 .tp_hash = (hashfunc)contextvar_tp_hash,
966 .tp_repr = (reprfunc)contextvar_tp_repr,
967};
968
969
970/////////////////////////// Token
971
972static PyObject * get_token_missing(void);
973
974
975/*[clinic input]
976class _contextvars.Token "PyContextToken *" "&PyContextToken_Type"
977[clinic start generated code]*/
978/*[clinic end generated code: output=da39a3ee5e6b4b0d input=338a5e2db13d3f5b]*/
979
980
981static PyObject *
982token_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
983{
984 PyErr_SetString(PyExc_RuntimeError,
985 "Tokens can only be created by ContextVars");
986 return NULL;
987}
988
989static int
990token_tp_clear(PyContextToken *self)
991{
992 Py_CLEAR(self->tok_ctx);
993 Py_CLEAR(self->tok_var);
994 Py_CLEAR(self->tok_oldval);
995 return 0;
996}
997
998static int
999token_tp_traverse(PyContextToken *self, visitproc visit, void *arg)
1000{
1001 Py_VISIT(self->tok_ctx);
1002 Py_VISIT(self->tok_var);
1003 Py_VISIT(self->tok_oldval);
1004 return 0;
1005}
1006
1007static void
1008token_tp_dealloc(PyContextToken *self)
1009{
1010 PyObject_GC_UnTrack(self);
1011 (void)token_tp_clear(self);
1012 Py_TYPE(self)->tp_free(self);
1013}
1014
1015static PyObject *
1016token_tp_repr(PyContextToken *self)
1017{
1018 _PyUnicodeWriter writer;
1019
1020 _PyUnicodeWriter_Init(&writer);
1021
1022 if (_PyUnicodeWriter_WriteASCIIString(&writer, "<Token", 6) < 0) {
1023 goto error;
1024 }
1025
1026 if (self->tok_used) {
1027 if (_PyUnicodeWriter_WriteASCIIString(&writer, " used", 5) < 0) {
1028 goto error;
1029 }
1030 }
1031
1032 if (_PyUnicodeWriter_WriteASCIIString(&writer, " var=", 5) < 0) {
1033 goto error;
1034 }
1035
1036 PyObject *var = PyObject_Repr((PyObject *)self->tok_var);
1037 if (var == NULL) {
1038 goto error;
1039 }
1040 if (_PyUnicodeWriter_WriteStr(&writer, var) < 0) {
1041 Py_DECREF(var);
1042 goto error;
1043 }
1044 Py_DECREF(var);
1045
1046 PyObject *addr = PyUnicode_FromFormat(" at %p>", self);
1047 if (addr == NULL) {
1048 goto error;
1049 }
1050 if (_PyUnicodeWriter_WriteStr(&writer, addr) < 0) {
1051 Py_DECREF(addr);
1052 goto error;
1053 }
1054 Py_DECREF(addr);
1055
1056 return _PyUnicodeWriter_Finish(&writer);
1057
1058error:
1059 _PyUnicodeWriter_Dealloc(&writer);
1060 return NULL;
1061}
1062
1063static PyObject *
1064token_get_var(PyContextToken *self)
1065{
1066 Py_INCREF(self->tok_var);
1067 return (PyObject *)self->tok_var;
1068}
1069
1070static PyObject *
1071token_get_old_value(PyContextToken *self)
1072{
1073 if (self->tok_oldval == NULL) {
1074 return get_token_missing();
1075 }
1076
1077 Py_INCREF(self->tok_oldval);
1078 return self->tok_oldval;
1079}
1080
1081static PyGetSetDef PyContextTokenType_getsetlist[] = {
1082 {"var", (getter)token_get_var, NULL, NULL},
1083 {"old_value", (getter)token_get_old_value, NULL, NULL},
1084 {NULL}
1085};
1086
1087PyTypeObject PyContextToken_Type = {
1088 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1089 "Token",
1090 sizeof(PyContextToken),
1091 .tp_getset = PyContextTokenType_getsetlist,
1092 .tp_dealloc = (destructor)token_tp_dealloc,
1093 .tp_getattro = PyObject_GenericGetAttr,
1094 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1095 .tp_traverse = (traverseproc)token_tp_traverse,
1096 .tp_clear = (inquiry)token_tp_clear,
1097 .tp_new = token_tp_new,
1098 .tp_free = PyObject_GC_Del,
1099 .tp_hash = PyObject_HashNotImplemented,
1100 .tp_repr = (reprfunc)token_tp_repr,
1101};
1102
1103static PyContextToken *
1104token_new(PyContext *ctx, PyContextVar *var, PyObject *val)
1105{
1106 PyContextToken *tok = PyObject_GC_New(PyContextToken, &PyContextToken_Type);
1107 if (tok == NULL) {
1108 return NULL;
1109 }
1110
1111 Py_INCREF(ctx);
1112 tok->tok_ctx = ctx;
1113
1114 Py_INCREF(var);
1115 tok->tok_var = var;
1116
1117 Py_XINCREF(val);
1118 tok->tok_oldval = val;
1119
1120 tok->tok_used = 0;
1121
1122 PyObject_GC_Track(tok);
1123 return tok;
1124}
1125
1126
1127/////////////////////////// Token.MISSING
1128
1129
1130static PyObject *_token_missing;
1131
1132
1133typedef struct {
1134 PyObject_HEAD
1135} PyContextTokenMissing;
1136
1137
1138static PyObject *
1139context_token_missing_tp_repr(PyObject *self)
1140{
1141 return PyUnicode_FromString("<Token.MISSING>");
1142}
1143
1144
1145PyTypeObject PyContextTokenMissing_Type = {
1146 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1147 "Token.MISSING",
1148 sizeof(PyContextTokenMissing),
1149 .tp_getattro = PyObject_GenericGetAttr,
1150 .tp_flags = Py_TPFLAGS_DEFAULT,
1151 .tp_repr = context_token_missing_tp_repr,
1152};
1153
1154
1155static PyObject *
1156get_token_missing(void)
1157{
1158 if (_token_missing != NULL) {
1159 Py_INCREF(_token_missing);
1160 return _token_missing;
1161 }
1162
1163 _token_missing = (PyObject *)PyObject_New(
1164 PyContextTokenMissing, &PyContextTokenMissing_Type);
1165 if (_token_missing == NULL) {
1166 return NULL;
1167 }
1168
1169 Py_INCREF(_token_missing);
1170 return _token_missing;
1171}
1172
1173
1174///////////////////////////
1175
1176
1177int
1178PyContext_ClearFreeList(void)
1179{
Serhiy Storchakabfe4fd52018-02-09 17:31:26 +02001180 int size = ctx_freelist_len;
Yury Selivanovf23746a2018-01-22 19:11:18 -05001181 while (ctx_freelist_len) {
1182 PyContext *ctx = ctx_freelist;
1183 ctx_freelist = (PyContext *)ctx->ctx_weakreflist;
1184 ctx->ctx_weakreflist = NULL;
1185 PyObject_GC_Del(ctx);
1186 ctx_freelist_len--;
1187 }
1188 return size;
1189}
1190
1191
1192void
1193_PyContext_Fini(void)
1194{
1195 Py_CLEAR(_token_missing);
1196 (void)PyContext_ClearFreeList();
1197 (void)_PyHamt_Fini();
1198}
1199
1200
1201int
1202_PyContext_Init(void)
1203{
1204 if (!_PyHamt_Init()) {
1205 return 0;
1206 }
1207
1208 if ((PyType_Ready(&PyContext_Type) < 0) ||
1209 (PyType_Ready(&PyContextVar_Type) < 0) ||
1210 (PyType_Ready(&PyContextToken_Type) < 0) ||
1211 (PyType_Ready(&PyContextTokenMissing_Type) < 0))
1212 {
1213 return 0;
1214 }
1215
1216 PyObject *missing = get_token_missing();
1217 if (PyDict_SetItemString(
1218 PyContextToken_Type.tp_dict, "MISSING", missing))
1219 {
1220 Py_DECREF(missing);
1221 return 0;
1222 }
1223 Py_DECREF(missing);
1224
1225 return 1;
1226}