Coverage Report

Created: 2025-07-11 06:59

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