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