Coverage Report

Created: 2022-07-08 09:39

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