diff options
author | Stefan Krah <skrah@bytereef.org> | 2020-02-29 18:43:42 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-29 18:43:42 (GMT) |
commit | 815280eb160af637e1347213659f9236adf78f80 (patch) | |
tree | ecb6e85f7e8cec204316d3bfb82289a3724a173d /Modules/_decimal | |
parent | 0aeab5c4381f0cc11479362af2533b3a391312ac (diff) | |
download | cpython-815280eb160af637e1347213659f9236adf78f80.zip cpython-815280eb160af637e1347213659f9236adf78f80.tar.gz cpython-815280eb160af637e1347213659f9236adf78f80.tar.bz2 |
bpo-39794: Add --without-decimal-contextvar (#18702)
Diffstat (limited to 'Modules/_decimal')
-rw-r--r-- | Modules/_decimal/_decimal.c | 169 | ||||
-rwxr-xr-x | Modules/_decimal/tests/runall-memorydebugger.sh | 36 |
2 files changed, 176 insertions, 29 deletions
diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index e2f6ea1..617941b 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -122,7 +122,14 @@ incr_false(void) } +#ifndef WITH_DECIMAL_CONTEXTVAR +/* Key for thread state dictionary */ +static PyObject *tls_context_key = NULL; +/* Invariant: NULL or the most recently accessed thread local context */ +static PyDecContextObject *cached_context = NULL; +#else static PyObject *current_context_var; +#endif /* Template for creating new thread contexts, calling Context() without * arguments and initializing the module_context on first access. */ @@ -1217,6 +1224,12 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED) static void context_dealloc(PyDecContextObject *self) { +#ifndef WITH_DECIMAL_CONTEXTVAR + if (self == cached_context) { + cached_context = NULL; + } +#endif + Py_XDECREF(self->traps); Py_XDECREF(self->flags); Py_TYPE(self)->tp_free(self); @@ -1491,6 +1504,134 @@ static PyGetSetDef context_getsets [] = * operation. */ +#ifndef WITH_DECIMAL_CONTEXTVAR +/* Get the context from the thread state dictionary. */ +static PyObject * +current_context_from_dict(void) +{ + PyObject *dict; + PyObject *tl_context; + PyThreadState *tstate; + + dict = PyThreadState_GetDict(); + if (dict == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "cannot get thread state"); + return NULL; + } + + tl_context = PyDict_GetItemWithError(dict, tls_context_key); + if (tl_context != NULL) { + /* We already have a thread local context. */ + CONTEXT_CHECK(tl_context); + } + else { + if (PyErr_Occurred()) { + return NULL; + } + + /* Set up a new thread local context. */ + tl_context = context_copy(default_context_template, NULL); + if (tl_context == NULL) { + return NULL; + } + CTX(tl_context)->status = 0; + + if (PyDict_SetItem(dict, tls_context_key, tl_context) < 0) { + Py_DECREF(tl_context); + return NULL; + } + Py_DECREF(tl_context); + } + + /* Cache the context of the current thread, assuming that it + * will be accessed several times before a thread switch. */ + tstate = PyThreadState_GET(); + if (tstate) { + cached_context = (PyDecContextObject *)tl_context; + cached_context->tstate = tstate; + } + + /* Borrowed reference with refcount==1 */ + return tl_context; +} + +/* Return borrowed reference to thread local context. */ +static PyObject * +current_context(void) +{ + PyThreadState *tstate; + + tstate = PyThreadState_GET(); + if (cached_context && cached_context->tstate == tstate) { + return (PyObject *)cached_context; + } + + return current_context_from_dict(); +} + +/* ctxobj := borrowed reference to the current context */ +#define CURRENT_CONTEXT(ctxobj) \ + ctxobj = current_context(); \ + if (ctxobj == NULL) { \ + return NULL; \ + } + +/* Return a new reference to the current context */ +static PyObject * +PyDec_GetCurrentContext(PyObject *self UNUSED, PyObject *args UNUSED) +{ + PyObject *context; + + context = current_context(); + if (context == NULL) { + return NULL; + } + + Py_INCREF(context); + return context; +} + +/* Set the thread local context to a new context, decrement old reference */ +static PyObject * +PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v) +{ + PyObject *dict; + + CONTEXT_CHECK(v); + + dict = PyThreadState_GetDict(); + if (dict == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "cannot get thread state"); + return NULL; + } + + /* If the new context is one of the templates, make a copy. + * This is the current behavior of decimal.py. */ + if (v == default_context_template || + v == basic_context_template || + v == extended_context_template) { + v = context_copy(v, NULL); + if (v == NULL) { + return NULL; + } + CTX(v)->status = 0; + } + else { + Py_INCREF(v); + } + + cached_context = NULL; + if (PyDict_SetItem(dict, tls_context_key, v) < 0) { + Py_DECREF(v); + return NULL; + } + + Py_DECREF(v); + Py_RETURN_NONE; +} +#else static PyObject * init_current_context(void) { @@ -1570,6 +1711,7 @@ PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v) Py_RETURN_NONE; } +#endif /* Context manager object for the 'with' statement. The manager * owns one reference to the global (outer) context and one @@ -4388,15 +4530,8 @@ _dec_hash(PyDecObject *v) mpd_ssize_t exp; uint32_t status = 0; mpd_context_t maxctx; - PyObject *context; - context = current_context(); - if (context == NULL) { - return -1; - } - Py_DECREF(context); - if (mpd_isspecial(MPD(v))) { if (mpd_issnan(MPD(v))) { PyErr_SetString(PyExc_TypeError, @@ -5538,11 +5673,6 @@ PyInit__decimal(void) mpd_free = PyMem_Free; mpd_setminalloc(_Py_DEC_MINALLOC); - /* Init context variable */ - current_context_var = PyContextVar_New("decimal_context", NULL); - if (current_context_var == NULL) { - goto error; - } /* Init external C-API functions */ _py_long_multiply = PyLong_Type.tp_as_number->nb_multiply; @@ -5714,6 +5844,15 @@ PyInit__decimal(void) CHECK_INT(PyModule_AddObject(m, "DefaultContext", default_context_template)); +#ifndef WITH_DECIMAL_CONTEXTVAR + ASSIGN_PTR(tls_context_key, PyUnicode_FromString("___DECIMAL_CTX__")); + Py_INCREF(Py_False); + CHECK_INT(PyModule_AddObject(m, "HAVE_CONTEXTVAR", Py_False)); +#else + ASSIGN_PTR(current_context_var, PyContextVar_New("decimal_context", NULL)); + Py_INCREF(Py_True); + CHECK_INT(PyModule_AddObject(m, "HAVE_CONTEXTVAR", Py_True)); +#endif Py_INCREF(Py_True); CHECK_INT(PyModule_AddObject(m, "HAVE_THREADS", Py_True)); @@ -5773,9 +5912,13 @@ error: Py_CLEAR(SignalTuple); /* GCOV_NOT_REACHED */ Py_CLEAR(DecimalTuple); /* GCOV_NOT_REACHED */ Py_CLEAR(default_context_template); /* GCOV_NOT_REACHED */ +#ifndef WITH_DECIMAL_CONTEXTVAR + Py_CLEAR(tls_context_key); /* GCOV_NOT_REACHED */ +#else + Py_CLEAR(current_context_var); /* GCOV_NOT_REACHED */ +#endif Py_CLEAR(basic_context_template); /* GCOV_NOT_REACHED */ Py_CLEAR(extended_context_template); /* GCOV_NOT_REACHED */ - Py_CLEAR(current_context_var); /* GCOV_NOT_REACHED */ Py_CLEAR(m); /* GCOV_NOT_REACHED */ return NULL; /* GCOV_NOT_REACHED */ diff --git a/Modules/_decimal/tests/runall-memorydebugger.sh b/Modules/_decimal/tests/runall-memorydebugger.sh index 29b7723..7f3e527 100755 --- a/Modules/_decimal/tests/runall-memorydebugger.sh +++ b/Modules/_decimal/tests/runall-memorydebugger.sh @@ -1,8 +1,8 @@ #!/bin/sh # -# Purpose: test all machine configurations, pydebug, refleaks, release build -# and release build with valgrind. +# Purpose: test with and without contextvar, all machine configurations, pydebug, +# refleaks, release build and release build with valgrind. # # Synopsis: ./runall-memorydebugger.sh [--all-configs64 | --all-configs32] # @@ -57,7 +57,8 @@ print_config () cd .. # test_decimal: refleak, regular and Valgrind tests -for config in $CONFIGS; do +for args in "--without-decimal-contextvar" ""; do + for config in $CONFIGS; do unset PYTHON_DECIMAL_WITH_MACHINE libmpdec_config=$config @@ -69,12 +70,12 @@ for config in $CONFIGS; do fi ############ refleak tests ########### - print_config "refleak tests: config=$config" + print_config "refleak tests: config=$config" $args printf "\nbuilding python ...\n\n" cd ../../ $GMAKE distclean > /dev/null 2>&1 - ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" --with-pydebug > /dev/null 2>&1 + ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" --with-pydebug $args > /dev/null 2>&1 $GMAKE | grep _decimal printf "\n\n# ======================== refleak tests ===========================\n\n" @@ -82,11 +83,11 @@ for config in $CONFIGS; do ############ regular tests ########### - print_config "regular tests: config=$config" + print_config "regular tests: config=$config" $args printf "\nbuilding python ...\n\n" $GMAKE distclean > /dev/null 2>&1 - ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" > /dev/null 2>&1 + ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" $args > /dev/null 2>&1 $GMAKE | grep _decimal printf "\n\n# ======================== regular tests ===========================\n\n" @@ -103,21 +104,23 @@ for config in $CONFIGS; do esac esac - print_config "valgrind tests: config=$config" + print_config "valgrind tests: config=$config" $args printf "\nbuilding python ...\n\n" $GMAKE distclean > /dev/null 2>&1 - ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" --without-pymalloc > /dev/null 2>&1 + ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" --without-pymalloc $args > /dev/null 2>&1 $GMAKE | grep _decimal printf "\n\n# ======================== valgrind tests ===========================\n\n" $valgrind ./python -m test -uall test_decimal cd Modules/_decimal + done done # deccheck cd ../../ -for config in $CONFIGS; do +for args in "--without-decimal-contextvar" ""; do + for config in $CONFIGS; do unset PYTHON_DECIMAL_WITH_MACHINE if [ X"$config" != X"auto" ]; then @@ -126,22 +129,22 @@ for config in $CONFIGS; do fi ############ debug ############ - print_config "deccheck: config=$config --with-pydebug" + print_config "deccheck: config=$config --with-pydebug" $args printf "\nbuilding python ...\n\n" $GMAKE distclean > /dev/null 2>&1 - ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" --with-pydebug > /dev/null 2>&1 + ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" --with-pydebug $args > /dev/null 2>&1 $GMAKE | grep _decimal printf "\n\n# ========================== debug ===========================\n\n" ./python Modules/_decimal/tests/deccheck.py ########### regular ########### - print_config "deccheck: config=$config " + print_config "deccheck: config=$config" $args printf "\nbuilding python ...\n\n" $GMAKE distclean > /dev/null 2>&1 - ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" > /dev/null 2>&1 + ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" $args > /dev/null 2>&1 $GMAKE | grep _decimal printf "\n\n# ======================== regular ===========================\n\n" @@ -157,15 +160,16 @@ for config in $CONFIGS; do esac esac - print_config "valgrind deccheck: config=$config " + print_config "valgrind deccheck: config=$config" $args printf "\nbuilding python ...\n\n" $GMAKE distclean > /dev/null 2>&1 - ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" --without-pymalloc > /dev/null 2>&1 + ./configure CFLAGS="$ADD_CFLAGS" LDFLAGS="$ADD_LDFLAGS" --without-pymalloc $args > /dev/null 2>&1 $GMAKE | grep _decimal printf "\n\n# ======================== valgrind ==========================\n\n" $valgrind ./python Modules/_decimal/tests/deccheck.py + done done |