blob: e0338c97e1873a31a7b0e15c508529d42ebf0970 [file] [log] [blame]
Yury Selivanovf23746a2018-01-22 19:11:18 -05001#include "Python.h"
2
Victor Stinner27e2d1f2018-11-01 00:52:28 +01003#include "pycore_context.h"
4#include "pycore_hamt.h"
Victor Stinnerbcda8f12018-11-21 22:27:47 +01005#include "pycore_object.h"
Victor Stinner7e433732019-11-08 10:05:17 +01006#include "pycore_pyerrors.h"
Victor Stinnerbcda8f12018-11-21 22:27:47 +01007#include "pycore_pystate.h"
8#include "structmember.h"
Yury Selivanovf23746a2018-01-22 19:11:18 -05009
10
11#define CONTEXT_FREELIST_MAXLEN 255
12static PyContext *ctx_freelist = NULL;
Serhiy Storchakabfe4fd52018-02-09 17:31:26 +020013static int ctx_freelist_len = 0;
Yury Selivanovf23746a2018-01-22 19:11:18 -050014
15
16#include "clinic/context.c.h"
17/*[clinic input]
18module _contextvars
19[clinic start generated code]*/
20/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a0955718c8b8cea6]*/
21
22
Yury Selivanov2ec872b2018-09-21 15:33:56 -040023#define ENSURE_Context(o, err_ret) \
24 if (!PyContext_CheckExact(o)) { \
25 PyErr_SetString(PyExc_TypeError, \
26 "an instance of Context was expected"); \
27 return err_ret; \
28 }
29
30#define ENSURE_ContextVar(o, err_ret) \
31 if (!PyContextVar_CheckExact(o)) { \
32 PyErr_SetString(PyExc_TypeError, \
33 "an instance of ContextVar was expected"); \
34 return err_ret; \
35 }
36
37#define ENSURE_ContextToken(o, err_ret) \
38 if (!PyContextToken_CheckExact(o)) { \
39 PyErr_SetString(PyExc_TypeError, \
40 "an instance of Token was expected"); \
41 return err_ret; \
42 }
43
44
Yury Selivanovf23746a2018-01-22 19:11:18 -050045/////////////////////////// Context API
46
47
48static PyContext *
49context_new_empty(void);
50
51static PyContext *
52context_new_from_vars(PyHamtObject *vars);
53
54static inline PyContext *
55context_get(void);
56
57static PyContextToken *
58token_new(PyContext *ctx, PyContextVar *var, PyObject *val);
59
60static PyContextVar *
61contextvar_new(PyObject *name, PyObject *def);
62
63static int
64contextvar_set(PyContextVar *var, PyObject *val);
65
66static int
67contextvar_del(PyContextVar *var);
68
69
70PyObject *
71_PyContext_NewHamtForTests(void)
72{
73 return (PyObject *)_PyHamt_New();
74}
75
76
Yury Selivanov2ec872b2018-09-21 15:33:56 -040077PyObject *
Yury Selivanovf23746a2018-01-22 19:11:18 -050078PyContext_New(void)
79{
Yury Selivanov2ec872b2018-09-21 15:33:56 -040080 return (PyObject *)context_new_empty();
Yury Selivanovf23746a2018-01-22 19:11:18 -050081}
82
83
Yury Selivanov2ec872b2018-09-21 15:33:56 -040084PyObject *
85PyContext_Copy(PyObject * octx)
Yury Selivanovf23746a2018-01-22 19:11:18 -050086{
Yury Selivanov2ec872b2018-09-21 15:33:56 -040087 ENSURE_Context(octx, NULL)
88 PyContext *ctx = (PyContext *)octx;
89 return (PyObject *)context_new_from_vars(ctx->ctx_vars);
Yury Selivanovf23746a2018-01-22 19:11:18 -050090}
91
92
Yury Selivanov2ec872b2018-09-21 15:33:56 -040093PyObject *
Yury Selivanovf23746a2018-01-22 19:11:18 -050094PyContext_CopyCurrent(void)
95{
96 PyContext *ctx = context_get();
97 if (ctx == NULL) {
98 return NULL;
99 }
100
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400101 return (PyObject *)context_new_from_vars(ctx->ctx_vars);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500102}
103
104
Victor Stinner7e433732019-11-08 10:05:17 +0100105static int
106_PyContext_Enter(PyThreadState *ts, PyObject *octx)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500107{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400108 ENSURE_Context(octx, -1)
109 PyContext *ctx = (PyContext *)octx;
110
Yury Selivanovf23746a2018-01-22 19:11:18 -0500111 if (ctx->ctx_entered) {
Victor Stinner7e433732019-11-08 10:05:17 +0100112 _PyErr_Format(ts, PyExc_RuntimeError,
113 "cannot enter context: %R is already entered", ctx);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500114 return -1;
115 }
116
Yury Selivanovf23746a2018-01-22 19:11:18 -0500117 ctx->ctx_prev = (PyContext *)ts->context; /* borrow */
118 ctx->ctx_entered = 1;
119
120 Py_INCREF(ctx);
121 ts->context = (PyObject *)ctx;
122 ts->context_ver++;
123
124 return 0;
125}
126
127
128int
Victor Stinner7e433732019-11-08 10:05:17 +0100129PyContext_Enter(PyObject *octx)
130{
131 PyThreadState *ts = _PyThreadState_GET();
132 assert(ts != NULL);
133 return _PyContext_Enter(ts, octx);
134}
135
136
137static int
138_PyContext_Exit(PyThreadState *ts, PyObject *octx)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500139{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400140 ENSURE_Context(octx, -1)
141 PyContext *ctx = (PyContext *)octx;
142
Yury Selivanovf23746a2018-01-22 19:11:18 -0500143 if (!ctx->ctx_entered) {
144 PyErr_Format(PyExc_RuntimeError,
145 "cannot exit context: %R has not been entered", ctx);
146 return -1;
147 }
148
Yury Selivanovf23746a2018-01-22 19:11:18 -0500149 if (ts->context != (PyObject *)ctx) {
150 /* Can only happen if someone misuses the C API */
151 PyErr_SetString(PyExc_RuntimeError,
152 "cannot exit context: thread state references "
153 "a different context object");
154 return -1;
155 }
156
157 Py_SETREF(ts->context, (PyObject *)ctx->ctx_prev);
158 ts->context_ver++;
159
160 ctx->ctx_prev = NULL;
161 ctx->ctx_entered = 0;
162
163 return 0;
164}
165
Victor Stinner7e433732019-11-08 10:05:17 +0100166int
167PyContext_Exit(PyObject *octx)
168{
169 PyThreadState *ts = _PyThreadState_GET();
170 assert(ts != NULL);
171 return _PyContext_Exit(ts, octx);
172}
173
Yury Selivanovf23746a2018-01-22 19:11:18 -0500174
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400175PyObject *
Yury Selivanovf23746a2018-01-22 19:11:18 -0500176PyContextVar_New(const char *name, PyObject *def)
177{
178 PyObject *pyname = PyUnicode_FromString(name);
179 if (pyname == NULL) {
180 return NULL;
181 }
Yury Selivanov6ab62922018-01-25 14:18:55 -0500182 PyContextVar *var = contextvar_new(pyname, def);
183 Py_DECREF(pyname);
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400184 return (PyObject *)var;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500185}
186
187
188int
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400189PyContextVar_Get(PyObject *ovar, PyObject *def, PyObject **val)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500190{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400191 ENSURE_ContextVar(ovar, -1)
192 PyContextVar *var = (PyContextVar *)ovar;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500193
Victor Stinner50b48572018-11-01 01:51:40 +0100194 PyThreadState *ts = _PyThreadState_GET();
Yury Selivanov226e5002018-01-26 17:24:52 -0500195 assert(ts != NULL);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500196 if (ts->context == NULL) {
197 goto not_found;
198 }
199
200 if (var->var_cached != NULL &&
201 var->var_cached_tsid == ts->id &&
202 var->var_cached_tsver == ts->context_ver)
203 {
204 *val = var->var_cached;
205 goto found;
206 }
207
208 assert(PyContext_CheckExact(ts->context));
209 PyHamtObject *vars = ((PyContext *)ts->context)->ctx_vars;
210
211 PyObject *found = NULL;
212 int res = _PyHamt_Find(vars, (PyObject*)var, &found);
213 if (res < 0) {
214 goto error;
215 }
216 if (res == 1) {
217 assert(found != NULL);
218 var->var_cached = found; /* borrow */
219 var->var_cached_tsid = ts->id;
220 var->var_cached_tsver = ts->context_ver;
221
222 *val = found;
223 goto found;
224 }
225
226not_found:
227 if (def == NULL) {
228 if (var->var_default != NULL) {
229 *val = var->var_default;
230 goto found;
231 }
232
233 *val = NULL;
234 goto found;
235 }
236 else {
237 *val = def;
238 goto found;
239 }
240
241found:
242 Py_XINCREF(*val);
243 return 0;
244
245error:
246 *val = NULL;
247 return -1;
248}
249
250
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400251PyObject *
252PyContextVar_Set(PyObject *ovar, PyObject *val)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500253{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400254 ENSURE_ContextVar(ovar, NULL)
255 PyContextVar *var = (PyContextVar *)ovar;
256
Yury Selivanovf23746a2018-01-22 19:11:18 -0500257 if (!PyContextVar_CheckExact(var)) {
258 PyErr_SetString(
259 PyExc_TypeError, "an instance of ContextVar was expected");
260 return NULL;
261 }
262
263 PyContext *ctx = context_get();
264 if (ctx == NULL) {
265 return NULL;
266 }
267
268 PyObject *old_val = NULL;
269 int found = _PyHamt_Find(ctx->ctx_vars, (PyObject *)var, &old_val);
270 if (found < 0) {
271 return NULL;
272 }
273
274 Py_XINCREF(old_val);
275 PyContextToken *tok = token_new(ctx, var, old_val);
276 Py_XDECREF(old_val);
277
278 if (contextvar_set(var, val)) {
279 Py_DECREF(tok);
280 return NULL;
281 }
282
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400283 return (PyObject *)tok;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500284}
285
286
287int
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400288PyContextVar_Reset(PyObject *ovar, PyObject *otok)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500289{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400290 ENSURE_ContextVar(ovar, -1)
291 ENSURE_ContextToken(otok, -1)
292 PyContextVar *var = (PyContextVar *)ovar;
293 PyContextToken *tok = (PyContextToken *)otok;
294
Yury Selivanovf23746a2018-01-22 19:11:18 -0500295 if (tok->tok_used) {
296 PyErr_Format(PyExc_RuntimeError,
297 "%R has already been used once", tok);
298 return -1;
299 }
300
301 if (var != tok->tok_var) {
302 PyErr_Format(PyExc_ValueError,
303 "%R was created by a different ContextVar", tok);
304 return -1;
305 }
306
307 PyContext *ctx = context_get();
308 if (ctx != tok->tok_ctx) {
309 PyErr_Format(PyExc_ValueError,
310 "%R was created in a different Context", tok);
311 return -1;
312 }
313
314 tok->tok_used = 1;
315
316 if (tok->tok_oldval == NULL) {
317 return contextvar_del(var);
318 }
319 else {
320 return contextvar_set(var, tok->tok_oldval);
321 }
322}
323
324
325/////////////////////////// PyContext
326
327/*[clinic input]
328class _contextvars.Context "PyContext *" "&PyContext_Type"
329[clinic start generated code]*/
330/*[clinic end generated code: output=da39a3ee5e6b4b0d input=bdf87f8e0cb580e8]*/
331
332
333static inline PyContext *
334_context_alloc(void)
335{
336 PyContext *ctx;
337 if (ctx_freelist_len) {
338 ctx_freelist_len--;
339 ctx = ctx_freelist;
340 ctx_freelist = (PyContext *)ctx->ctx_weakreflist;
341 ctx->ctx_weakreflist = NULL;
342 _Py_NewReference((PyObject *)ctx);
343 }
344 else {
345 ctx = PyObject_GC_New(PyContext, &PyContext_Type);
346 if (ctx == NULL) {
347 return NULL;
348 }
349 }
350
351 ctx->ctx_vars = NULL;
352 ctx->ctx_prev = NULL;
353 ctx->ctx_entered = 0;
354 ctx->ctx_weakreflist = NULL;
355
356 return ctx;
357}
358
359
360static PyContext *
361context_new_empty(void)
362{
363 PyContext *ctx = _context_alloc();
364 if (ctx == NULL) {
365 return NULL;
366 }
367
368 ctx->ctx_vars = _PyHamt_New();
369 if (ctx->ctx_vars == NULL) {
370 Py_DECREF(ctx);
371 return NULL;
372 }
373
374 _PyObject_GC_TRACK(ctx);
375 return ctx;
376}
377
378
379static PyContext *
380context_new_from_vars(PyHamtObject *vars)
381{
382 PyContext *ctx = _context_alloc();
383 if (ctx == NULL) {
384 return NULL;
385 }
386
387 Py_INCREF(vars);
388 ctx->ctx_vars = vars;
389
390 _PyObject_GC_TRACK(ctx);
391 return ctx;
392}
393
394
395static inline PyContext *
396context_get(void)
397{
Victor Stinner50b48572018-11-01 01:51:40 +0100398 PyThreadState *ts = _PyThreadState_GET();
Yury Selivanovbc4123b2018-01-27 13:24:20 -0500399 assert(ts != NULL);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500400 PyContext *current_ctx = (PyContext *)ts->context;
401 if (current_ctx == NULL) {
402 current_ctx = context_new_empty();
403 if (current_ctx == NULL) {
404 return NULL;
405 }
406 ts->context = (PyObject *)current_ctx;
407 }
408 return current_ctx;
409}
410
411static int
412context_check_key_type(PyObject *key)
413{
414 if (!PyContextVar_CheckExact(key)) {
415 // abort();
416 PyErr_Format(PyExc_TypeError,
417 "a ContextVar key was expected, got %R", key);
418 return -1;
419 }
420 return 0;
421}
422
423static PyObject *
424context_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
425{
426 if (PyTuple_Size(args) || (kwds != NULL && PyDict_Size(kwds))) {
427 PyErr_SetString(
428 PyExc_TypeError, "Context() does not accept any arguments");
429 return NULL;
430 }
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400431 return PyContext_New();
Yury Selivanovf23746a2018-01-22 19:11:18 -0500432}
433
434static int
435context_tp_clear(PyContext *self)
436{
437 Py_CLEAR(self->ctx_prev);
438 Py_CLEAR(self->ctx_vars);
439 return 0;
440}
441
442static int
443context_tp_traverse(PyContext *self, visitproc visit, void *arg)
444{
445 Py_VISIT(self->ctx_prev);
446 Py_VISIT(self->ctx_vars);
447 return 0;
448}
449
450static void
451context_tp_dealloc(PyContext *self)
452{
453 _PyObject_GC_UNTRACK(self);
454
455 if (self->ctx_weakreflist != NULL) {
456 PyObject_ClearWeakRefs((PyObject*)self);
457 }
458 (void)context_tp_clear(self);
459
460 if (ctx_freelist_len < CONTEXT_FREELIST_MAXLEN) {
461 ctx_freelist_len++;
462 self->ctx_weakreflist = (PyObject *)ctx_freelist;
463 ctx_freelist = self;
464 }
465 else {
466 Py_TYPE(self)->tp_free(self);
467 }
468}
469
470static PyObject *
471context_tp_iter(PyContext *self)
472{
473 return _PyHamt_NewIterKeys(self->ctx_vars);
474}
475
476static PyObject *
477context_tp_richcompare(PyObject *v, PyObject *w, int op)
478{
479 if (!PyContext_CheckExact(v) || !PyContext_CheckExact(w) ||
480 (op != Py_EQ && op != Py_NE))
481 {
482 Py_RETURN_NOTIMPLEMENTED;
483 }
484
485 int res = _PyHamt_Eq(
486 ((PyContext *)v)->ctx_vars, ((PyContext *)w)->ctx_vars);
487 if (res < 0) {
488 return NULL;
489 }
490
491 if (op == Py_NE) {
492 res = !res;
493 }
494
495 if (res) {
496 Py_RETURN_TRUE;
497 }
498 else {
499 Py_RETURN_FALSE;
500 }
501}
502
503static Py_ssize_t
504context_tp_len(PyContext *self)
505{
506 return _PyHamt_Len(self->ctx_vars);
507}
508
509static PyObject *
510context_tp_subscript(PyContext *self, PyObject *key)
511{
512 if (context_check_key_type(key)) {
513 return NULL;
514 }
515 PyObject *val = NULL;
516 int found = _PyHamt_Find(self->ctx_vars, key, &val);
517 if (found < 0) {
518 return NULL;
519 }
520 if (found == 0) {
521 PyErr_SetObject(PyExc_KeyError, key);
522 return NULL;
523 }
524 Py_INCREF(val);
525 return val;
526}
527
528static int
529context_tp_contains(PyContext *self, PyObject *key)
530{
531 if (context_check_key_type(key)) {
532 return -1;
533 }
534 PyObject *val = NULL;
535 return _PyHamt_Find(self->ctx_vars, key, &val);
536}
537
538
539/*[clinic input]
540_contextvars.Context.get
541 key: object
542 default: object = None
543 /
Peter Lamut20678fd2018-07-30 16:15:44 +0100544
545Return the value for `key` if `key` has the value in the context object.
546
547If `key` does not exist, return `default`. If `default` is not given,
548return None.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500549[clinic start generated code]*/
550
551static PyObject *
552_contextvars_Context_get_impl(PyContext *self, PyObject *key,
553 PyObject *default_value)
Peter Lamut20678fd2018-07-30 16:15:44 +0100554/*[clinic end generated code: output=0c54aa7664268189 input=c8eeb81505023995]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500555{
556 if (context_check_key_type(key)) {
557 return NULL;
558 }
559
560 PyObject *val = NULL;
561 int found = _PyHamt_Find(self->ctx_vars, key, &val);
562 if (found < 0) {
563 return NULL;
564 }
565 if (found == 0) {
566 Py_INCREF(default_value);
567 return default_value;
568 }
569 Py_INCREF(val);
570 return val;
571}
572
573
574/*[clinic input]
575_contextvars.Context.items
Peter Lamut20678fd2018-07-30 16:15:44 +0100576
577Return all variables and their values in the context object.
578
579The result is returned as a list of 2-tuples (variable, value).
Yury Selivanovf23746a2018-01-22 19:11:18 -0500580[clinic start generated code]*/
581
582static PyObject *
583_contextvars_Context_items_impl(PyContext *self)
Peter Lamut20678fd2018-07-30 16:15:44 +0100584/*[clinic end generated code: output=fa1655c8a08502af input=00db64ae379f9f42]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500585{
586 return _PyHamt_NewIterItems(self->ctx_vars);
587}
588
589
590/*[clinic input]
591_contextvars.Context.keys
Peter Lamut20678fd2018-07-30 16:15:44 +0100592
593Return a list of all variables in the context object.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500594[clinic start generated code]*/
595
596static PyObject *
597_contextvars_Context_keys_impl(PyContext *self)
Peter Lamut20678fd2018-07-30 16:15:44 +0100598/*[clinic end generated code: output=177227c6b63ec0e2 input=114b53aebca3449c]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500599{
600 return _PyHamt_NewIterKeys(self->ctx_vars);
601}
602
603
604/*[clinic input]
605_contextvars.Context.values
Peter Lamut20678fd2018-07-30 16:15:44 +0100606
animalize463572c2019-02-25 07:18:48 +0800607Return a list of all variables' values in the context object.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500608[clinic start generated code]*/
609
610static PyObject *
611_contextvars_Context_values_impl(PyContext *self)
animalize463572c2019-02-25 07:18:48 +0800612/*[clinic end generated code: output=d286dabfc8db6dde input=ce8075d04a6ea526]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500613{
614 return _PyHamt_NewIterValues(self->ctx_vars);
615}
616
617
618/*[clinic input]
619_contextvars.Context.copy
Peter Lamut20678fd2018-07-30 16:15:44 +0100620
621Return a shallow copy of the context object.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500622[clinic start generated code]*/
623
624static PyObject *
625_contextvars_Context_copy_impl(PyContext *self)
Peter Lamut20678fd2018-07-30 16:15:44 +0100626/*[clinic end generated code: output=30ba8896c4707a15 input=ebafdbdd9c72d592]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500627{
628 return (PyObject *)context_new_from_vars(self->ctx_vars);
629}
630
631
632static PyObject *
633context_run(PyContext *self, PyObject *const *args,
634 Py_ssize_t nargs, PyObject *kwnames)
635{
Victor Stinner7e433732019-11-08 10:05:17 +0100636 PyThreadState *ts = _PyThreadState_GET();
637
Yury Selivanovf23746a2018-01-22 19:11:18 -0500638 if (nargs < 1) {
Victor Stinner7e433732019-11-08 10:05:17 +0100639 _PyErr_SetString(ts, PyExc_TypeError,
640 "run() missing 1 required positional argument");
Yury Selivanovf23746a2018-01-22 19:11:18 -0500641 return NULL;
642 }
643
Victor Stinner7e433732019-11-08 10:05:17 +0100644 if (_PyContext_Enter(ts, (PyObject *)self)) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500645 return NULL;
646 }
647
Victor Stinner7e433732019-11-08 10:05:17 +0100648 PyObject *call_result = _PyObject_VectorcallTstate(
649 ts, args[0], args + 1, nargs - 1, kwnames);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500650
Victor Stinner7e433732019-11-08 10:05:17 +0100651 if (_PyContext_Exit(ts, (PyObject *)self)) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500652 return NULL;
653 }
654
655 return call_result;
656}
657
658
659static PyMethodDef PyContext_methods[] = {
660 _CONTEXTVARS_CONTEXT_GET_METHODDEF
661 _CONTEXTVARS_CONTEXT_ITEMS_METHODDEF
662 _CONTEXTVARS_CONTEXT_KEYS_METHODDEF
663 _CONTEXTVARS_CONTEXT_VALUES_METHODDEF
664 _CONTEXTVARS_CONTEXT_COPY_METHODDEF
Serhiy Storchaka62be7422018-11-27 13:27:31 +0200665 {"run", (PyCFunction)(void(*)(void))context_run, METH_FASTCALL | METH_KEYWORDS, NULL},
Yury Selivanovf23746a2018-01-22 19:11:18 -0500666 {NULL, NULL}
667};
668
669static PySequenceMethods PyContext_as_sequence = {
670 0, /* sq_length */
671 0, /* sq_concat */
672 0, /* sq_repeat */
673 0, /* sq_item */
674 0, /* sq_slice */
675 0, /* sq_ass_item */
676 0, /* sq_ass_slice */
677 (objobjproc)context_tp_contains, /* sq_contains */
678 0, /* sq_inplace_concat */
679 0, /* sq_inplace_repeat */
680};
681
682static PyMappingMethods PyContext_as_mapping = {
683 (lenfunc)context_tp_len, /* mp_length */
684 (binaryfunc)context_tp_subscript, /* mp_subscript */
685};
686
687PyTypeObject PyContext_Type = {
688 PyVarObject_HEAD_INIT(&PyType_Type, 0)
689 "Context",
690 sizeof(PyContext),
691 .tp_methods = PyContext_methods,
692 .tp_as_mapping = &PyContext_as_mapping,
693 .tp_as_sequence = &PyContext_as_sequence,
694 .tp_iter = (getiterfunc)context_tp_iter,
695 .tp_dealloc = (destructor)context_tp_dealloc,
696 .tp_getattro = PyObject_GenericGetAttr,
697 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
698 .tp_richcompare = context_tp_richcompare,
699 .tp_traverse = (traverseproc)context_tp_traverse,
700 .tp_clear = (inquiry)context_tp_clear,
701 .tp_new = context_tp_new,
702 .tp_weaklistoffset = offsetof(PyContext, ctx_weakreflist),
703 .tp_hash = PyObject_HashNotImplemented,
704};
705
706
707/////////////////////////// ContextVar
708
709
710static int
711contextvar_set(PyContextVar *var, PyObject *val)
712{
713 var->var_cached = NULL;
714 PyThreadState *ts = PyThreadState_Get();
715
716 PyContext *ctx = context_get();
717 if (ctx == NULL) {
718 return -1;
719 }
720
721 PyHamtObject *new_vars = _PyHamt_Assoc(
722 ctx->ctx_vars, (PyObject *)var, val);
723 if (new_vars == NULL) {
724 return -1;
725 }
726
727 Py_SETREF(ctx->ctx_vars, new_vars);
728
729 var->var_cached = val; /* borrow */
730 var->var_cached_tsid = ts->id;
731 var->var_cached_tsver = ts->context_ver;
732 return 0;
733}
734
735static int
736contextvar_del(PyContextVar *var)
737{
738 var->var_cached = NULL;
739
740 PyContext *ctx = context_get();
741 if (ctx == NULL) {
742 return -1;
743 }
744
745 PyHamtObject *vars = ctx->ctx_vars;
746 PyHamtObject *new_vars = _PyHamt_Without(vars, (PyObject *)var);
747 if (new_vars == NULL) {
748 return -1;
749 }
750
751 if (vars == new_vars) {
752 Py_DECREF(new_vars);
753 PyErr_SetObject(PyExc_LookupError, (PyObject *)var);
754 return -1;
755 }
756
757 Py_SETREF(ctx->ctx_vars, new_vars);
758 return 0;
759}
760
761static Py_hash_t
762contextvar_generate_hash(void *addr, PyObject *name)
763{
764 /* Take hash of `name` and XOR it with the object's addr.
765
766 The structure of the tree is encoded in objects' hashes, which
767 means that sufficiently similar hashes would result in tall trees
768 with many Collision nodes. Which would, in turn, result in slower
769 get and set operations.
770
771 The XORing helps to ensure that:
772
773 (1) sequentially allocated ContextVar objects have
774 different hashes;
775
776 (2) context variables with equal names have
777 different hashes.
778 */
779
780 Py_hash_t name_hash = PyObject_Hash(name);
781 if (name_hash == -1) {
782 return -1;
783 }
784
785 Py_hash_t res = _Py_HashPointer(addr) ^ name_hash;
786 return res == -1 ? -2 : res;
787}
788
789static PyContextVar *
790contextvar_new(PyObject *name, PyObject *def)
791{
792 if (!PyUnicode_Check(name)) {
793 PyErr_SetString(PyExc_TypeError,
794 "context variable name must be a str");
795 return NULL;
796 }
797
798 PyContextVar *var = PyObject_GC_New(PyContextVar, &PyContextVar_Type);
799 if (var == NULL) {
800 return NULL;
801 }
802
803 var->var_hash = contextvar_generate_hash(var, name);
804 if (var->var_hash == -1) {
805 Py_DECREF(var);
806 return NULL;
807 }
808
809 Py_INCREF(name);
810 var->var_name = name;
811
812 Py_XINCREF(def);
813 var->var_default = def;
814
815 var->var_cached = NULL;
816 var->var_cached_tsid = 0;
817 var->var_cached_tsver = 0;
818
Yury Selivanov6ab62922018-01-25 14:18:55 -0500819 if (_PyObject_GC_MAY_BE_TRACKED(name) ||
820 (def != NULL && _PyObject_GC_MAY_BE_TRACKED(def)))
Yury Selivanovf23746a2018-01-22 19:11:18 -0500821 {
822 PyObject_GC_Track(var);
823 }
824 return var;
825}
826
827
828/*[clinic input]
829class _contextvars.ContextVar "PyContextVar *" "&PyContextVar_Type"
830[clinic start generated code]*/
831/*[clinic end generated code: output=da39a3ee5e6b4b0d input=445da935fa8883c3]*/
832
833
834static PyObject *
835contextvar_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
836{
837 static char *kwlist[] = {"", "default", NULL};
838 PyObject *name;
839 PyObject *def = NULL;
840
841 if (!PyArg_ParseTupleAndKeywords(
842 args, kwds, "O|$O:ContextVar", kwlist, &name, &def))
843 {
844 return NULL;
845 }
846
847 return (PyObject *)contextvar_new(name, def);
848}
849
850static int
851contextvar_tp_clear(PyContextVar *self)
852{
853 Py_CLEAR(self->var_name);
854 Py_CLEAR(self->var_default);
855 self->var_cached = NULL;
856 self->var_cached_tsid = 0;
857 self->var_cached_tsver = 0;
858 return 0;
859}
860
861static int
862contextvar_tp_traverse(PyContextVar *self, visitproc visit, void *arg)
863{
864 Py_VISIT(self->var_name);
865 Py_VISIT(self->var_default);
866 return 0;
867}
868
869static void
870contextvar_tp_dealloc(PyContextVar *self)
871{
872 PyObject_GC_UnTrack(self);
873 (void)contextvar_tp_clear(self);
874 Py_TYPE(self)->tp_free(self);
875}
876
877static Py_hash_t
878contextvar_tp_hash(PyContextVar *self)
879{
880 return self->var_hash;
881}
882
883static PyObject *
884contextvar_tp_repr(PyContextVar *self)
885{
886 _PyUnicodeWriter writer;
887
888 _PyUnicodeWriter_Init(&writer);
889
890 if (_PyUnicodeWriter_WriteASCIIString(
891 &writer, "<ContextVar name=", 17) < 0)
892 {
893 goto error;
894 }
895
896 PyObject *name = PyObject_Repr(self->var_name);
897 if (name == NULL) {
898 goto error;
899 }
900 if (_PyUnicodeWriter_WriteStr(&writer, name) < 0) {
901 Py_DECREF(name);
902 goto error;
903 }
904 Py_DECREF(name);
905
906 if (self->var_default != NULL) {
907 if (_PyUnicodeWriter_WriteASCIIString(&writer, " default=", 9) < 0) {
908 goto error;
909 }
910
911 PyObject *def = PyObject_Repr(self->var_default);
912 if (def == NULL) {
913 goto error;
914 }
915 if (_PyUnicodeWriter_WriteStr(&writer, def) < 0) {
916 Py_DECREF(def);
917 goto error;
918 }
919 Py_DECREF(def);
920 }
921
922 PyObject *addr = PyUnicode_FromFormat(" at %p>", self);
923 if (addr == NULL) {
924 goto error;
925 }
926 if (_PyUnicodeWriter_WriteStr(&writer, addr) < 0) {
927 Py_DECREF(addr);
928 goto error;
929 }
930 Py_DECREF(addr);
931
932 return _PyUnicodeWriter_Finish(&writer);
933
934error:
935 _PyUnicodeWriter_Dealloc(&writer);
936 return NULL;
937}
938
939
940/*[clinic input]
941_contextvars.ContextVar.get
942 default: object = NULL
943 /
Peter Lamut20678fd2018-07-30 16:15:44 +0100944
945Return a value for the context variable for the current context.
946
947If there is no value for the variable in the current context, the method will:
948 * return the value of the default argument of the method, if provided; or
949 * return the default value for the context variable, if it was created
950 with one; or
951 * raise a LookupError.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500952[clinic start generated code]*/
953
954static PyObject *
955_contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value)
Peter Lamut20678fd2018-07-30 16:15:44 +0100956/*[clinic end generated code: output=0746bd0aa2ced7bf input=30aa2ab9e433e401]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500957{
958 if (!PyContextVar_CheckExact(self)) {
959 PyErr_SetString(
960 PyExc_TypeError, "an instance of ContextVar was expected");
961 return NULL;
962 }
963
964 PyObject *val;
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400965 if (PyContextVar_Get((PyObject *)self, default_value, &val) < 0) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500966 return NULL;
967 }
968
969 if (val == NULL) {
970 PyErr_SetObject(PyExc_LookupError, (PyObject *)self);
971 return NULL;
972 }
973
974 return val;
975}
976
977/*[clinic input]
978_contextvars.ContextVar.set
979 value: object
980 /
Peter Lamut20678fd2018-07-30 16:15:44 +0100981
982Call to set a new value for the context variable in the current context.
983
984The required value argument is the new value for the context variable.
985
986Returns a Token object that can be used to restore the variable to its previous
987value via the `ContextVar.reset()` method.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500988[clinic start generated code]*/
989
990static PyObject *
991_contextvars_ContextVar_set(PyContextVar *self, PyObject *value)
Peter Lamut20678fd2018-07-30 16:15:44 +0100992/*[clinic end generated code: output=446ed5e820d6d60b input=c0a6887154227453]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500993{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400994 return PyContextVar_Set((PyObject *)self, value);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500995}
996
997/*[clinic input]
998_contextvars.ContextVar.reset
999 token: object
1000 /
Peter Lamut20678fd2018-07-30 16:15:44 +01001001
1002Reset the context variable.
1003
1004The variable is reset to the value it had before the `ContextVar.set()` that
1005created the token was used.
Yury Selivanovf23746a2018-01-22 19:11:18 -05001006[clinic start generated code]*/
1007
1008static PyObject *
1009_contextvars_ContextVar_reset(PyContextVar *self, PyObject *token)
Peter Lamut20678fd2018-07-30 16:15:44 +01001010/*[clinic end generated code: output=d4ee34d0742d62ee input=ebe2881e5af4ffda]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -05001011{
1012 if (!PyContextToken_CheckExact(token)) {
1013 PyErr_Format(PyExc_TypeError,
1014 "expected an instance of Token, got %R", token);
1015 return NULL;
1016 }
1017
Yury Selivanov2ec872b2018-09-21 15:33:56 -04001018 if (PyContextVar_Reset((PyObject *)self, token)) {
Yury Selivanovf23746a2018-01-22 19:11:18 -05001019 return NULL;
1020 }
1021
1022 Py_RETURN_NONE;
1023}
1024
1025
1026static PyObject *
AMIR28c91632019-12-08 15:05:59 +03301027contextvar_cls_getitem(PyObject *self, PyObject *arg)
Yury Selivanovf23746a2018-01-22 19:11:18 -05001028{
AMIR28c91632019-12-08 15:05:59 +03301029 Py_INCREF(self);
1030 return self;
Yury Selivanovf23746a2018-01-22 19:11:18 -05001031}
1032
Yury Selivanov41cb0ba2018-06-28 13:20:29 -04001033static PyMemberDef PyContextVar_members[] = {
1034 {"name", T_OBJECT, offsetof(PyContextVar, var_name), READONLY},
1035 {NULL}
1036};
Yury Selivanovf23746a2018-01-22 19:11:18 -05001037
1038static PyMethodDef PyContextVar_methods[] = {
1039 _CONTEXTVARS_CONTEXTVAR_GET_METHODDEF
1040 _CONTEXTVARS_CONTEXTVAR_SET_METHODDEF
1041 _CONTEXTVARS_CONTEXTVAR_RESET_METHODDEF
1042 {"__class_getitem__", contextvar_cls_getitem,
AMIR28c91632019-12-08 15:05:59 +03301043 METH_O | METH_CLASS, NULL},
Yury Selivanovf23746a2018-01-22 19:11:18 -05001044 {NULL, NULL}
1045};
1046
1047PyTypeObject PyContextVar_Type = {
1048 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1049 "ContextVar",
1050 sizeof(PyContextVar),
1051 .tp_methods = PyContextVar_methods,
Yury Selivanov41cb0ba2018-06-28 13:20:29 -04001052 .tp_members = PyContextVar_members,
Yury Selivanovf23746a2018-01-22 19:11:18 -05001053 .tp_dealloc = (destructor)contextvar_tp_dealloc,
1054 .tp_getattro = PyObject_GenericGetAttr,
1055 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1056 .tp_traverse = (traverseproc)contextvar_tp_traverse,
1057 .tp_clear = (inquiry)contextvar_tp_clear,
1058 .tp_new = contextvar_tp_new,
1059 .tp_free = PyObject_GC_Del,
1060 .tp_hash = (hashfunc)contextvar_tp_hash,
1061 .tp_repr = (reprfunc)contextvar_tp_repr,
1062};
1063
1064
1065/////////////////////////// Token
1066
1067static PyObject * get_token_missing(void);
1068
1069
1070/*[clinic input]
1071class _contextvars.Token "PyContextToken *" "&PyContextToken_Type"
1072[clinic start generated code]*/
1073/*[clinic end generated code: output=da39a3ee5e6b4b0d input=338a5e2db13d3f5b]*/
1074
1075
1076static PyObject *
1077token_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1078{
1079 PyErr_SetString(PyExc_RuntimeError,
1080 "Tokens can only be created by ContextVars");
1081 return NULL;
1082}
1083
1084static int
1085token_tp_clear(PyContextToken *self)
1086{
1087 Py_CLEAR(self->tok_ctx);
1088 Py_CLEAR(self->tok_var);
1089 Py_CLEAR(self->tok_oldval);
1090 return 0;
1091}
1092
1093static int
1094token_tp_traverse(PyContextToken *self, visitproc visit, void *arg)
1095{
1096 Py_VISIT(self->tok_ctx);
1097 Py_VISIT(self->tok_var);
1098 Py_VISIT(self->tok_oldval);
1099 return 0;
1100}
1101
1102static void
1103token_tp_dealloc(PyContextToken *self)
1104{
1105 PyObject_GC_UnTrack(self);
1106 (void)token_tp_clear(self);
1107 Py_TYPE(self)->tp_free(self);
1108}
1109
1110static PyObject *
1111token_tp_repr(PyContextToken *self)
1112{
1113 _PyUnicodeWriter writer;
1114
1115 _PyUnicodeWriter_Init(&writer);
1116
1117 if (_PyUnicodeWriter_WriteASCIIString(&writer, "<Token", 6) < 0) {
1118 goto error;
1119 }
1120
1121 if (self->tok_used) {
1122 if (_PyUnicodeWriter_WriteASCIIString(&writer, " used", 5) < 0) {
1123 goto error;
1124 }
1125 }
1126
1127 if (_PyUnicodeWriter_WriteASCIIString(&writer, " var=", 5) < 0) {
1128 goto error;
1129 }
1130
1131 PyObject *var = PyObject_Repr((PyObject *)self->tok_var);
1132 if (var == NULL) {
1133 goto error;
1134 }
1135 if (_PyUnicodeWriter_WriteStr(&writer, var) < 0) {
1136 Py_DECREF(var);
1137 goto error;
1138 }
1139 Py_DECREF(var);
1140
1141 PyObject *addr = PyUnicode_FromFormat(" at %p>", self);
1142 if (addr == NULL) {
1143 goto error;
1144 }
1145 if (_PyUnicodeWriter_WriteStr(&writer, addr) < 0) {
1146 Py_DECREF(addr);
1147 goto error;
1148 }
1149 Py_DECREF(addr);
1150
1151 return _PyUnicodeWriter_Finish(&writer);
1152
1153error:
1154 _PyUnicodeWriter_Dealloc(&writer);
1155 return NULL;
1156}
1157
1158static PyObject *
Serhiy Storchakad4f9cf52018-11-27 19:34:35 +02001159token_get_var(PyContextToken *self, void *Py_UNUSED(ignored))
Yury Selivanovf23746a2018-01-22 19:11:18 -05001160{
1161 Py_INCREF(self->tok_var);
1162 return (PyObject *)self->tok_var;
1163}
1164
1165static PyObject *
Serhiy Storchakad4f9cf52018-11-27 19:34:35 +02001166token_get_old_value(PyContextToken *self, void *Py_UNUSED(ignored))
Yury Selivanovf23746a2018-01-22 19:11:18 -05001167{
1168 if (self->tok_oldval == NULL) {
1169 return get_token_missing();
1170 }
1171
1172 Py_INCREF(self->tok_oldval);
1173 return self->tok_oldval;
1174}
1175
1176static PyGetSetDef PyContextTokenType_getsetlist[] = {
1177 {"var", (getter)token_get_var, NULL, NULL},
1178 {"old_value", (getter)token_get_old_value, NULL, NULL},
1179 {NULL}
1180};
1181
1182PyTypeObject PyContextToken_Type = {
1183 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1184 "Token",
1185 sizeof(PyContextToken),
1186 .tp_getset = PyContextTokenType_getsetlist,
1187 .tp_dealloc = (destructor)token_tp_dealloc,
1188 .tp_getattro = PyObject_GenericGetAttr,
1189 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1190 .tp_traverse = (traverseproc)token_tp_traverse,
1191 .tp_clear = (inquiry)token_tp_clear,
1192 .tp_new = token_tp_new,
1193 .tp_free = PyObject_GC_Del,
1194 .tp_hash = PyObject_HashNotImplemented,
1195 .tp_repr = (reprfunc)token_tp_repr,
1196};
1197
1198static PyContextToken *
1199token_new(PyContext *ctx, PyContextVar *var, PyObject *val)
1200{
1201 PyContextToken *tok = PyObject_GC_New(PyContextToken, &PyContextToken_Type);
1202 if (tok == NULL) {
1203 return NULL;
1204 }
1205
1206 Py_INCREF(ctx);
1207 tok->tok_ctx = ctx;
1208
1209 Py_INCREF(var);
1210 tok->tok_var = var;
1211
1212 Py_XINCREF(val);
1213 tok->tok_oldval = val;
1214
1215 tok->tok_used = 0;
1216
1217 PyObject_GC_Track(tok);
1218 return tok;
1219}
1220
1221
1222/////////////////////////// Token.MISSING
1223
1224
1225static PyObject *_token_missing;
1226
1227
1228typedef struct {
1229 PyObject_HEAD
1230} PyContextTokenMissing;
1231
1232
1233static PyObject *
1234context_token_missing_tp_repr(PyObject *self)
1235{
1236 return PyUnicode_FromString("<Token.MISSING>");
1237}
1238
1239
1240PyTypeObject PyContextTokenMissing_Type = {
1241 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1242 "Token.MISSING",
1243 sizeof(PyContextTokenMissing),
1244 .tp_getattro = PyObject_GenericGetAttr,
1245 .tp_flags = Py_TPFLAGS_DEFAULT,
1246 .tp_repr = context_token_missing_tp_repr,
1247};
1248
1249
1250static PyObject *
1251get_token_missing(void)
1252{
1253 if (_token_missing != NULL) {
1254 Py_INCREF(_token_missing);
1255 return _token_missing;
1256 }
1257
1258 _token_missing = (PyObject *)PyObject_New(
1259 PyContextTokenMissing, &PyContextTokenMissing_Type);
1260 if (_token_missing == NULL) {
1261 return NULL;
1262 }
1263
1264 Py_INCREF(_token_missing);
1265 return _token_missing;
1266}
1267
1268
1269///////////////////////////
1270
1271
1272int
1273PyContext_ClearFreeList(void)
1274{
Serhiy Storchakabfe4fd52018-02-09 17:31:26 +02001275 int size = ctx_freelist_len;
Yury Selivanovf23746a2018-01-22 19:11:18 -05001276 while (ctx_freelist_len) {
1277 PyContext *ctx = ctx_freelist;
1278 ctx_freelist = (PyContext *)ctx->ctx_weakreflist;
1279 ctx->ctx_weakreflist = NULL;
1280 PyObject_GC_Del(ctx);
1281 ctx_freelist_len--;
1282 }
1283 return size;
1284}
1285
1286
1287void
1288_PyContext_Fini(void)
1289{
1290 Py_CLEAR(_token_missing);
1291 (void)PyContext_ClearFreeList();
1292 (void)_PyHamt_Fini();
1293}
1294
1295
1296int
1297_PyContext_Init(void)
1298{
1299 if (!_PyHamt_Init()) {
1300 return 0;
1301 }
1302
1303 if ((PyType_Ready(&PyContext_Type) < 0) ||
1304 (PyType_Ready(&PyContextVar_Type) < 0) ||
1305 (PyType_Ready(&PyContextToken_Type) < 0) ||
1306 (PyType_Ready(&PyContextTokenMissing_Type) < 0))
1307 {
1308 return 0;
1309 }
1310
1311 PyObject *missing = get_token_missing();
1312 if (PyDict_SetItemString(
1313 PyContextToken_Type.tp_dict, "MISSING", missing))
1314 {
1315 Py_DECREF(missing);
1316 return 0;
1317 }
1318 Py_DECREF(missing);
1319
1320 return 1;
1321}