blob: 3cf8db4c90cdfef78e1e5fb4e40524758ba93243 [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"
Victor Stinnere5014be2020-04-14 17:52:15 +02004#include "pycore_gc.h" // _PyObject_GC_MAY_BE_TRACKED()
Victor Stinner27e2d1f2018-11-01 00:52:28 +01005#include "pycore_hamt.h"
Victor Stinnerbcda8f12018-11-21 22:27:47 +01006#include "pycore_object.h"
Victor Stinner7e433732019-11-08 10:05:17 +01007#include "pycore_pyerrors.h"
Victor Stinnere5014be2020-04-14 17:52:15 +02008#include "pycore_pystate.h" // _PyThreadState_GET()
Victor Stinner4a21e572020-04-15 02:35:41 +02009#include "structmember.h" // PyMemberDef
Yury Selivanovf23746a2018-01-22 19:11:18 -050010
11
12#define CONTEXT_FREELIST_MAXLEN 255
Yury Selivanovf23746a2018-01-22 19:11:18 -050013
14
15#include "clinic/context.c.h"
16/*[clinic input]
17module _contextvars
18[clinic start generated code]*/
19/*[clinic end generated code: output=da39a3ee5e6b4b0d input=a0955718c8b8cea6]*/
20
21
Yury Selivanov2ec872b2018-09-21 15:33:56 -040022#define ENSURE_Context(o, err_ret) \
23 if (!PyContext_CheckExact(o)) { \
24 PyErr_SetString(PyExc_TypeError, \
25 "an instance of Context was expected"); \
26 return err_ret; \
27 }
28
29#define ENSURE_ContextVar(o, err_ret) \
30 if (!PyContextVar_CheckExact(o)) { \
31 PyErr_SetString(PyExc_TypeError, \
32 "an instance of ContextVar was expected"); \
33 return err_ret; \
34 }
35
36#define ENSURE_ContextToken(o, err_ret) \
37 if (!PyContextToken_CheckExact(o)) { \
38 PyErr_SetString(PyExc_TypeError, \
39 "an instance of Token was expected"); \
40 return err_ret; \
41 }
42
43
Yury Selivanovf23746a2018-01-22 19:11:18 -050044/////////////////////////// Context API
45
46
47static PyContext *
48context_new_empty(void);
49
50static PyContext *
51context_new_from_vars(PyHamtObject *vars);
52
53static inline PyContext *
54context_get(void);
55
56static PyContextToken *
57token_new(PyContext *ctx, PyContextVar *var, PyObject *val);
58
59static PyContextVar *
60contextvar_new(PyObject *name, PyObject *def);
61
62static int
63contextvar_set(PyContextVar *var, PyObject *val);
64
65static int
66contextvar_del(PyContextVar *var);
67
68
69PyObject *
70_PyContext_NewHamtForTests(void)
71{
72 return (PyObject *)_PyHamt_New();
73}
74
75
Yury Selivanov2ec872b2018-09-21 15:33:56 -040076PyObject *
Yury Selivanovf23746a2018-01-22 19:11:18 -050077PyContext_New(void)
78{
Yury Selivanov2ec872b2018-09-21 15:33:56 -040079 return (PyObject *)context_new_empty();
Yury Selivanovf23746a2018-01-22 19:11:18 -050080}
81
82
Yury Selivanov2ec872b2018-09-21 15:33:56 -040083PyObject *
84PyContext_Copy(PyObject * octx)
Yury Selivanovf23746a2018-01-22 19:11:18 -050085{
Yury Selivanov2ec872b2018-09-21 15:33:56 -040086 ENSURE_Context(octx, NULL)
87 PyContext *ctx = (PyContext *)octx;
88 return (PyObject *)context_new_from_vars(ctx->ctx_vars);
Yury Selivanovf23746a2018-01-22 19:11:18 -050089}
90
91
Yury Selivanov2ec872b2018-09-21 15:33:56 -040092PyObject *
Yury Selivanovf23746a2018-01-22 19:11:18 -050093PyContext_CopyCurrent(void)
94{
95 PyContext *ctx = context_get();
96 if (ctx == NULL) {
97 return NULL;
98 }
99
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400100 return (PyObject *)context_new_from_vars(ctx->ctx_vars);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500101}
102
103
Victor Stinner7e433732019-11-08 10:05:17 +0100104static int
105_PyContext_Enter(PyThreadState *ts, PyObject *octx)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500106{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400107 ENSURE_Context(octx, -1)
108 PyContext *ctx = (PyContext *)octx;
109
Yury Selivanovf23746a2018-01-22 19:11:18 -0500110 if (ctx->ctx_entered) {
Victor Stinner7e433732019-11-08 10:05:17 +0100111 _PyErr_Format(ts, PyExc_RuntimeError,
112 "cannot enter context: %R is already entered", ctx);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500113 return -1;
114 }
115
Yury Selivanovf23746a2018-01-22 19:11:18 -0500116 ctx->ctx_prev = (PyContext *)ts->context; /* borrow */
117 ctx->ctx_entered = 1;
118
119 Py_INCREF(ctx);
120 ts->context = (PyObject *)ctx;
121 ts->context_ver++;
122
123 return 0;
124}
125
126
127int
Victor Stinner7e433732019-11-08 10:05:17 +0100128PyContext_Enter(PyObject *octx)
129{
130 PyThreadState *ts = _PyThreadState_GET();
131 assert(ts != NULL);
132 return _PyContext_Enter(ts, octx);
133}
134
135
136static int
137_PyContext_Exit(PyThreadState *ts, PyObject *octx)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500138{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400139 ENSURE_Context(octx, -1)
140 PyContext *ctx = (PyContext *)octx;
141
Yury Selivanovf23746a2018-01-22 19:11:18 -0500142 if (!ctx->ctx_entered) {
143 PyErr_Format(PyExc_RuntimeError,
144 "cannot exit context: %R has not been entered", ctx);
145 return -1;
146 }
147
Yury Selivanovf23746a2018-01-22 19:11:18 -0500148 if (ts->context != (PyObject *)ctx) {
149 /* Can only happen if someone misuses the C API */
150 PyErr_SetString(PyExc_RuntimeError,
151 "cannot exit context: thread state references "
152 "a different context object");
153 return -1;
154 }
155
156 Py_SETREF(ts->context, (PyObject *)ctx->ctx_prev);
157 ts->context_ver++;
158
159 ctx->ctx_prev = NULL;
160 ctx->ctx_entered = 0;
161
162 return 0;
163}
164
Victor Stinner7e433732019-11-08 10:05:17 +0100165int
166PyContext_Exit(PyObject *octx)
167{
168 PyThreadState *ts = _PyThreadState_GET();
169 assert(ts != NULL);
170 return _PyContext_Exit(ts, octx);
171}
172
Yury Selivanovf23746a2018-01-22 19:11:18 -0500173
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400174PyObject *
Yury Selivanovf23746a2018-01-22 19:11:18 -0500175PyContextVar_New(const char *name, PyObject *def)
176{
177 PyObject *pyname = PyUnicode_FromString(name);
178 if (pyname == NULL) {
179 return NULL;
180 }
Yury Selivanov6ab62922018-01-25 14:18:55 -0500181 PyContextVar *var = contextvar_new(pyname, def);
182 Py_DECREF(pyname);
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400183 return (PyObject *)var;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500184}
185
186
187int
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400188PyContextVar_Get(PyObject *ovar, PyObject *def, PyObject **val)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500189{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400190 ENSURE_ContextVar(ovar, -1)
191 PyContextVar *var = (PyContextVar *)ovar;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500192
Victor Stinner50b48572018-11-01 01:51:40 +0100193 PyThreadState *ts = _PyThreadState_GET();
Yury Selivanov226e5002018-01-26 17:24:52 -0500194 assert(ts != NULL);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500195 if (ts->context == NULL) {
196 goto not_found;
197 }
198
199 if (var->var_cached != NULL &&
200 var->var_cached_tsid == ts->id &&
201 var->var_cached_tsver == ts->context_ver)
202 {
203 *val = var->var_cached;
204 goto found;
205 }
206
207 assert(PyContext_CheckExact(ts->context));
208 PyHamtObject *vars = ((PyContext *)ts->context)->ctx_vars;
209
210 PyObject *found = NULL;
211 int res = _PyHamt_Find(vars, (PyObject*)var, &found);
212 if (res < 0) {
213 goto error;
214 }
215 if (res == 1) {
216 assert(found != NULL);
217 var->var_cached = found; /* borrow */
218 var->var_cached_tsid = ts->id;
219 var->var_cached_tsver = ts->context_ver;
220
221 *val = found;
222 goto found;
223 }
224
225not_found:
226 if (def == NULL) {
227 if (var->var_default != NULL) {
228 *val = var->var_default;
229 goto found;
230 }
231
232 *val = NULL;
233 goto found;
234 }
235 else {
236 *val = def;
237 goto found;
238 }
239
240found:
241 Py_XINCREF(*val);
242 return 0;
243
244error:
245 *val = NULL;
246 return -1;
247}
248
249
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400250PyObject *
251PyContextVar_Set(PyObject *ovar, PyObject *val)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500252{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400253 ENSURE_ContextVar(ovar, NULL)
254 PyContextVar *var = (PyContextVar *)ovar;
255
Yury Selivanovf23746a2018-01-22 19:11:18 -0500256 if (!PyContextVar_CheckExact(var)) {
257 PyErr_SetString(
258 PyExc_TypeError, "an instance of ContextVar was expected");
259 return NULL;
260 }
261
262 PyContext *ctx = context_get();
263 if (ctx == NULL) {
264 return NULL;
265 }
266
267 PyObject *old_val = NULL;
268 int found = _PyHamt_Find(ctx->ctx_vars, (PyObject *)var, &old_val);
269 if (found < 0) {
270 return NULL;
271 }
272
273 Py_XINCREF(old_val);
274 PyContextToken *tok = token_new(ctx, var, old_val);
275 Py_XDECREF(old_val);
276
277 if (contextvar_set(var, val)) {
278 Py_DECREF(tok);
279 return NULL;
280 }
281
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400282 return (PyObject *)tok;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500283}
284
285
286int
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400287PyContextVar_Reset(PyObject *ovar, PyObject *otok)
Yury Selivanovf23746a2018-01-22 19:11:18 -0500288{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400289 ENSURE_ContextVar(ovar, -1)
290 ENSURE_ContextToken(otok, -1)
291 PyContextVar *var = (PyContextVar *)ovar;
292 PyContextToken *tok = (PyContextToken *)otok;
293
Yury Selivanovf23746a2018-01-22 19:11:18 -0500294 if (tok->tok_used) {
295 PyErr_Format(PyExc_RuntimeError,
296 "%R has already been used once", tok);
297 return -1;
298 }
299
300 if (var != tok->tok_var) {
301 PyErr_Format(PyExc_ValueError,
302 "%R was created by a different ContextVar", tok);
303 return -1;
304 }
305
306 PyContext *ctx = context_get();
307 if (ctx != tok->tok_ctx) {
308 PyErr_Format(PyExc_ValueError,
309 "%R was created in a different Context", tok);
310 return -1;
311 }
312
313 tok->tok_used = 1;
314
315 if (tok->tok_oldval == NULL) {
316 return contextvar_del(var);
317 }
318 else {
319 return contextvar_set(var, tok->tok_oldval);
320 }
321}
322
323
324/////////////////////////// PyContext
325
326/*[clinic input]
327class _contextvars.Context "PyContext *" "&PyContext_Type"
328[clinic start generated code]*/
329/*[clinic end generated code: output=da39a3ee5e6b4b0d input=bdf87f8e0cb580e8]*/
330
331
332static inline PyContext *
333_context_alloc(void)
334{
Victor Stinnere005ead2020-06-05 02:56:37 +0200335 PyInterpreterState *interp = _PyInterpreterState_GET();
336 struct _Py_context_state *state = &interp->context;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500337 PyContext *ctx;
Victor Stinnere005ead2020-06-05 02:56:37 +0200338 if (state->numfree) {
339 state->numfree--;
340 ctx = state->freelist;
341 state->freelist = (PyContext *)ctx->ctx_weakreflist;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500342 ctx->ctx_weakreflist = NULL;
343 _Py_NewReference((PyObject *)ctx);
344 }
345 else {
346 ctx = PyObject_GC_New(PyContext, &PyContext_Type);
347 if (ctx == NULL) {
348 return NULL;
349 }
350 }
351
352 ctx->ctx_vars = NULL;
353 ctx->ctx_prev = NULL;
354 ctx->ctx_entered = 0;
355 ctx->ctx_weakreflist = NULL;
356
357 return ctx;
358}
359
360
361static PyContext *
362context_new_empty(void)
363{
364 PyContext *ctx = _context_alloc();
365 if (ctx == NULL) {
366 return NULL;
367 }
368
369 ctx->ctx_vars = _PyHamt_New();
370 if (ctx->ctx_vars == NULL) {
371 Py_DECREF(ctx);
372 return NULL;
373 }
374
375 _PyObject_GC_TRACK(ctx);
376 return ctx;
377}
378
379
380static PyContext *
381context_new_from_vars(PyHamtObject *vars)
382{
383 PyContext *ctx = _context_alloc();
384 if (ctx == NULL) {
385 return NULL;
386 }
387
388 Py_INCREF(vars);
389 ctx->ctx_vars = vars;
390
391 _PyObject_GC_TRACK(ctx);
392 return ctx;
393}
394
395
396static inline PyContext *
397context_get(void)
398{
Victor Stinner50b48572018-11-01 01:51:40 +0100399 PyThreadState *ts = _PyThreadState_GET();
Yury Selivanovbc4123b2018-01-27 13:24:20 -0500400 assert(ts != NULL);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500401 PyContext *current_ctx = (PyContext *)ts->context;
402 if (current_ctx == NULL) {
403 current_ctx = context_new_empty();
404 if (current_ctx == NULL) {
405 return NULL;
406 }
407 ts->context = (PyObject *)current_ctx;
408 }
409 return current_ctx;
410}
411
412static int
413context_check_key_type(PyObject *key)
414{
415 if (!PyContextVar_CheckExact(key)) {
416 // abort();
417 PyErr_Format(PyExc_TypeError,
418 "a ContextVar key was expected, got %R", key);
419 return -1;
420 }
421 return 0;
422}
423
424static PyObject *
425context_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
426{
427 if (PyTuple_Size(args) || (kwds != NULL && PyDict_Size(kwds))) {
428 PyErr_SetString(
429 PyExc_TypeError, "Context() does not accept any arguments");
430 return NULL;
431 }
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400432 return PyContext_New();
Yury Selivanovf23746a2018-01-22 19:11:18 -0500433}
434
435static int
436context_tp_clear(PyContext *self)
437{
438 Py_CLEAR(self->ctx_prev);
439 Py_CLEAR(self->ctx_vars);
440 return 0;
441}
442
443static int
444context_tp_traverse(PyContext *self, visitproc visit, void *arg)
445{
446 Py_VISIT(self->ctx_prev);
447 Py_VISIT(self->ctx_vars);
448 return 0;
449}
450
451static void
452context_tp_dealloc(PyContext *self)
453{
454 _PyObject_GC_UNTRACK(self);
455
456 if (self->ctx_weakreflist != NULL) {
457 PyObject_ClearWeakRefs((PyObject*)self);
458 }
459 (void)context_tp_clear(self);
460
Victor Stinnere005ead2020-06-05 02:56:37 +0200461 PyInterpreterState *interp = _PyInterpreterState_GET();
462 struct _Py_context_state *state = &interp->context;
463 if (state->numfree < CONTEXT_FREELIST_MAXLEN) {
464 state->numfree++;
465 self->ctx_weakreflist = (PyObject *)state->freelist;
466 state->freelist = self;
Yury Selivanovf23746a2018-01-22 19:11:18 -0500467 }
468 else {
469 Py_TYPE(self)->tp_free(self);
470 }
471}
472
473static PyObject *
474context_tp_iter(PyContext *self)
475{
476 return _PyHamt_NewIterKeys(self->ctx_vars);
477}
478
479static PyObject *
480context_tp_richcompare(PyObject *v, PyObject *w, int op)
481{
482 if (!PyContext_CheckExact(v) || !PyContext_CheckExact(w) ||
483 (op != Py_EQ && op != Py_NE))
484 {
485 Py_RETURN_NOTIMPLEMENTED;
486 }
487
488 int res = _PyHamt_Eq(
489 ((PyContext *)v)->ctx_vars, ((PyContext *)w)->ctx_vars);
490 if (res < 0) {
491 return NULL;
492 }
493
494 if (op == Py_NE) {
495 res = !res;
496 }
497
498 if (res) {
499 Py_RETURN_TRUE;
500 }
501 else {
502 Py_RETURN_FALSE;
503 }
504}
505
506static Py_ssize_t
507context_tp_len(PyContext *self)
508{
509 return _PyHamt_Len(self->ctx_vars);
510}
511
512static PyObject *
513context_tp_subscript(PyContext *self, PyObject *key)
514{
515 if (context_check_key_type(key)) {
516 return NULL;
517 }
518 PyObject *val = NULL;
519 int found = _PyHamt_Find(self->ctx_vars, key, &val);
520 if (found < 0) {
521 return NULL;
522 }
523 if (found == 0) {
524 PyErr_SetObject(PyExc_KeyError, key);
525 return NULL;
526 }
527 Py_INCREF(val);
528 return val;
529}
530
531static int
532context_tp_contains(PyContext *self, PyObject *key)
533{
534 if (context_check_key_type(key)) {
535 return -1;
536 }
537 PyObject *val = NULL;
538 return _PyHamt_Find(self->ctx_vars, key, &val);
539}
540
541
542/*[clinic input]
543_contextvars.Context.get
544 key: object
545 default: object = None
546 /
Peter Lamut20678fd2018-07-30 16:15:44 +0100547
548Return the value for `key` if `key` has the value in the context object.
549
550If `key` does not exist, return `default`. If `default` is not given,
551return None.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500552[clinic start generated code]*/
553
554static PyObject *
555_contextvars_Context_get_impl(PyContext *self, PyObject *key,
556 PyObject *default_value)
Peter Lamut20678fd2018-07-30 16:15:44 +0100557/*[clinic end generated code: output=0c54aa7664268189 input=c8eeb81505023995]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500558{
559 if (context_check_key_type(key)) {
560 return NULL;
561 }
562
563 PyObject *val = NULL;
564 int found = _PyHamt_Find(self->ctx_vars, key, &val);
565 if (found < 0) {
566 return NULL;
567 }
568 if (found == 0) {
569 Py_INCREF(default_value);
570 return default_value;
571 }
572 Py_INCREF(val);
573 return val;
574}
575
576
577/*[clinic input]
578_contextvars.Context.items
Peter Lamut20678fd2018-07-30 16:15:44 +0100579
580Return all variables and their values in the context object.
581
582The result is returned as a list of 2-tuples (variable, value).
Yury Selivanovf23746a2018-01-22 19:11:18 -0500583[clinic start generated code]*/
584
585static PyObject *
586_contextvars_Context_items_impl(PyContext *self)
Peter Lamut20678fd2018-07-30 16:15:44 +0100587/*[clinic end generated code: output=fa1655c8a08502af input=00db64ae379f9f42]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500588{
589 return _PyHamt_NewIterItems(self->ctx_vars);
590}
591
592
593/*[clinic input]
594_contextvars.Context.keys
Peter Lamut20678fd2018-07-30 16:15:44 +0100595
596Return a list of all variables in the context object.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500597[clinic start generated code]*/
598
599static PyObject *
600_contextvars_Context_keys_impl(PyContext *self)
Peter Lamut20678fd2018-07-30 16:15:44 +0100601/*[clinic end generated code: output=177227c6b63ec0e2 input=114b53aebca3449c]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500602{
603 return _PyHamt_NewIterKeys(self->ctx_vars);
604}
605
606
607/*[clinic input]
608_contextvars.Context.values
Peter Lamut20678fd2018-07-30 16:15:44 +0100609
animalize463572c2019-02-25 07:18:48 +0800610Return a list of all variables' values in the context object.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500611[clinic start generated code]*/
612
613static PyObject *
614_contextvars_Context_values_impl(PyContext *self)
animalize463572c2019-02-25 07:18:48 +0800615/*[clinic end generated code: output=d286dabfc8db6dde input=ce8075d04a6ea526]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500616{
617 return _PyHamt_NewIterValues(self->ctx_vars);
618}
619
620
621/*[clinic input]
622_contextvars.Context.copy
Peter Lamut20678fd2018-07-30 16:15:44 +0100623
624Return a shallow copy of the context object.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500625[clinic start generated code]*/
626
627static PyObject *
628_contextvars_Context_copy_impl(PyContext *self)
Peter Lamut20678fd2018-07-30 16:15:44 +0100629/*[clinic end generated code: output=30ba8896c4707a15 input=ebafdbdd9c72d592]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500630{
631 return (PyObject *)context_new_from_vars(self->ctx_vars);
632}
633
634
635static PyObject *
636context_run(PyContext *self, PyObject *const *args,
637 Py_ssize_t nargs, PyObject *kwnames)
638{
Victor Stinner7e433732019-11-08 10:05:17 +0100639 PyThreadState *ts = _PyThreadState_GET();
640
Yury Selivanovf23746a2018-01-22 19:11:18 -0500641 if (nargs < 1) {
Victor Stinner7e433732019-11-08 10:05:17 +0100642 _PyErr_SetString(ts, PyExc_TypeError,
643 "run() missing 1 required positional argument");
Yury Selivanovf23746a2018-01-22 19:11:18 -0500644 return NULL;
645 }
646
Victor Stinner7e433732019-11-08 10:05:17 +0100647 if (_PyContext_Enter(ts, (PyObject *)self)) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500648 return NULL;
649 }
650
Victor Stinner7e433732019-11-08 10:05:17 +0100651 PyObject *call_result = _PyObject_VectorcallTstate(
652 ts, args[0], args + 1, nargs - 1, kwnames);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500653
Victor Stinner7e433732019-11-08 10:05:17 +0100654 if (_PyContext_Exit(ts, (PyObject *)self)) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500655 return NULL;
656 }
657
658 return call_result;
659}
660
661
662static PyMethodDef PyContext_methods[] = {
663 _CONTEXTVARS_CONTEXT_GET_METHODDEF
664 _CONTEXTVARS_CONTEXT_ITEMS_METHODDEF
665 _CONTEXTVARS_CONTEXT_KEYS_METHODDEF
666 _CONTEXTVARS_CONTEXT_VALUES_METHODDEF
667 _CONTEXTVARS_CONTEXT_COPY_METHODDEF
Serhiy Storchaka62be7422018-11-27 13:27:31 +0200668 {"run", (PyCFunction)(void(*)(void))context_run, METH_FASTCALL | METH_KEYWORDS, NULL},
Yury Selivanovf23746a2018-01-22 19:11:18 -0500669 {NULL, NULL}
670};
671
672static PySequenceMethods PyContext_as_sequence = {
673 0, /* sq_length */
674 0, /* sq_concat */
675 0, /* sq_repeat */
676 0, /* sq_item */
677 0, /* sq_slice */
678 0, /* sq_ass_item */
679 0, /* sq_ass_slice */
680 (objobjproc)context_tp_contains, /* sq_contains */
681 0, /* sq_inplace_concat */
682 0, /* sq_inplace_repeat */
683};
684
685static PyMappingMethods PyContext_as_mapping = {
686 (lenfunc)context_tp_len, /* mp_length */
687 (binaryfunc)context_tp_subscript, /* mp_subscript */
688};
689
690PyTypeObject PyContext_Type = {
691 PyVarObject_HEAD_INIT(&PyType_Type, 0)
692 "Context",
693 sizeof(PyContext),
694 .tp_methods = PyContext_methods,
695 .tp_as_mapping = &PyContext_as_mapping,
696 .tp_as_sequence = &PyContext_as_sequence,
697 .tp_iter = (getiterfunc)context_tp_iter,
698 .tp_dealloc = (destructor)context_tp_dealloc,
699 .tp_getattro = PyObject_GenericGetAttr,
700 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
701 .tp_richcompare = context_tp_richcompare,
702 .tp_traverse = (traverseproc)context_tp_traverse,
703 .tp_clear = (inquiry)context_tp_clear,
704 .tp_new = context_tp_new,
705 .tp_weaklistoffset = offsetof(PyContext, ctx_weakreflist),
706 .tp_hash = PyObject_HashNotImplemented,
707};
708
709
710/////////////////////////// ContextVar
711
712
713static int
714contextvar_set(PyContextVar *var, PyObject *val)
715{
716 var->var_cached = NULL;
717 PyThreadState *ts = PyThreadState_Get();
718
719 PyContext *ctx = context_get();
720 if (ctx == NULL) {
721 return -1;
722 }
723
724 PyHamtObject *new_vars = _PyHamt_Assoc(
725 ctx->ctx_vars, (PyObject *)var, val);
726 if (new_vars == NULL) {
727 return -1;
728 }
729
730 Py_SETREF(ctx->ctx_vars, new_vars);
731
732 var->var_cached = val; /* borrow */
733 var->var_cached_tsid = ts->id;
734 var->var_cached_tsver = ts->context_ver;
735 return 0;
736}
737
738static int
739contextvar_del(PyContextVar *var)
740{
741 var->var_cached = NULL;
742
743 PyContext *ctx = context_get();
744 if (ctx == NULL) {
745 return -1;
746 }
747
748 PyHamtObject *vars = ctx->ctx_vars;
749 PyHamtObject *new_vars = _PyHamt_Without(vars, (PyObject *)var);
750 if (new_vars == NULL) {
751 return -1;
752 }
753
754 if (vars == new_vars) {
755 Py_DECREF(new_vars);
756 PyErr_SetObject(PyExc_LookupError, (PyObject *)var);
757 return -1;
758 }
759
760 Py_SETREF(ctx->ctx_vars, new_vars);
761 return 0;
762}
763
764static Py_hash_t
765contextvar_generate_hash(void *addr, PyObject *name)
766{
767 /* Take hash of `name` and XOR it with the object's addr.
768
769 The structure of the tree is encoded in objects' hashes, which
770 means that sufficiently similar hashes would result in tall trees
771 with many Collision nodes. Which would, in turn, result in slower
772 get and set operations.
773
774 The XORing helps to ensure that:
775
776 (1) sequentially allocated ContextVar objects have
777 different hashes;
778
779 (2) context variables with equal names have
780 different hashes.
781 */
782
783 Py_hash_t name_hash = PyObject_Hash(name);
784 if (name_hash == -1) {
785 return -1;
786 }
787
788 Py_hash_t res = _Py_HashPointer(addr) ^ name_hash;
789 return res == -1 ? -2 : res;
790}
791
792static PyContextVar *
793contextvar_new(PyObject *name, PyObject *def)
794{
795 if (!PyUnicode_Check(name)) {
796 PyErr_SetString(PyExc_TypeError,
797 "context variable name must be a str");
798 return NULL;
799 }
800
801 PyContextVar *var = PyObject_GC_New(PyContextVar, &PyContextVar_Type);
802 if (var == NULL) {
803 return NULL;
804 }
805
806 var->var_hash = contextvar_generate_hash(var, name);
807 if (var->var_hash == -1) {
808 Py_DECREF(var);
809 return NULL;
810 }
811
812 Py_INCREF(name);
813 var->var_name = name;
814
815 Py_XINCREF(def);
816 var->var_default = def;
817
818 var->var_cached = NULL;
819 var->var_cached_tsid = 0;
820 var->var_cached_tsver = 0;
821
Yury Selivanov6ab62922018-01-25 14:18:55 -0500822 if (_PyObject_GC_MAY_BE_TRACKED(name) ||
823 (def != NULL && _PyObject_GC_MAY_BE_TRACKED(def)))
Yury Selivanovf23746a2018-01-22 19:11:18 -0500824 {
825 PyObject_GC_Track(var);
826 }
827 return var;
828}
829
830
831/*[clinic input]
832class _contextvars.ContextVar "PyContextVar *" "&PyContextVar_Type"
833[clinic start generated code]*/
834/*[clinic end generated code: output=da39a3ee5e6b4b0d input=445da935fa8883c3]*/
835
836
837static PyObject *
838contextvar_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
839{
840 static char *kwlist[] = {"", "default", NULL};
841 PyObject *name;
842 PyObject *def = NULL;
843
844 if (!PyArg_ParseTupleAndKeywords(
845 args, kwds, "O|$O:ContextVar", kwlist, &name, &def))
846 {
847 return NULL;
848 }
849
850 return (PyObject *)contextvar_new(name, def);
851}
852
853static int
854contextvar_tp_clear(PyContextVar *self)
855{
856 Py_CLEAR(self->var_name);
857 Py_CLEAR(self->var_default);
858 self->var_cached = NULL;
859 self->var_cached_tsid = 0;
860 self->var_cached_tsver = 0;
861 return 0;
862}
863
864static int
865contextvar_tp_traverse(PyContextVar *self, visitproc visit, void *arg)
866{
867 Py_VISIT(self->var_name);
868 Py_VISIT(self->var_default);
869 return 0;
870}
871
872static void
873contextvar_tp_dealloc(PyContextVar *self)
874{
875 PyObject_GC_UnTrack(self);
876 (void)contextvar_tp_clear(self);
877 Py_TYPE(self)->tp_free(self);
878}
879
880static Py_hash_t
881contextvar_tp_hash(PyContextVar *self)
882{
883 return self->var_hash;
884}
885
886static PyObject *
887contextvar_tp_repr(PyContextVar *self)
888{
889 _PyUnicodeWriter writer;
890
891 _PyUnicodeWriter_Init(&writer);
892
893 if (_PyUnicodeWriter_WriteASCIIString(
894 &writer, "<ContextVar name=", 17) < 0)
895 {
896 goto error;
897 }
898
899 PyObject *name = PyObject_Repr(self->var_name);
900 if (name == NULL) {
901 goto error;
902 }
903 if (_PyUnicodeWriter_WriteStr(&writer, name) < 0) {
904 Py_DECREF(name);
905 goto error;
906 }
907 Py_DECREF(name);
908
909 if (self->var_default != NULL) {
910 if (_PyUnicodeWriter_WriteASCIIString(&writer, " default=", 9) < 0) {
911 goto error;
912 }
913
914 PyObject *def = PyObject_Repr(self->var_default);
915 if (def == NULL) {
916 goto error;
917 }
918 if (_PyUnicodeWriter_WriteStr(&writer, def) < 0) {
919 Py_DECREF(def);
920 goto error;
921 }
922 Py_DECREF(def);
923 }
924
925 PyObject *addr = PyUnicode_FromFormat(" at %p>", self);
926 if (addr == NULL) {
927 goto error;
928 }
929 if (_PyUnicodeWriter_WriteStr(&writer, addr) < 0) {
930 Py_DECREF(addr);
931 goto error;
932 }
933 Py_DECREF(addr);
934
935 return _PyUnicodeWriter_Finish(&writer);
936
937error:
938 _PyUnicodeWriter_Dealloc(&writer);
939 return NULL;
940}
941
942
943/*[clinic input]
944_contextvars.ContextVar.get
945 default: object = NULL
946 /
Peter Lamut20678fd2018-07-30 16:15:44 +0100947
948Return a value for the context variable for the current context.
949
950If there is no value for the variable in the current context, the method will:
951 * return the value of the default argument of the method, if provided; or
952 * return the default value for the context variable, if it was created
953 with one; or
954 * raise a LookupError.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500955[clinic start generated code]*/
956
957static PyObject *
958_contextvars_ContextVar_get_impl(PyContextVar *self, PyObject *default_value)
Peter Lamut20678fd2018-07-30 16:15:44 +0100959/*[clinic end generated code: output=0746bd0aa2ced7bf input=30aa2ab9e433e401]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500960{
961 if (!PyContextVar_CheckExact(self)) {
962 PyErr_SetString(
963 PyExc_TypeError, "an instance of ContextVar was expected");
964 return NULL;
965 }
966
967 PyObject *val;
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400968 if (PyContextVar_Get((PyObject *)self, default_value, &val) < 0) {
Yury Selivanovf23746a2018-01-22 19:11:18 -0500969 return NULL;
970 }
971
972 if (val == NULL) {
973 PyErr_SetObject(PyExc_LookupError, (PyObject *)self);
974 return NULL;
975 }
976
977 return val;
978}
979
980/*[clinic input]
981_contextvars.ContextVar.set
982 value: object
983 /
Peter Lamut20678fd2018-07-30 16:15:44 +0100984
985Call to set a new value for the context variable in the current context.
986
987The required value argument is the new value for the context variable.
988
989Returns a Token object that can be used to restore the variable to its previous
990value via the `ContextVar.reset()` method.
Yury Selivanovf23746a2018-01-22 19:11:18 -0500991[clinic start generated code]*/
992
993static PyObject *
994_contextvars_ContextVar_set(PyContextVar *self, PyObject *value)
Peter Lamut20678fd2018-07-30 16:15:44 +0100995/*[clinic end generated code: output=446ed5e820d6d60b input=c0a6887154227453]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -0500996{
Yury Selivanov2ec872b2018-09-21 15:33:56 -0400997 return PyContextVar_Set((PyObject *)self, value);
Yury Selivanovf23746a2018-01-22 19:11:18 -0500998}
999
1000/*[clinic input]
1001_contextvars.ContextVar.reset
1002 token: object
1003 /
Peter Lamut20678fd2018-07-30 16:15:44 +01001004
1005Reset the context variable.
1006
1007The variable is reset to the value it had before the `ContextVar.set()` that
1008created the token was used.
Yury Selivanovf23746a2018-01-22 19:11:18 -05001009[clinic start generated code]*/
1010
1011static PyObject *
1012_contextvars_ContextVar_reset(PyContextVar *self, PyObject *token)
Peter Lamut20678fd2018-07-30 16:15:44 +01001013/*[clinic end generated code: output=d4ee34d0742d62ee input=ebe2881e5af4ffda]*/
Yury Selivanovf23746a2018-01-22 19:11:18 -05001014{
1015 if (!PyContextToken_CheckExact(token)) {
1016 PyErr_Format(PyExc_TypeError,
1017 "expected an instance of Token, got %R", token);
1018 return NULL;
1019 }
1020
Yury Selivanov2ec872b2018-09-21 15:33:56 -04001021 if (PyContextVar_Reset((PyObject *)self, token)) {
Yury Selivanovf23746a2018-01-22 19:11:18 -05001022 return NULL;
1023 }
1024
1025 Py_RETURN_NONE;
1026}
1027
1028
Yury Selivanov41cb0ba2018-06-28 13:20:29 -04001029static PyMemberDef PyContextVar_members[] = {
1030 {"name", T_OBJECT, offsetof(PyContextVar, var_name), READONLY},
1031 {NULL}
1032};
Yury Selivanovf23746a2018-01-22 19:11:18 -05001033
1034static PyMethodDef PyContextVar_methods[] = {
1035 _CONTEXTVARS_CONTEXTVAR_GET_METHODDEF
1036 _CONTEXTVARS_CONTEXTVAR_SET_METHODDEF
1037 _CONTEXTVARS_CONTEXTVAR_RESET_METHODDEF
Ethan Smithd01628e2020-04-14 16:14:15 -07001038 {"__class_getitem__", (PyCFunction)Py_GenericAlias,
1039 METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
Yury Selivanovf23746a2018-01-22 19:11:18 -05001040 {NULL, NULL}
1041};
1042
1043PyTypeObject PyContextVar_Type = {
1044 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1045 "ContextVar",
1046 sizeof(PyContextVar),
1047 .tp_methods = PyContextVar_methods,
Yury Selivanov41cb0ba2018-06-28 13:20:29 -04001048 .tp_members = PyContextVar_members,
Yury Selivanovf23746a2018-01-22 19:11:18 -05001049 .tp_dealloc = (destructor)contextvar_tp_dealloc,
1050 .tp_getattro = PyObject_GenericGetAttr,
1051 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1052 .tp_traverse = (traverseproc)contextvar_tp_traverse,
1053 .tp_clear = (inquiry)contextvar_tp_clear,
1054 .tp_new = contextvar_tp_new,
1055 .tp_free = PyObject_GC_Del,
1056 .tp_hash = (hashfunc)contextvar_tp_hash,
1057 .tp_repr = (reprfunc)contextvar_tp_repr,
1058};
1059
1060
1061/////////////////////////// Token
1062
1063static PyObject * get_token_missing(void);
1064
1065
1066/*[clinic input]
1067class _contextvars.Token "PyContextToken *" "&PyContextToken_Type"
1068[clinic start generated code]*/
1069/*[clinic end generated code: output=da39a3ee5e6b4b0d input=338a5e2db13d3f5b]*/
1070
1071
1072static PyObject *
1073token_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1074{
1075 PyErr_SetString(PyExc_RuntimeError,
1076 "Tokens can only be created by ContextVars");
1077 return NULL;
1078}
1079
1080static int
1081token_tp_clear(PyContextToken *self)
1082{
1083 Py_CLEAR(self->tok_ctx);
1084 Py_CLEAR(self->tok_var);
1085 Py_CLEAR(self->tok_oldval);
1086 return 0;
1087}
1088
1089static int
1090token_tp_traverse(PyContextToken *self, visitproc visit, void *arg)
1091{
1092 Py_VISIT(self->tok_ctx);
1093 Py_VISIT(self->tok_var);
1094 Py_VISIT(self->tok_oldval);
1095 return 0;
1096}
1097
1098static void
1099token_tp_dealloc(PyContextToken *self)
1100{
1101 PyObject_GC_UnTrack(self);
1102 (void)token_tp_clear(self);
1103 Py_TYPE(self)->tp_free(self);
1104}
1105
1106static PyObject *
1107token_tp_repr(PyContextToken *self)
1108{
1109 _PyUnicodeWriter writer;
1110
1111 _PyUnicodeWriter_Init(&writer);
1112
1113 if (_PyUnicodeWriter_WriteASCIIString(&writer, "<Token", 6) < 0) {
1114 goto error;
1115 }
1116
1117 if (self->tok_used) {
1118 if (_PyUnicodeWriter_WriteASCIIString(&writer, " used", 5) < 0) {
1119 goto error;
1120 }
1121 }
1122
1123 if (_PyUnicodeWriter_WriteASCIIString(&writer, " var=", 5) < 0) {
1124 goto error;
1125 }
1126
1127 PyObject *var = PyObject_Repr((PyObject *)self->tok_var);
1128 if (var == NULL) {
1129 goto error;
1130 }
1131 if (_PyUnicodeWriter_WriteStr(&writer, var) < 0) {
1132 Py_DECREF(var);
1133 goto error;
1134 }
1135 Py_DECREF(var);
1136
1137 PyObject *addr = PyUnicode_FromFormat(" at %p>", self);
1138 if (addr == NULL) {
1139 goto error;
1140 }
1141 if (_PyUnicodeWriter_WriteStr(&writer, addr) < 0) {
1142 Py_DECREF(addr);
1143 goto error;
1144 }
1145 Py_DECREF(addr);
1146
1147 return _PyUnicodeWriter_Finish(&writer);
1148
1149error:
1150 _PyUnicodeWriter_Dealloc(&writer);
1151 return NULL;
1152}
1153
1154static PyObject *
Serhiy Storchakad4f9cf52018-11-27 19:34:35 +02001155token_get_var(PyContextToken *self, void *Py_UNUSED(ignored))
Yury Selivanovf23746a2018-01-22 19:11:18 -05001156{
1157 Py_INCREF(self->tok_var);
1158 return (PyObject *)self->tok_var;
1159}
1160
1161static PyObject *
Serhiy Storchakad4f9cf52018-11-27 19:34:35 +02001162token_get_old_value(PyContextToken *self, void *Py_UNUSED(ignored))
Yury Selivanovf23746a2018-01-22 19:11:18 -05001163{
1164 if (self->tok_oldval == NULL) {
1165 return get_token_missing();
1166 }
1167
1168 Py_INCREF(self->tok_oldval);
1169 return self->tok_oldval;
1170}
1171
1172static PyGetSetDef PyContextTokenType_getsetlist[] = {
1173 {"var", (getter)token_get_var, NULL, NULL},
1174 {"old_value", (getter)token_get_old_value, NULL, NULL},
1175 {NULL}
1176};
1177
Ethan Smithd01628e2020-04-14 16:14:15 -07001178static PyMethodDef PyContextTokenType_methods[] = {
1179 {"__class_getitem__", (PyCFunction)Py_GenericAlias,
1180 METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
1181 {NULL}
1182};
1183
Yury Selivanovf23746a2018-01-22 19:11:18 -05001184PyTypeObject PyContextToken_Type = {
1185 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1186 "Token",
1187 sizeof(PyContextToken),
Ethan Smithd01628e2020-04-14 16:14:15 -07001188 .tp_methods = PyContextTokenType_methods,
Yury Selivanovf23746a2018-01-22 19:11:18 -05001189 .tp_getset = PyContextTokenType_getsetlist,
1190 .tp_dealloc = (destructor)token_tp_dealloc,
1191 .tp_getattro = PyObject_GenericGetAttr,
1192 .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
1193 .tp_traverse = (traverseproc)token_tp_traverse,
1194 .tp_clear = (inquiry)token_tp_clear,
1195 .tp_new = token_tp_new,
1196 .tp_free = PyObject_GC_Del,
1197 .tp_hash = PyObject_HashNotImplemented,
1198 .tp_repr = (reprfunc)token_tp_repr,
1199};
1200
1201static PyContextToken *
1202token_new(PyContext *ctx, PyContextVar *var, PyObject *val)
1203{
1204 PyContextToken *tok = PyObject_GC_New(PyContextToken, &PyContextToken_Type);
1205 if (tok == NULL) {
1206 return NULL;
1207 }
1208
1209 Py_INCREF(ctx);
1210 tok->tok_ctx = ctx;
1211
1212 Py_INCREF(var);
1213 tok->tok_var = var;
1214
1215 Py_XINCREF(val);
1216 tok->tok_oldval = val;
1217
1218 tok->tok_used = 0;
1219
1220 PyObject_GC_Track(tok);
1221 return tok;
1222}
1223
1224
1225/////////////////////////// Token.MISSING
1226
1227
1228static PyObject *_token_missing;
1229
1230
1231typedef struct {
1232 PyObject_HEAD
1233} PyContextTokenMissing;
1234
1235
1236static PyObject *
1237context_token_missing_tp_repr(PyObject *self)
1238{
1239 return PyUnicode_FromString("<Token.MISSING>");
1240}
1241
1242
1243PyTypeObject PyContextTokenMissing_Type = {
1244 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1245 "Token.MISSING",
1246 sizeof(PyContextTokenMissing),
1247 .tp_getattro = PyObject_GenericGetAttr,
1248 .tp_flags = Py_TPFLAGS_DEFAULT,
1249 .tp_repr = context_token_missing_tp_repr,
1250};
1251
1252
1253static PyObject *
1254get_token_missing(void)
1255{
1256 if (_token_missing != NULL) {
1257 Py_INCREF(_token_missing);
1258 return _token_missing;
1259 }
1260
1261 _token_missing = (PyObject *)PyObject_New(
1262 PyContextTokenMissing, &PyContextTokenMissing_Type);
1263 if (_token_missing == NULL) {
1264 return NULL;
1265 }
1266
1267 Py_INCREF(_token_missing);
1268 return _token_missing;
1269}
1270
1271
1272///////////////////////////
1273
1274
Victor Stinnerae00a5a2020-04-29 02:29:20 +02001275void
Victor Stinnere005ead2020-06-05 02:56:37 +02001276_PyContext_ClearFreeList(PyThreadState *tstate)
Yury Selivanovf23746a2018-01-22 19:11:18 -05001277{
Victor Stinnere005ead2020-06-05 02:56:37 +02001278 struct _Py_context_state *state = &tstate->interp->context;
1279 for (; state->numfree; state->numfree--) {
1280 PyContext *ctx = state->freelist;
1281 state->freelist = (PyContext *)ctx->ctx_weakreflist;
Yury Selivanovf23746a2018-01-22 19:11:18 -05001282 ctx->ctx_weakreflist = NULL;
1283 PyObject_GC_Del(ctx);
Yury Selivanovf23746a2018-01-22 19:11:18 -05001284 }
Yury Selivanovf23746a2018-01-22 19:11:18 -05001285}
1286
1287
1288void
Victor Stinnere005ead2020-06-05 02:56:37 +02001289_PyContext_Fini(PyThreadState *tstate)
Yury Selivanovf23746a2018-01-22 19:11:18 -05001290{
1291 Py_CLEAR(_token_missing);
Victor Stinnere005ead2020-06-05 02:56:37 +02001292 _PyContext_ClearFreeList(tstate);
Victor Stinnerae00a5a2020-04-29 02:29:20 +02001293 _PyHamt_Fini();
Yury Selivanovf23746a2018-01-22 19:11:18 -05001294}
1295
1296
1297int
1298_PyContext_Init(void)
1299{
1300 if (!_PyHamt_Init()) {
1301 return 0;
1302 }
1303
1304 if ((PyType_Ready(&PyContext_Type) < 0) ||
1305 (PyType_Ready(&PyContextVar_Type) < 0) ||
1306 (PyType_Ready(&PyContextToken_Type) < 0) ||
1307 (PyType_Ready(&PyContextTokenMissing_Type) < 0))
1308 {
1309 return 0;
1310 }
1311
1312 PyObject *missing = get_token_missing();
1313 if (PyDict_SetItemString(
1314 PyContextToken_Type.tp_dict, "MISSING", missing))
1315 {
1316 Py_DECREF(missing);
1317 return 0;
1318 }
1319 Py_DECREF(missing);
1320
1321 return 1;
1322}