@@ -122,7 +122,14 @@ incr_false(void)
122
122
}
123
123
124
124
125
+ #ifndef WITH_DECIMAL_CONTEXTVAR
126
+ /* Key for thread state dictionary */
127
+ static PyObject * tls_context_key = NULL ;
128
+ /* Invariant: NULL or the most recently accessed thread local context */
129
+ static PyDecContextObject * cached_context = NULL ;
130
+ #else
125
131
static PyObject * current_context_var ;
132
+ #endif
126
133
127
134
/* Template for creating new thread contexts, calling Context() without
128
135
* arguments and initializing the module_context on first access. */
@@ -1217,6 +1224,12 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED)
1217
1224
static void
1218
1225
context_dealloc (PyDecContextObject * self )
1219
1226
{
1227
+ #ifndef WITH_DECIMAL_CONTEXTVAR
1228
+ if (self == cached_context ) {
1229
+ cached_context = NULL ;
1230
+ }
1231
+ #endif
1232
+
1220
1233
Py_XDECREF (self -> traps );
1221
1234
Py_XDECREF (self -> flags );
1222
1235
Py_TYPE (self )-> tp_free (self );
@@ -1491,6 +1504,134 @@ static PyGetSetDef context_getsets [] =
1491
1504
* operation.
1492
1505
*/
1493
1506
1507
+ #ifndef WITH_DECIMAL_CONTEXTVAR
1508
+ /* Get the context from the thread state dictionary. */
1509
+ static PyObject *
1510
+ current_context_from_dict (void )
1511
+ {
1512
+ PyObject * dict ;
1513
+ PyObject * tl_context ;
1514
+ PyThreadState * tstate ;
1515
+
1516
+ dict = PyThreadState_GetDict ();
1517
+ if (dict == NULL ) {
1518
+ PyErr_SetString (PyExc_RuntimeError ,
1519
+ "cannot get thread state" );
1520
+ return NULL ;
1521
+ }
1522
+
1523
+ tl_context = PyDict_GetItemWithError (dict , tls_context_key );
1524
+ if (tl_context != NULL ) {
1525
+ /* We already have a thread local context. */
1526
+ CONTEXT_CHECK (tl_context );
1527
+ }
1528
+ else {
1529
+ if (PyErr_Occurred ()) {
1530
+ return NULL ;
1531
+ }
1532
+
1533
+ /* Set up a new thread local context. */
1534
+ tl_context = context_copy (default_context_template , NULL );
1535
+ if (tl_context == NULL ) {
1536
+ return NULL ;
1537
+ }
1538
+ CTX (tl_context )-> status = 0 ;
1539
+
1540
+ if (PyDict_SetItem (dict , tls_context_key , tl_context ) < 0 ) {
1541
+ Py_DECREF (tl_context );
1542
+ return NULL ;
1543
+ }
1544
+ Py_DECREF (tl_context );
1545
+ }
1546
+
1547
+ /* Cache the context of the current thread, assuming that it
1548
+ * will be accessed several times before a thread switch. */
1549
+ tstate = PyThreadState_GET ();
1550
+ if (tstate ) {
1551
+ cached_context = (PyDecContextObject * )tl_context ;
1552
+ cached_context -> tstate = tstate ;
1553
+ }
1554
+
1555
+ /* Borrowed reference with refcount==1 */
1556
+ return tl_context ;
1557
+ }
1558
+
1559
+ /* Return borrowed reference to thread local context. */
1560
+ static PyObject *
1561
+ current_context (void )
1562
+ {
1563
+ PyThreadState * tstate ;
1564
+
1565
+ tstate = PyThreadState_GET ();
1566
+ if (cached_context && cached_context -> tstate == tstate ) {
1567
+ return (PyObject * )cached_context ;
1568
+ }
1569
+
1570
+ return current_context_from_dict ();
1571
+ }
1572
+
1573
+ /* ctxobj := borrowed reference to the current context */
1574
+ #define CURRENT_CONTEXT (ctxobj ) \
1575
+ ctxobj = current_context(); \
1576
+ if (ctxobj == NULL) { \
1577
+ return NULL; \
1578
+ }
1579
+
1580
+ /* Return a new reference to the current context */
1581
+ static PyObject *
1582
+ PyDec_GetCurrentContext (PyObject * self UNUSED , PyObject * args UNUSED )
1583
+ {
1584
+ PyObject * context ;
1585
+
1586
+ context = current_context ();
1587
+ if (context == NULL ) {
1588
+ return NULL ;
1589
+ }
1590
+
1591
+ Py_INCREF (context );
1592
+ return context ;
1593
+ }
1594
+
1595
+ /* Set the thread local context to a new context, decrement old reference */
1596
+ static PyObject *
1597
+ PyDec_SetCurrentContext (PyObject * self UNUSED , PyObject * v )
1598
+ {
1599
+ PyObject * dict ;
1600
+
1601
+ CONTEXT_CHECK (v );
1602
+
1603
+ dict = PyThreadState_GetDict ();
1604
+ if (dict == NULL ) {
1605
+ PyErr_SetString (PyExc_RuntimeError ,
1606
+ "cannot get thread state" );
1607
+ return NULL ;
1608
+ }
1609
+
1610
+ /* If the new context is one of the templates, make a copy.
1611
+ * This is the current behavior of decimal.py. */
1612
+ if (v == default_context_template ||
1613
+ v == basic_context_template ||
1614
+ v == extended_context_template ) {
1615
+ v = context_copy (v , NULL );
1616
+ if (v == NULL ) {
1617
+ return NULL ;
1618
+ }
1619
+ CTX (v )-> status = 0 ;
1620
+ }
1621
+ else {
1622
+ Py_INCREF (v );
1623
+ }
1624
+
1625
+ cached_context = NULL ;
1626
+ if (PyDict_SetItem (dict , tls_context_key , v ) < 0 ) {
1627
+ Py_DECREF (v );
1628
+ return NULL ;
1629
+ }
1630
+
1631
+ Py_DECREF (v );
1632
+ Py_RETURN_NONE ;
1633
+ }
1634
+ #else
1494
1635
static PyObject *
1495
1636
init_current_context (void )
1496
1637
{
@@ -1570,6 +1711,7 @@ PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v)
1570
1711
1571
1712
Py_RETURN_NONE ;
1572
1713
}
1714
+ #endif
1573
1715
1574
1716
/* Context manager object for the 'with' statement. The manager
1575
1717
* owns one reference to the global (outer) context and one
@@ -4388,15 +4530,8 @@ _dec_hash(PyDecObject *v)
4388
4530
mpd_ssize_t exp ;
4389
4531
uint32_t status = 0 ;
4390
4532
mpd_context_t maxctx ;
4391
- PyObject * context ;
4392
4533
4393
4534
4394
- context = current_context ();
4395
- if (context == NULL ) {
4396
- return -1 ;
4397
- }
4398
- Py_DECREF (context );
4399
-
4400
4535
if (mpd_isspecial (MPD (v ))) {
4401
4536
if (mpd_issnan (MPD (v ))) {
4402
4537
PyErr_SetString (PyExc_TypeError ,
@@ -5538,11 +5673,6 @@ PyInit__decimal(void)
5538
5673
mpd_free = PyMem_Free ;
5539
5674
mpd_setminalloc (_Py_DEC_MINALLOC );
5540
5675
5541
- /* Init context variable */
5542
- current_context_var = PyContextVar_New ("decimal_context" , NULL );
5543
- if (current_context_var == NULL ) {
5544
- goto error ;
5545
- }
5546
5676
5547
5677
/* Init external C-API functions */
5548
5678
_py_long_multiply = PyLong_Type .tp_as_number -> nb_multiply ;
@@ -5714,6 +5844,15 @@ PyInit__decimal(void)
5714
5844
CHECK_INT (PyModule_AddObject (m , "DefaultContext" ,
5715
5845
default_context_template ));
5716
5846
5847
+ #ifndef WITH_DECIMAL_CONTEXTVAR
5848
+ ASSIGN_PTR (tls_context_key , PyUnicode_FromString ("___DECIMAL_CTX__" ));
5849
+ Py_INCREF (Py_False );
5850
+ CHECK_INT (PyModule_AddObject (m , "HAVE_CONTEXTVAR" , Py_False ));
5851
+ #else
5852
+ ASSIGN_PTR (current_context_var , PyContextVar_New ("decimal_context" , NULL ));
5853
+ Py_INCREF (Py_True );
5854
+ CHECK_INT (PyModule_AddObject (m , "HAVE_CONTEXTVAR" , Py_True ));
5855
+ #endif
5717
5856
Py_INCREF (Py_True );
5718
5857
CHECK_INT (PyModule_AddObject (m , "HAVE_THREADS" , Py_True ));
5719
5858
@@ -5773,9 +5912,13 @@ PyInit__decimal(void)
5773
5912
Py_CLEAR (SignalTuple ); /* GCOV_NOT_REACHED */
5774
5913
Py_CLEAR (DecimalTuple ); /* GCOV_NOT_REACHED */
5775
5914
Py_CLEAR (default_context_template ); /* GCOV_NOT_REACHED */
5915
+ #ifndef WITH_DECIMAL_CONTEXTVAR
5916
+ Py_CLEAR (tls_context_key ); /* GCOV_NOT_REACHED */
5917
+ #else
5918
+ Py_CLEAR (current_context_var ); /* GCOV_NOT_REACHED */
5919
+ #endif
5776
5920
Py_CLEAR (basic_context_template ); /* GCOV_NOT_REACHED */
5777
5921
Py_CLEAR (extended_context_template ); /* GCOV_NOT_REACHED */
5778
- Py_CLEAR (current_context_var ); /* GCOV_NOT_REACHED */
5779
5922
Py_CLEAR (m ); /* GCOV_NOT_REACHED */
5780
5923
5781
5924
return NULL ; /* GCOV_NOT_REACHED */
0 commit comments