@@ -122,7 +122,14 @@ incr_false(void)
122122}
123123
124124
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
125131static PyObject * current_context_var ;
132+ #endif
126133
127134/* Template for creating new thread contexts, calling Context() without
128135 * arguments and initializing the module_context on first access. */
@@ -1217,6 +1224,12 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED)
12171224static void
12181225context_dealloc (PyDecContextObject * self )
12191226{
1227+ #ifndef WITH_DECIMAL_CONTEXTVAR
1228+ if (self == cached_context ) {
1229+ cached_context = NULL ;
1230+ }
1231+ #endif
1232+
12201233 Py_XDECREF (self -> traps );
12211234 Py_XDECREF (self -> flags );
12221235 Py_TYPE (self )-> tp_free (self );
@@ -1491,6 +1504,134 @@ static PyGetSetDef context_getsets [] =
14911504 * operation.
14921505 */
14931506
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
14941635static PyObject *
14951636init_current_context (void )
14961637{
@@ -1570,6 +1711,7 @@ PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v)
15701711
15711712 Py_RETURN_NONE ;
15721713}
1714+ #endif
15731715
15741716/* Context manager object for the 'with' statement. The manager
15751717 * owns one reference to the global (outer) context and one
@@ -4388,15 +4530,8 @@ _dec_hash(PyDecObject *v)
43884530 mpd_ssize_t exp ;
43894531 uint32_t status = 0 ;
43904532 mpd_context_t maxctx ;
4391- PyObject * context ;
43924533
43934534
4394- context = current_context ();
4395- if (context == NULL ) {
4396- return -1 ;
4397- }
4398- Py_DECREF (context );
4399-
44004535 if (mpd_isspecial (MPD (v ))) {
44014536 if (mpd_issnan (MPD (v ))) {
44024537 PyErr_SetString (PyExc_TypeError ,
@@ -5538,11 +5673,6 @@ PyInit__decimal(void)
55385673 mpd_free = PyMem_Free ;
55395674 mpd_setminalloc (_Py_DEC_MINALLOC );
55405675
5541- /* Init context variable */
5542- current_context_var = PyContextVar_New ("decimal_context" , NULL );
5543- if (current_context_var == NULL ) {
5544- goto error ;
5545- }
55465676
55475677 /* Init external C-API functions */
55485678 _py_long_multiply = PyLong_Type .tp_as_number -> nb_multiply ;
@@ -5714,6 +5844,15 @@ PyInit__decimal(void)
57145844 CHECK_INT (PyModule_AddObject (m , "DefaultContext" ,
57155845 default_context_template ));
57165846
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
57175856 Py_INCREF (Py_True );
57185857 CHECK_INT (PyModule_AddObject (m , "HAVE_THREADS" , Py_True ));
57195858
@@ -5773,9 +5912,13 @@ PyInit__decimal(void)
57735912 Py_CLEAR (SignalTuple ); /* GCOV_NOT_REACHED */
57745913 Py_CLEAR (DecimalTuple ); /* GCOV_NOT_REACHED */
57755914 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
57765920 Py_CLEAR (basic_context_template ); /* GCOV_NOT_REACHED */
57775921 Py_CLEAR (extended_context_template ); /* GCOV_NOT_REACHED */
5778- Py_CLEAR (current_context_var ); /* GCOV_NOT_REACHED */
57795922 Py_CLEAR (m ); /* GCOV_NOT_REACHED */
57805923
57815924 return NULL ; /* GCOV_NOT_REACHED */
0 commit comments