blob: 8ee048ccdd5c5949be2332bad5255cde1c26ee1c [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;
Miss Islington (bot)7df80492018-02-09 07:56:34 -080011static 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
Miss Islington (bot)4c20d2b2018-06-28 10:39:54 -0700943static PyMemberDef PyContextVar_members[] = {
944 {"name", T_OBJECT, offsetof(PyContextVar, var_name), READONLY},
945 {NULL}
946};
Yury Selivanovf23746a2018-01-22 19:11:18 -0500947
948static PyMethodDef PyContextVar_methods[] = {
949 _CONTEXTVARS_CONTEXTVAR_GET_METHODDEF
950 _CONTEXTVARS_CONTEXTVAR_SET_METHODDEF
951 _CONTEXTVARS_CONTEXTVAR_RESET_METHODDEF
952 {"__class_getitem__", contextvar_cls_getitem,
953 METH_VARARGS | METH_STATIC, NULL},
954 {NULL, NULL}
955};
956
957PyTypeObject PyContextVar_Type = {
958 PyVarObject_HEAD_INIT(&PyType_Type, 0)
959 "ContextVar",
960 sizeof(PyContextVar),
961 .tp_methods = PyContextVar_methods,
Miss Islington (bot)4c20d2b2018-06-28 10:39:54 -0700962 .tp_members = PyContextVar_members,
Yury Selivanovf23746a2018-01-22 19:11:18 -0500963 .tp_dealloc = (destructor)contextvar_tp_dealloc,
964 .tp_getattro = PyObject_GenericGetAttr,
965 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
966 .tp_traverse = (traverseproc)contextvar_tp_traverse,
967 .tp_clear = (inquiry)contextvar_tp_clear,
968 .tp_new = contextvar_tp_new,
969 .tp_free = PyObject_GC_Del,
970 .tp_hash = (hashfunc)contextvar_tp_hash,
971 .tp_repr = (reprfunc)contextvar_tp_repr,
972};
973
974
975/////////////////////////// Token
976
977static PyObject * get_token_missing(void);
978
979
980/*[clinic input]
981class _contextvars.Token "PyContextToken *" "&PyContextToken_Type"
982[clinic start generated code]*/
983/*[clinic end generated code: output=da39a3ee5e6b4b0d input=338a5e2db13d3f5b]*/
984
985
986static PyObject *
987token_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
988{
989 PyErr_SetString(PyExc_RuntimeError,
990 "Tokens can only be created by ContextVars");
991 return NULL;
992}
993
994static int
995token_tp_clear(PyContextToken *self)
996{
997 Py_CLEAR(self->tok_ctx);
998 Py_CLEAR(self->tok_var);
999 Py_CLEAR(self->tok_oldval);
1000 return 0;
1001}
1002
1003static int
1004token_tp_traverse(PyContextToken *self, visitproc visit, void *arg)
1005{
1006 Py_VISIT(self->tok_ctx);
1007 Py_VISIT(self->tok_var);
1008 Py_VISIT(self->tok_oldval);
1009 return 0;
1010}
1011
1012static void
1013token_tp_dealloc(PyContextToken *self)
1014{
1015 PyObject_GC_UnTrack(self);
1016 (void)token_tp_clear(self);
1017 Py_TYPE(self)->tp_free(self);
1018}
1019
1020static PyObject *
1021token_tp_repr(PyContextToken *self)
1022{
1023 _PyUnicodeWriter writer;
1024
1025 _PyUnicodeWriter_Init(&writer);
1026
1027 if (_PyUnicodeWriter_WriteASCIIString(&writer, "<Token", 6) < 0) {
1028 goto error;
1029 }
1030
1031 if (self->tok_used) {
1032 if (_PyUnicodeWriter_WriteASCIIString(&writer, " used", 5) < 0) {
1033 goto error;
1034 }
1035 }
1036
1037 if (_PyUnicodeWriter_WriteASCIIString(&writer, " var=", 5) < 0) {
1038 goto error;
1039 }
1040
1041 PyObject *var = PyObject_Repr((PyObject *)self->tok_var);
1042 if (var == NULL) {
1043 goto error;
1044 }
1045 if (_PyUnicodeWriter_WriteStr(&writer, var) < 0) {
1046 Py_DECREF(var);
1047 goto error;
1048 }
1049 Py_DECREF(var);
1050
1051 PyObject *addr = PyUnicode_FromFormat(" at %p>", self);
1052 if (addr == NULL) {
1053 goto error;
1054 }
1055 if (_PyUnicodeWriter_WriteStr(&writer, addr) < 0) {
1056 Py_DECREF(addr);
1057 goto error;
1058 }
1059 Py_DECREF(addr);
1060
1061 return _PyUnicodeWriter_Finish(&writer);
1062
1063error:
1064 _PyUnicodeWriter_Dealloc(&writer);
1065 return NULL;
1066}
1067
1068static PyObject *
1069token_get_var(PyContextToken *self)
1070{
1071 Py_INCREF(self->tok_var);
1072 return (PyObject *)self->tok_var;
1073}
1074
1075static PyObject *
1076token_get_old_value(PyContextToken *self)
1077{
1078 if (self->tok_oldval == NULL) {
1079 return get_token_missing();
1080 }
1081
1082 Py_INCREF(self->tok_oldval);
1083 return self->tok_oldval;
1084}
1085
1086static PyGetSetDef PyContextTokenType_getsetlist[] = {
1087 {"var", (getter)token_get_var, NULL, NULL},
1088 {"old_value", (getter)token_get_old_value, NULL, NULL},
1089 {NULL}
1090};
1091
1092PyTypeObject PyContextToken_Type = {
1093 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1094 "Token",
1095 sizeof(PyContextToken),
1096 .tp_getset = PyContextTokenType_getsetlist,
1097 .tp_dealloc = (destructor)token_tp_dealloc,
1098 .tp_getattro = PyObject_GenericGetAttr,
1099 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1100 .tp_traverse = (traverseproc)token_tp_traverse,
1101 .tp_clear = (inquiry)token_tp_clear,
1102 .tp_new = token_tp_new,
1103 .tp_free = PyObject_GC_Del,
1104 .tp_hash = PyObject_HashNotImplemented,
1105 .tp_repr = (reprfunc)token_tp_repr,
1106};
1107
1108static PyContextToken *
1109token_new(PyContext *ctx, PyContextVar *var, PyObject *val)
1110{
1111 PyContextToken *tok = PyObject_GC_New(PyContextToken, &PyContextToken_Type);
1112 if (tok == NULL) {
1113 return NULL;
1114 }
1115
1116 Py_INCREF(ctx);
1117 tok->tok_ctx = ctx;
1118
1119 Py_INCREF(var);
1120 tok->tok_var = var;
1121
1122 Py_XINCREF(val);
1123 tok->tok_oldval = val;
1124
1125 tok->tok_used = 0;
1126
1127 PyObject_GC_Track(tok);
1128 return tok;
1129}
1130
1131
1132/////////////////////////// Token.MISSING
1133
1134
1135static PyObject *_token_missing;
1136
1137
1138typedef struct {
1139 PyObject_HEAD
1140} PyContextTokenMissing;
1141
1142
1143static PyObject *
1144context_token_missing_tp_repr(PyObject *self)
1145{
1146 return PyUnicode_FromString("<Token.MISSING>");
1147}
1148
1149
1150PyTypeObject PyContextTokenMissing_Type = {
1151 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1152 "Token.MISSING",
1153 sizeof(PyContextTokenMissing),
1154 .tp_getattro = PyObject_GenericGetAttr,
1155 .tp_flags = Py_TPFLAGS_DEFAULT,
1156 .tp_repr = context_token_missing_tp_repr,
1157};
1158
1159
1160static PyObject *
1161get_token_missing(void)
1162{
1163 if (_token_missing != NULL) {
1164 Py_INCREF(_token_missing);
1165 return _token_missing;
1166 }
1167
1168 _token_missing = (PyObject *)PyObject_New(
1169 PyContextTokenMissing, &PyContextTokenMissing_Type);
1170 if (_token_missing == NULL) {
1171 return NULL;
1172 }
1173
1174 Py_INCREF(_token_missing);
1175 return _token_missing;
1176}
1177
1178
1179///////////////////////////
1180
1181
1182int
1183PyContext_ClearFreeList(void)
1184{
Miss Islington (bot)7df80492018-02-09 07:56:34 -08001185 int size = ctx_freelist_len;
Yury Selivanovf23746a2018-01-22 19:11:18 -05001186 while (ctx_freelist_len) {
1187 PyContext *ctx = ctx_freelist;
1188 ctx_freelist = (PyContext *)ctx->ctx_weakreflist;
1189 ctx->ctx_weakreflist = NULL;
1190 PyObject_GC_Del(ctx);
1191 ctx_freelist_len--;
1192 }
1193 return size;
1194}
1195
1196
1197void
1198_PyContext_Fini(void)
1199{
1200 Py_CLEAR(_token_missing);
1201 (void)PyContext_ClearFreeList();
1202 (void)_PyHamt_Fini();
1203}
1204
1205
1206int
1207_PyContext_Init(void)
1208{
1209 if (!_PyHamt_Init()) {
1210 return 0;
1211 }
1212
1213 if ((PyType_Ready(&PyContext_Type) < 0) ||
1214 (PyType_Ready(&PyContextVar_Type) < 0) ||
1215 (PyType_Ready(&PyContextToken_Type) < 0) ||
1216 (PyType_Ready(&PyContextTokenMissing_Type) < 0))
1217 {
1218 return 0;
1219 }
1220
1221 PyObject *missing = get_token_missing();
1222 if (PyDict_SetItemString(
1223 PyContextToken_Type.tp_dict, "MISSING", missing))
1224 {
1225 Py_DECREF(missing);
1226 return 0;
1227 }
1228 Py_DECREF(missing);
1229
1230 return 1;
1231}