[Python-checkins] gh-106078: Convert `_decimal` types to heap types (#106079)

erlend-aasland webhook-mailer at python.org
Thu Jun 29 06:27:24 EDT 2023


https://github.com/python/cpython/commit/fb0d9b9ac1ec3ea13fae8b8ef6a4f0a5a80482b3
commit: fb0d9b9ac1ec3ea13fae8b8ef6a4f0a5a80482b3
branch: main
author: Charlie Zhao <zhaoyu_hit at qq.com>
committer: erlend-aasland <erlend.aasland at protonmail.com>
date: 2023-06-29T10:27:20Z
summary:

gh-106078: Convert `_decimal` types to heap types (#106079)

- Establish global state struct
- Convert static types to heap types and add them to global state:
    * PyDecContextManager_Type
    * PyDecContext_Type
    * PyDecSignalDictMixin_Type
    * PyDec_Type
- Add to global state:
    * PyDecSignalDict_Type
    * DecimalTuple

Co-authored-by: Kumar Aditya <59607654+kumaraditya303 at users.noreply.github.com>
Co-authored-by: Erlend E. Aasland <erlend at python.org>

files:
M Lib/test/test_decimal.py
M Modules/_decimal/_decimal.c
M Tools/c-analyzer/cpython/globals-to-fix.tsv

diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py
index 749496e3e9455..db67f37608f1f 100644
--- a/Lib/test/test_decimal.py
+++ b/Lib/test/test_decimal.py
@@ -34,7 +34,8 @@
 import locale
 from test.support import (is_resource_enabled,
                           requires_IEEE_754, requires_docstrings,
-                          requires_legacy_unicode_capi, check_sanitizer)
+                          requires_legacy_unicode_capi, check_sanitizer,
+                          check_disallow_instantiation)
 from test.support import (TestFailed,
                           run_with_locale, cpython_only,
                           darwin_malloc_err_warning, is_emscripten)
@@ -5681,6 +5682,24 @@ def test_maxcontext_exact_arith(self):
             self.assertEqual(Decimal(4) / 2, 2)
             self.assertEqual(Decimal(400) ** -1, Decimal('0.0025'))
 
+    def test_c_immutable_types(self):
+        SignalDict = type(C.Context().flags)
+        SignalDictMixin = SignalDict.__bases__[0]
+        ContextManager = type(C.localcontext())
+        types = (
+            SignalDictMixin,
+            ContextManager,
+            C.Decimal,
+            C.Context,
+        )
+        for tp in types:
+            with self.subTest(tp=tp):
+                with self.assertRaisesRegex(TypeError, "immutable"):
+                    tp.foo = 1
+
+    def test_c_disallow_instantiation(self):
+        ContextManager = type(C.localcontext())
+        check_disallow_instantiation(self, ContextManager)
 
 @requires_docstrings
 @requires_cdecimal
diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c
index 73df3f34829f0..b7cb19515b300 100644
--- a/Modules/_decimal/_decimal.c
+++ b/Modules/_decimal/_decimal.c
@@ -39,6 +39,18 @@
 
 #include "docstrings.h"
 
+typedef struct {
+    PyTypeObject *PyDecContextManager_Type;
+    PyTypeObject *PyDecContext_Type;
+    PyTypeObject *PyDecSignalDictMixin_Type;
+    PyTypeObject *PyDec_Type;
+    PyTypeObject *PyDecSignalDict_Type;
+    PyTypeObject *DecimalTuple;
+} decimal_state;
+
+static decimal_state global_state;
+
+#define GLOBAL_STATE() (&global_state)
 
 #if !defined(MPD_VERSION_HEX) || MPD_VERSION_HEX < 0x02050000
   #error "libmpdec version >= 2.5.0 required"
@@ -99,14 +111,10 @@ typedef struct {
 
 #undef MPD
 #undef CTX
-static PyTypeObject PyDec_Type;
-static PyTypeObject *PyDecSignalDict_Type;
-static PyTypeObject PyDecContext_Type;
-static PyTypeObject PyDecContextManager_Type;
-#define PyDec_CheckExact(v) Py_IS_TYPE(v, &PyDec_Type)
-#define PyDec_Check(v) PyObject_TypeCheck(v, &PyDec_Type)
-#define PyDecSignalDict_Check(v) Py_IS_TYPE(v, PyDecSignalDict_Type)
-#define PyDecContext_Check(v) PyObject_TypeCheck(v, &PyDecContext_Type)
+#define PyDec_CheckExact(st, v) Py_IS_TYPE(v, (st)->PyDec_Type)
+#define PyDec_Check(st, v) PyObject_TypeCheck(v, (st)->PyDec_Type)
+#define PyDecSignalDict_Check(st, v) Py_IS_TYPE(v, (st)->PyDecSignalDict_Type)
+#define PyDecContext_Check(st, v) PyObject_TypeCheck(v, (st)->PyDecContext_Type)
 #define MPD(v) (&((PyDecObject *)v)->dec)
 #define SdFlagAddr(v) (((PyDecSignalDictObject *)v)->flags)
 #define SdFlags(v) (*((PyDecSignalDictObject *)v)->flags)
@@ -603,6 +611,22 @@ signaldict_setitem(PyObject *self, PyObject *key, PyObject *value)
     return 0;
 }
 
+static int
+signaldict_traverse(PyObject *self, visitproc visit, void *arg)
+{
+    Py_VISIT(Py_TYPE(self));
+    return 0;
+}
+
+static void
+signaldict_dealloc(PyObject *self)
+{
+    PyTypeObject *tp = Py_TYPE(self);
+    PyObject_GC_UnTrack(self);
+    tp->tp_free(self);
+    Py_DECREF(tp);
+}
+
 static PyObject *
 signaldict_repr(PyObject *self)
 {
@@ -631,10 +655,11 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op)
 {
     PyObject *res = Py_NotImplemented;
 
-    assert(PyDecSignalDict_Check(v));
+    decimal_state *state = GLOBAL_STATE();
+    assert(PyDecSignalDict_Check(state, v));
 
     if (op == Py_EQ || op == Py_NE) {
-        if (PyDecSignalDict_Check(w)) {
+        if (PyDecSignalDict_Check(state, w)) {
             res = (SdFlags(v)==SdFlags(w)) ^ (op==Py_NE) ? Py_True : Py_False;
         }
         else if (PyDict_Check(w)) {
@@ -664,58 +689,36 @@ signaldict_copy(PyObject *self, PyObject *args UNUSED)
 }
 
 
-static PyMappingMethods signaldict_as_mapping = {
-    (lenfunc)signaldict_len,          /* mp_length */
-    (binaryfunc)signaldict_getitem,   /* mp_subscript */
-    (objobjargproc)signaldict_setitem /* mp_ass_subscript */
-};
-
 static PyMethodDef signaldict_methods[] = {
     { "copy", (PyCFunction)signaldict_copy, METH_NOARGS, NULL},
     {NULL, NULL}
 };
 
 
-static PyTypeObject PyDecSignalDictMixin_Type =
-{
-    PyVarObject_HEAD_INIT(0, 0)
-    "decimal.SignalDictMixin",                /* tp_name */
-    sizeof(PyDecSignalDictObject),            /* tp_basicsize */
-    0,                                        /* tp_itemsize */
-    0,                                        /* tp_dealloc */
-    0,                                        /* tp_vectorcall_offset */
-    (getattrfunc) 0,                          /* tp_getattr */
-    (setattrfunc) 0,                          /* tp_setattr */
-    0,                                        /* tp_as_async */
-    (reprfunc) signaldict_repr,               /* tp_repr */
-    0,                                        /* tp_as_number */
-    0,                                        /* tp_as_sequence */
-    &signaldict_as_mapping,                   /* tp_as_mapping */
-    PyObject_HashNotImplemented,              /* tp_hash */
-    0,                                        /* tp_call */
-    (reprfunc) 0,                             /* tp_str */
-    PyObject_GenericGetAttr,                  /* tp_getattro */
-    (setattrofunc) 0,                         /* tp_setattro */
-    (PyBufferProcs *) 0,                      /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,   /* tp_flags */
-    0,                                        /* tp_doc */
-    0,                                        /* tp_traverse */
-    0,                                        /* tp_clear */
-    signaldict_richcompare,                   /* tp_richcompare */
-    0,                                        /* tp_weaklistoffset */
-    (getiterfunc)signaldict_iter,             /* tp_iter */
-    0,                                        /* tp_iternext */
-    signaldict_methods,                       /* tp_methods */
-    0,                                        /* tp_members */
-    0,                                        /* tp_getset */
-    0,                                        /* tp_base */
-    0,                                        /* tp_dict */
-    0,                                        /* tp_descr_get */
-    0,                                        /* tp_descr_set */
-    0,                                        /* tp_dictoffset */
-    (initproc)signaldict_init,                /* tp_init */
-    0,                                        /* tp_alloc */
-    PyType_GenericNew,                        /* tp_new */
+static PyType_Slot signaldict_slots[] = {
+    {Py_tp_dealloc, signaldict_dealloc},
+    {Py_tp_traverse, signaldict_traverse},
+    {Py_tp_repr, signaldict_repr},
+    {Py_tp_hash, PyObject_HashNotImplemented},
+    {Py_tp_getattro, PyObject_GenericGetAttr},
+    {Py_tp_richcompare, signaldict_richcompare},
+    {Py_tp_iter, signaldict_iter},
+    {Py_tp_methods, signaldict_methods},
+    {Py_tp_init, signaldict_init},
+
+    // Mapping protocol
+    {Py_mp_length, signaldict_len},
+    {Py_mp_subscript, signaldict_getitem},
+    {Py_mp_ass_subscript, signaldict_setitem},
+    {0, NULL},
+};
+
+static PyType_Spec signaldict_spec = {
+    .name = "decimal.SignalDictMixin",
+    .basicsize = sizeof(PyDecSignalDictObject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+              Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = signaldict_slots,
 };
 
 
@@ -987,7 +990,8 @@ context_settraps_dict(PyObject *self, PyObject *value)
     mpd_context_t *ctx;
     uint32_t flags;
 
-    if (PyDecSignalDict_Check(value)) {
+    decimal_state *state = GLOBAL_STATE();
+    if (PyDecSignalDict_Check(state, value)) {
         flags = SdFlags(value);
     }
     else {
@@ -1051,7 +1055,8 @@ context_setstatus_dict(PyObject *self, PyObject *value)
     mpd_context_t *ctx;
     uint32_t flags;
 
-    if (PyDecSignalDict_Check(value)) {
+    decimal_state *state = GLOBAL_STATE();
+    if (PyDecSignalDict_Check(state, value)) {
         flags = SdFlags(value);
     }
     else {
@@ -1241,8 +1246,9 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED)
     PyDecContextObject *self = NULL;
     mpd_context_t *ctx;
 
-    if (type == &PyDecContext_Type) {
-        self = PyObject_New(PyDecContextObject, &PyDecContext_Type);
+    decimal_state *state = GLOBAL_STATE();
+    if (type == state->PyDecContext_Type) {
+        self = PyObject_GC_New(PyDecContextObject, state->PyDecContext_Type);
     }
     else {
         self = (PyDecContextObject *)type->tp_alloc(type, 0);
@@ -1252,13 +1258,13 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED)
         return NULL;
     }
 
-    self->traps = PyObject_CallObject((PyObject *)PyDecSignalDict_Type, NULL);
+    self->traps = PyObject_CallObject((PyObject *)state->PyDecSignalDict_Type, NULL);
     if (self->traps == NULL) {
         self->flags = NULL;
         Py_DECREF(self);
         return NULL;
     }
-    self->flags = PyObject_CallObject((PyObject *)PyDecSignalDict_Type, NULL);
+    self->flags = PyObject_CallObject((PyObject *)state->PyDecSignalDict_Type, NULL);
     if (self->flags == NULL) {
         Py_DECREF(self);
         return NULL;
@@ -1282,18 +1288,37 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED)
     return (PyObject *)self;
 }
 
+static int
+context_traverse(PyDecContextObject *self, visitproc visit, void *arg)
+{
+    Py_VISIT(Py_TYPE(self));
+    Py_VISIT(self->traps);
+    Py_VISIT(self->flags);
+    return 0;
+}
+
+static int
+context_clear(PyDecContextObject *self)
+{
+    Py_CLEAR(self->traps);
+    Py_CLEAR(self->flags);
+    return 0;
+}
+
 static void
 context_dealloc(PyDecContextObject *self)
 {
+    PyTypeObject *tp = Py_TYPE(self);
+    PyObject_GC_UnTrack(self);
 #ifndef WITH_DECIMAL_CONTEXTVAR
-    if (self == cached_context) {
-        cached_context = NULL;
+    decimal_state *state = GLOBAL_STATE();
+    if (self == state->cached_context) {
+        state->cached_context = NULL;
     }
 #endif
-
-    Py_XDECREF(self->traps);
-    Py_XDECREF(self->flags);
-    Py_TYPE(self)->tp_free(self);
+    (void)context_clear(self);
+    tp->tp_free(self);
+    Py_DECREF(tp);
 }
 
 static int
@@ -1337,7 +1362,10 @@ context_repr(PyDecContextObject *self)
     char traps[MPD_MAX_SIGNAL_LIST];
     int n, mem;
 
-    assert(PyDecContext_Check(self));
+#ifdef Py_DEBUG
+    decimal_state *state = GLOBAL_STATE();
+    assert(PyDecContext_Check(state, self));
+#endif
     ctx = CTX(self);
 
     mem = MPD_MAX_SIGNAL_LIST;
@@ -1403,7 +1431,8 @@ ieee_context(PyObject *dummy UNUSED, PyObject *v)
         goto error;
     }
 
-    context = PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL);
+    decimal_state *state = GLOBAL_STATE();
+    context = PyObject_CallObject((PyObject *)state->PyDecContext_Type, NULL);
     if (context == NULL) {
         return NULL;
     }
@@ -1425,7 +1454,8 @@ context_copy(PyObject *self, PyObject *args UNUSED)
 {
     PyObject *copy;
 
-    copy = PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL);
+    decimal_state *state = GLOBAL_STATE();
+    copy = PyObject_CallObject((PyObject *)state->PyDecContext_Type, NULL);
     if (copy == NULL) {
         return NULL;
     }
@@ -1487,18 +1517,18 @@ static PyGetSetDef context_getsets [] =
 };
 
 
-#define CONTEXT_CHECK(obj) \
-    if (!PyDecContext_Check(obj)) {        \
+#define CONTEXT_CHECK(state, obj) \
+    if (!PyDecContext_Check(state, obj)) { \
         PyErr_SetString(PyExc_TypeError,   \
             "argument must be a context"); \
         return NULL;                       \
     }
 
-#define CONTEXT_CHECK_VA(obj) \
+#define CONTEXT_CHECK_VA(state, obj) \
     if (obj == Py_None) {                           \
         CURRENT_CONTEXT(obj);                       \
     }                                               \
-    else if (!PyDecContext_Check(obj)) {            \
+    else if (!PyDecContext_Check(state, obj)) {     \
         PyErr_SetString(PyExc_TypeError,            \
             "optional argument must be a context"); \
         return NULL;                                \
@@ -1522,6 +1552,7 @@ static PyObject *
 current_context_from_dict(void)
 {
     PyThreadState *tstate = _PyThreadState_GET();
+    decimal_state *modstate = GLOBAL_STATE();
 #ifdef Py_DEBUG
     // The caller must hold the GIL
     _Py_EnsureTstateNotNULL(tstate);
@@ -1537,7 +1568,7 @@ current_context_from_dict(void)
     PyObject *tl_context = PyDict_GetItemWithError(dict, tls_context_key);
     if (tl_context != NULL) {
         /* We already have a thread local context. */
-        CONTEXT_CHECK(tl_context);
+        CONTEXT_CHECK(modstate, tl_context);
     }
     else {
         if (PyErr_Occurred()) {
@@ -1606,7 +1637,8 @@ PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v)
 {
     PyObject *dict;
 
-    CONTEXT_CHECK(v);
+    decimal_state *state = GLOBAL_STATE();
+    CONTEXT_CHECK(state, v);
 
     dict = PyThreadState_GetDict();
     if (dict == NULL) {
@@ -1693,7 +1725,8 @@ PyDec_GetCurrentContext(PyObject *self UNUSED, PyObject *args UNUSED)
 static PyObject *
 PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v)
 {
-    CONTEXT_CHECK(v);
+    decimal_state *state = GLOBAL_STATE();
+    CONTEXT_CHECK(state, v);
 
     /* If the new context is one of the templates, make a copy.
      * This is the current behavior of decimal.py. */
@@ -1733,7 +1766,6 @@ ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds)
       "clamp", "flags", "traps",
       NULL
     };
-    PyDecContextManagerObject *self;
     PyObject *local = Py_None;
     PyObject *global;
 
@@ -1746,6 +1778,7 @@ ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds)
     PyObject *flags = Py_None;
     PyObject *traps = Py_None;
 
+    decimal_state *state = GLOBAL_STATE();
     CURRENT_CONTEXT(global);
     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOOOOOO", kwlist, &local,
           &prec, &rounding, &Emin, &Emax, &capitals, &clamp, &flags, &traps)) {
@@ -1754,46 +1787,68 @@ ctxmanager_new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kwds)
     if (local == Py_None) {
         local = global;
     }
-    else if (!PyDecContext_Check(local)) {
+    else if (!PyDecContext_Check(state, local)) {
         PyErr_SetString(PyExc_TypeError,
             "optional argument must be a context");
         return NULL;
     }
 
-    self = PyObject_New(PyDecContextManagerObject,
-                        &PyDecContextManager_Type);
-    if (self == NULL) {
-        return NULL;
-    }
-
-    self->local = context_copy(local, NULL);
-    if (self->local == NULL) {
-        self->global = NULL;
-        Py_DECREF(self);
+    PyObject *local_copy = context_copy(local, NULL);
+    if (local_copy == NULL) {
         return NULL;
     }
-    self->global = Py_NewRef(global);
 
     int ret = context_setattrs(
-        self->local, prec, rounding,
+        local_copy, prec, rounding,
         Emin, Emax, capitals,
         clamp, flags, traps
     );
-
     if (ret < 0) {
-        Py_DECREF(self);
+        Py_DECREF(local_copy);
         return NULL;
     }
 
+    PyDecContextManagerObject *self;
+    self = PyObject_GC_New(PyDecContextManagerObject,
+                           state->PyDecContextManager_Type);
+    if (self == NULL) {
+        Py_DECREF(local_copy);
+        return NULL;
+    }
+
+    self->local = local_copy;
+    self->global = Py_NewRef(global);
+    PyObject_GC_Track(self);
+
     return (PyObject *)self;
 }
 
+static int
+ctxmanager_traverse(PyDecContextManagerObject *self, visitproc visit,
+                    void *arg)
+{
+    Py_VISIT(Py_TYPE(self));
+    Py_VISIT(self->local);
+    Py_VISIT(self->global);
+    return 0;
+}
+
+static int
+ctxmanager_clear(PyDecContextManagerObject *self)
+{
+    Py_CLEAR(self->local);
+    Py_CLEAR(self->global);
+    return 0;
+}
+
 static void
 ctxmanager_dealloc(PyDecContextManagerObject *self)
 {
-    Py_XDECREF(self->local);
-    Py_XDECREF(self->global);
-    PyObject_Free(self);
+    PyTypeObject *tp = Py_TYPE(self);
+    PyObject_GC_UnTrack(self);
+    (void)ctxmanager_clear(self);
+    tp->tp_free((PyObject *)self);
+    Py_DECREF(tp);
 }
 
 static PyObject *
@@ -1832,36 +1887,21 @@ static PyMethodDef ctxmanager_methods[] = {
   {NULL, NULL}
 };
 
-static PyTypeObject PyDecContextManager_Type =
-{
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "decimal.ContextManager",               /* tp_name */
-    sizeof(PyDecContextManagerObject),      /* tp_basicsize */
-    0,                                      /* tp_itemsize */
-    (destructor) ctxmanager_dealloc,        /* tp_dealloc */
-    0,                                      /* tp_vectorcall_offset */
-    (getattrfunc) 0,                        /* tp_getattr */
-    (setattrfunc) 0,                        /* tp_setattr */
-    0,                                      /* tp_as_async */
-    (reprfunc) 0,                           /* tp_repr */
-    0,                                      /* tp_as_number */
-    0,                                      /* tp_as_sequence */
-    0,                                      /* tp_as_mapping */
-    0,                                      /* tp_hash */
-    0,                                      /* tp_call */
-    0,                                      /* tp_str */
-    (getattrofunc) PyObject_GenericGetAttr, /* tp_getattro */
-    (setattrofunc) 0,                       /* tp_setattro */
-    (PyBufferProcs *) 0,                    /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT,                     /* tp_flags */
-    0,                                      /* tp_doc */
-    0,                                      /* tp_traverse */
-    0,                                      /* tp_clear */
-    0,                                      /* tp_richcompare */
-    0,                                      /* tp_weaklistoffset */
-    0,                                      /* tp_iter */
-    0,                                      /* tp_iternext */
-    ctxmanager_methods,                     /* tp_methods */
+static PyType_Slot ctxmanager_slots[] = {
+    {Py_tp_dealloc, ctxmanager_dealloc},
+    {Py_tp_getattro, PyObject_GenericGetAttr},
+    {Py_tp_traverse, ctxmanager_traverse},
+    {Py_tp_clear, ctxmanager_clear},
+    {Py_tp_methods, ctxmanager_methods},
+    {0, NULL},
+};
+
+static PyType_Spec ctxmanager_spec = {
+    .name = "decimal.ContextManager",
+    .basicsize = sizeof(PyDecContextManagerObject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+              Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION),
+    .slots = ctxmanager_slots,
 };
 
 
@@ -1874,8 +1914,9 @@ PyDecType_New(PyTypeObject *type)
 {
     PyDecObject *dec;
 
-    if (type == &PyDec_Type) {
-        dec = PyObject_New(PyDecObject, &PyDec_Type);
+    decimal_state *state = GLOBAL_STATE();
+    if (type == state->PyDec_Type) {
+        dec = PyObject_GC_New(PyDecObject, state->PyDec_Type);
     }
     else {
         dec = (PyDecObject *)type->tp_alloc(type, 0);
@@ -1895,13 +1936,23 @@ PyDecType_New(PyTypeObject *type)
 
     return (PyObject *)dec;
 }
-#define dec_alloc() PyDecType_New(&PyDec_Type)
+#define dec_alloc(st) PyDecType_New((st)->PyDec_Type)
+
+static int
+dec_traverse(PyObject *dec, visitproc visit, void *arg)
+{
+    Py_VISIT(Py_TYPE(dec));
+    return 0;
+}
 
 static void
 dec_dealloc(PyObject *dec)
 {
+    PyTypeObject *tp = Py_TYPE(dec);
+    PyObject_GC_UnTrack(dec);
     mpd_del(MPD(dec));
-    Py_TYPE(dec)->tp_free(dec);
+    tp->tp_free(dec);
+    Py_DECREF(tp);
 }
 
 
@@ -2259,9 +2310,10 @@ PyDecType_FromFloatExact(PyTypeObject *type, PyObject *v,
     uint32_t status = 0;
     mpd_context_t maxctx;
 
-
-    assert(PyType_IsSubtype(type, &PyDec_Type));
-
+#ifdef Py_DEBUG
+    decimal_state *state = GLOBAL_STATE();
+    assert(PyType_IsSubtype(type, state->PyDec_Type));
+#endif
     if (PyLong_Check(v)) {
         return PyDecType_FromLongExact(type, v, context);
     }
@@ -2395,7 +2447,8 @@ PyDecType_FromDecimalExact(PyTypeObject *type, PyObject *v, PyObject *context)
     PyObject *dec;
     uint32_t status = 0;
 
-    if (type == &PyDec_Type && PyDec_CheckExact(v)) {
+    decimal_state *state = GLOBAL_STATE();
+    if (type == state->PyDec_Type && PyDec_CheckExact(state, v)) {
         return Py_NewRef(v);
     }
 
@@ -2630,37 +2683,37 @@ PyDecType_FromSequenceExact(PyTypeObject *type, PyObject *v,
     return dec;
 }
 
-#define PyDec_FromCString(str, context) \
-        PyDecType_FromCString(&PyDec_Type, str, context)
-#define PyDec_FromCStringExact(str, context) \
-        PyDecType_FromCStringExact(&PyDec_Type, str, context)
+#define PyDec_FromCString(st, str, context) \
+        PyDecType_FromCString((st)->PyDec_Type, str, context)
+#define PyDec_FromCStringExact(st, str, context) \
+        PyDecType_FromCStringExact((st)->PyDec_Type, str, context)
 
-#define PyDec_FromUnicode(unicode, context) \
-        PyDecType_FromUnicode(&PyDec_Type, unicode, context)
-#define PyDec_FromUnicodeExact(unicode, context) \
-        PyDecType_FromUnicodeExact(&PyDec_Type, unicode, context)
-#define PyDec_FromUnicodeExactWS(unicode, context) \
-        PyDecType_FromUnicodeExactWS(&PyDec_Type, unicode, context)
+#define PyDec_FromUnicode(st, unicode, context) \
+        PyDecType_FromUnicode((st)->PyDec_Type, unicode, context)
+#define PyDec_FromUnicodeExact(st, unicode, context) \
+        PyDecType_FromUnicodeExact((st)->PyDec_Type, unicode, context)
+#define PyDec_FromUnicodeExactWS(st, unicode, context) \
+        PyDecType_FromUnicodeExactWS((st)->PyDec_Type, unicode, context)
 
-#define PyDec_FromSsize(v, context) \
-        PyDecType_FromSsize(&PyDec_Type, v, context)
-#define PyDec_FromSsizeExact(v, context) \
-        PyDecType_FromSsizeExact(&PyDec_Type, v, context)
+#define PyDec_FromSsize(st, v, context) \
+        PyDecType_FromSsize((st)->PyDec_Type, v, context)
+#define PyDec_FromSsizeExact(st, v, context) \
+        PyDecType_FromSsizeExact((st)->PyDec_Type, v, context)
 
-#define PyDec_FromLong(pylong, context) \
-        PyDecType_FromLong(&PyDec_Type, pylong, context)
-#define PyDec_FromLongExact(pylong, context) \
-        PyDecType_FromLongExact(&PyDec_Type, pylong, context)
+#define PyDec_FromLong(st, pylong, context) \
+        PyDecType_FromLong((st)->PyDec_Type, pylong, context)
+#define PyDec_FromLongExact(st, pylong, context) \
+        PyDecType_FromLongExact((st)->PyDec_Type, pylong, context)
 
-#define PyDec_FromFloat(pyfloat, context) \
-        PyDecType_FromFloat(&PyDec_Type, pyfloat, context)
-#define PyDec_FromFloatExact(pyfloat, context) \
-        PyDecType_FromFloatExact(&PyDec_Type, pyfloat, context)
+#define PyDec_FromFloat(st, pyfloat, context) \
+        PyDecType_FromFloat((st)->PyDec_Type, pyfloat, context)
+#define PyDec_FromFloatExact(st, pyfloat, context) \
+        PyDecType_FromFloatExact((st)->PyDec_Type, pyfloat, context)
 
-#define PyDec_FromSequence(sequence, context) \
-        PyDecType_FromSequence(&PyDec_Type, sequence, context)
-#define PyDec_FromSequenceExact(sequence, context) \
-        PyDecType_FromSequenceExact(&PyDec_Type, sequence, context)
+#define PyDec_FromSequence(st, sequence, context) \
+        PyDecType_FromSequence((st)->PyDec_Type, sequence, context)
+#define PyDec_FromSequenceExact(st, sequence, context) \
+        PyDecType_FromSequenceExact((st)->PyDec_Type, sequence, context)
 
 /* class method */
 static PyObject *
@@ -2670,8 +2723,9 @@ dec_from_float(PyObject *type, PyObject *pyfloat)
     PyObject *result;
 
     CURRENT_CONTEXT(context);
-    result = PyDecType_FromFloatExact(&PyDec_Type, pyfloat, context);
-    if (type != (PyObject *)&PyDec_Type && result != NULL) {
+    decimal_state *state = GLOBAL_STATE();
+    result = PyDecType_FromFloatExact(state->PyDec_Type, pyfloat, context);
+    if (type != (PyObject *)state->PyDec_Type && result != NULL) {
         Py_SETREF(result, PyObject_CallFunctionObjArgs(type, result, NULL));
     }
 
@@ -2682,7 +2736,8 @@ dec_from_float(PyObject *type, PyObject *pyfloat)
 static PyObject *
 ctx_from_float(PyObject *context, PyObject *v)
 {
-    return PyDec_FromFloat(v, context);
+    decimal_state *state = GLOBAL_STATE();
+    return PyDec_FromFloat(state, v, context);
 }
 
 /* Apply the context to the input operand. Return a new PyDecObject. */
@@ -2692,7 +2747,8 @@ dec_apply(PyObject *v, PyObject *context)
     PyObject *result;
     uint32_t status = 0;
 
-    result = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    result = dec_alloc(state);
     if (result == NULL) {
         return NULL;
     }
@@ -2718,10 +2774,11 @@ dec_apply(PyObject *v, PyObject *context)
 static PyObject *
 PyDecType_FromObjectExact(PyTypeObject *type, PyObject *v, PyObject *context)
 {
+    decimal_state *state = GLOBAL_STATE();
     if (v == NULL) {
         return PyDecType_FromSsizeExact(type, 0, context);
     }
-    else if (PyDec_Check(v)) {
+    else if (PyDec_Check(state, v)) {
         return PyDecType_FromDecimalExact(type, v, context);
     }
     else if (PyUnicode_Check(v)) {
@@ -2752,10 +2809,11 @@ PyDecType_FromObjectExact(PyTypeObject *type, PyObject *v, PyObject *context)
 static PyObject *
 PyDec_FromObject(PyObject *v, PyObject *context)
 {
+    decimal_state *state = GLOBAL_STATE();
     if (v == NULL) {
-        return PyDec_FromSsize(0, context);
+        return PyDec_FromSsize(state, 0, context);
     }
-    else if (PyDec_Check(v)) {
+    else if (PyDec_Check(state, v)) {
         mpd_context_t *ctx = CTX(context);
         if (mpd_isnan(MPD(v)) &&
             MPD(v)->digits > ctx->prec - ctx->clamp) {
@@ -2764,7 +2822,7 @@ PyDec_FromObject(PyObject *v, PyObject *context)
             if (dec_addstatus(context, MPD_Conversion_syntax)) {
                 return NULL;
             }
-            result = dec_alloc();
+            result = dec_alloc(state);
             if (result == NULL) {
                 return NULL;
             }
@@ -2774,19 +2832,19 @@ PyDec_FromObject(PyObject *v, PyObject *context)
         return dec_apply(v, context);
     }
     else if (PyUnicode_Check(v)) {
-        return PyDec_FromUnicode(v, context);
+        return PyDec_FromUnicode(state, v, context);
     }
     else if (PyLong_Check(v)) {
-        return PyDec_FromLong(v, context);
+        return PyDec_FromLong(state, v, context);
     }
     else if (PyTuple_Check(v) || PyList_Check(v)) {
-        return PyDec_FromSequence(v, context);
+        return PyDec_FromSequence(state, v, context);
     }
     else if (PyFloat_Check(v)) {
         if (dec_addstatus(context, MPD_Float_operation)) {
             return NULL;
         }
-        return PyDec_FromFloat(v, context);
+        return PyDec_FromFloat(state, v, context);
     }
     else {
         PyErr_Format(PyExc_TypeError,
@@ -2807,7 +2865,8 @@ dec_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
                                      &v, &context)) {
         return NULL;
     }
-    CONTEXT_CHECK_VA(context);
+    decimal_state *state = GLOBAL_STATE();
+    CONTEXT_CHECK_VA(state, context);
 
     return PyDecType_FromObjectExact(type, v, context);
 }
@@ -2837,13 +2896,13 @@ ctx_create_decimal(PyObject *context, PyObject *args)
 Py_LOCAL_INLINE(int)
 convert_op(int type_err, PyObject **conv, PyObject *v, PyObject *context)
 {
-
-    if (PyDec_Check(v)) {
+    decimal_state *state = GLOBAL_STATE();
+    if (PyDec_Check(state, v)) {
         *conv = Py_NewRef(v);
         return 1;
     }
     if (PyLong_Check(v)) {
-        *conv = PyDec_FromLongExact(v, context);
+        *conv = PyDec_FromLongExact(state, v, context);
         if (*conv == NULL) {
             return 0;
         }
@@ -2942,7 +3001,8 @@ multiply_by_denominator(PyObject *v, PyObject *r, PyObject *context)
     if (tmp == NULL) {
         return NULL;
     }
-    denom = PyDec_FromLongExact(tmp, context);
+    decimal_state *state = GLOBAL_STATE();
+    denom = PyDec_FromLongExact(state, tmp, context);
     Py_DECREF(tmp);
     if (denom == NULL) {
         return NULL;
@@ -2954,7 +3014,7 @@ multiply_by_denominator(PyObject *v, PyObject *r, PyObject *context)
         PyErr_NoMemory();
         return NULL;
     }
-    result = dec_alloc();
+    result = dec_alloc(state);
     if (result == NULL) {
         Py_DECREF(denom);
         mpd_del(vv);
@@ -2996,7 +3056,8 @@ numerator_as_decimal(PyObject *r, PyObject *context)
         return NULL;
     }
 
-    num = PyDec_FromLongExact(tmp, context);
+    decimal_state *state = GLOBAL_STATE();
+    num = PyDec_FromLongExact(state, tmp, context);
     Py_DECREF(tmp);
     return num;
 }
@@ -3014,11 +3075,12 @@ convert_op_cmp(PyObject **vcmp, PyObject **wcmp, PyObject *v, PyObject *w,
 
     *vcmp = v;
 
-    if (PyDec_Check(w)) {
+    decimal_state *state = GLOBAL_STATE();
+    if (PyDec_Check(state, w)) {
         *wcmp = Py_NewRef(w);
     }
     else if (PyLong_Check(w)) {
-        *wcmp = PyDec_FromLongExact(w, context);
+        *wcmp = PyDec_FromLongExact(state, w, context);
     }
     else if (PyFloat_Check(w)) {
         if (op != Py_EQ && op != Py_NE &&
@@ -3027,7 +3089,7 @@ convert_op_cmp(PyObject **vcmp, PyObject **wcmp, PyObject *v, PyObject *w,
         }
         else {
             ctx->status |= MPD_Float_operation;
-            *wcmp = PyDec_FromFloatExact(w, context);
+            *wcmp = PyDec_FromFloatExact(state, w, context);
         }
     }
     else if (PyComplex_Check(w) && (op == Py_EQ || op == Py_NE)) {
@@ -3042,7 +3104,7 @@ convert_op_cmp(PyObject **vcmp, PyObject **wcmp, PyObject *v, PyObject *w,
             }
             else {
                 ctx->status |= MPD_Float_operation;
-                *wcmp = PyDec_FromFloatExact(tmp, context);
+                *wcmp = PyDec_FromFloatExact(state, tmp, context);
                 Py_DECREF(tmp);
             }
         }
@@ -3556,7 +3618,8 @@ dec_as_integer_ratio(PyObject *self, PyObject *args UNUSED)
 
     CURRENT_CONTEXT(context);
 
-    tmp = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    tmp = dec_alloc(state);
     if (tmp == NULL) {
         return NULL;
     }
@@ -3646,7 +3709,8 @@ PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds)
                                      &rounding, &context)) {
         return NULL;
     }
-    CONTEXT_CHECK_VA(context);
+    decimal_state *state = GLOBAL_STATE();
+    CONTEXT_CHECK_VA(state, context);
 
     workctx = *CTX(context);
     if (rounding != Py_None) {
@@ -3659,7 +3723,7 @@ PyDec_ToIntegralValue(PyObject *dec, PyObject *args, PyObject *kwds)
         }
     }
 
-    result = dec_alloc();
+    result = dec_alloc(state);
     if (result == NULL) {
         return NULL;
     }
@@ -3687,7 +3751,8 @@ PyDec_ToIntegralExact(PyObject *dec, PyObject *args, PyObject *kwds)
                                      &rounding, &context)) {
         return NULL;
     }
-    CONTEXT_CHECK_VA(context);
+    decimal_state *state = GLOBAL_STATE();
+    CONTEXT_CHECK_VA(state, context);
 
     workctx = *CTX(context);
     if (rounding != Py_None) {
@@ -3700,7 +3765,7 @@ PyDec_ToIntegralExact(PyObject *dec, PyObject *args, PyObject *kwds)
         }
     }
 
-    result = dec_alloc();
+    result = dec_alloc(state);
     if (result == NULL) {
         return NULL;
     }
@@ -3754,7 +3819,6 @@ PyDec_Round(PyObject *dec, PyObject *args)
     uint32_t status = 0;
     PyObject *context;
 
-
     CURRENT_CONTEXT(context);
     if (!PyArg_ParseTuple(args, "|O", &x)) {
         return NULL;
@@ -3775,7 +3839,8 @@ PyDec_Round(PyObject *dec, PyObject *args)
         if (y == -1 && PyErr_Occurred()) {
             return NULL;
         }
-        result = dec_alloc();
+        decimal_state *state = GLOBAL_STATE();
+        result = dec_alloc(state);
         if (result == NULL) {
             return NULL;
         }
@@ -3794,7 +3859,6 @@ PyDec_Round(PyObject *dec, PyObject *args)
     }
 }
 
-static PyTypeObject *DecimalTuple = NULL;
 /* Return the DecimalTuple representation of a PyDecObject. */
 static PyObject *
 PyDec_AsTuple(PyObject *dec, PyObject *dummy UNUSED)
@@ -3877,7 +3941,8 @@ PyDec_AsTuple(PyObject *dec, PyObject *dummy UNUSED)
         }
     }
 
-    result = PyObject_CallFunctionObjArgs((PyObject *)DecimalTuple,
+    decimal_state *state = GLOBAL_STATE();
+    result = PyObject_CallFunctionObjArgs((PyObject *)state->DecimalTuple,
                                           sign, coeff, expt, NULL);
 
 out:
@@ -3903,8 +3968,9 @@ nm_##MPDFUNC(PyObject *self)                                \
     PyObject *context;                                      \
     uint32_t status = 0;                                    \
                                                             \
+    decimal_state *state = GLOBAL_STATE();                  \
     CURRENT_CONTEXT(context);                               \
-    if ((result = dec_alloc()) == NULL) {                   \
+    if ((result = dec_alloc(state)) == NULL) {              \
         return NULL;                                        \
     }                                                       \
                                                             \
@@ -3927,10 +3993,11 @@ nm_##MPDFUNC(PyObject *self, PyObject *other)                    \
     PyObject *context;                                           \
     uint32_t status = 0;                                         \
                                                                  \
+    decimal_state *state = GLOBAL_STATE();                       \
     CURRENT_CONTEXT(context) ;                                   \
     CONVERT_BINOP(&a, &b, self, other, context);                 \
                                                                  \
-    if ((result = dec_alloc()) == NULL) {                        \
+    if ((result = dec_alloc(state)) == NULL) {                   \
         Py_DECREF(a);                                            \
         Py_DECREF(b);                                            \
         return NULL;                                             \
@@ -3967,7 +4034,8 @@ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds)             \
                                      &context)) {                         \
         return NULL;                                                      \
     }                                                                     \
-    CONTEXT_CHECK_VA(context);                                            \
+    decimal_state *state = GLOBAL_STATE();                                \
+    CONTEXT_CHECK_VA(state, context);                                     \
                                                                           \
     return MPDFUNC(MPD(self), CTX(context)) ? incr_true() : incr_false(); \
 }
@@ -3986,9 +4054,10 @@ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds)  \
                                      &context)) {              \
         return NULL;                                           \
     }                                                          \
-    CONTEXT_CHECK_VA(context);                                 \
+    decimal_state *state = GLOBAL_STATE();                     \
+    CONTEXT_CHECK_VA(state, context);                          \
                                                                \
-    if ((result = dec_alloc()) == NULL) {                      \
+    if ((result = dec_alloc(state)) == NULL) {                 \
         return NULL;                                           \
     }                                                          \
                                                                \
@@ -4017,10 +4086,11 @@ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds)    \
                                      &other, &context)) {        \
         return NULL;                                             \
     }                                                            \
-    CONTEXT_CHECK_VA(context);                                   \
+    decimal_state *state = GLOBAL_STATE();                       \
+    CONTEXT_CHECK_VA(state, context);                            \
     CONVERT_BINOP_RAISE(&a, &b, self, other, context);           \
                                                                  \
-    if ((result = dec_alloc()) == NULL) {                        \
+    if ((result = dec_alloc(state)) == NULL) {                   \
         Py_DECREF(a);                                            \
         Py_DECREF(b);                                            \
         return NULL;                                             \
@@ -4054,10 +4124,11 @@ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds)   \
                                      &other, &context)) {       \
         return NULL;                                            \
     }                                                           \
-    CONTEXT_CHECK_VA(context);                                  \
+    decimal_state *state = GLOBAL_STATE();                      \
+    CONTEXT_CHECK_VA(state, context);                           \
     CONVERT_BINOP_RAISE(&a, &b, self, other, context);          \
                                                                 \
-    if ((result = dec_alloc()) == NULL) {                       \
+    if ((result = dec_alloc(state)) == NULL) {                  \
         Py_DECREF(a);                                           \
         Py_DECREF(b);                                           \
         return NULL;                                            \
@@ -4086,10 +4157,11 @@ dec_##MPDFUNC(PyObject *self, PyObject *args, PyObject *kwds)            \
                                      &other, &third, &context)) {        \
         return NULL;                                                     \
     }                                                                    \
-    CONTEXT_CHECK_VA(context);                                           \
+    decimal_state *state = GLOBAL_STATE();                               \
+    CONTEXT_CHECK_VA(state, context);                                    \
     CONVERT_TERNOP_RAISE(&a, &b, &c, self, other, third, context);       \
                                                                          \
-    if ((result = dec_alloc()) == NULL) {                                \
+    if ((result = dec_alloc(state)) == NULL) {                           \
         Py_DECREF(a);                                                    \
         Py_DECREF(b);                                                    \
         Py_DECREF(c);                                                    \
@@ -4151,13 +4223,14 @@ nm_mpd_qdivmod(PyObject *v, PyObject *w)
     CURRENT_CONTEXT(context);
     CONVERT_BINOP(&a, &b, v, w, context);
 
-    q = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    q = dec_alloc(state);
     if (q == NULL) {
         Py_DECREF(a);
         Py_DECREF(b);
         return NULL;
     }
-    r = dec_alloc();
+    r = dec_alloc(state);
     if (r == NULL) {
         Py_DECREF(a);
         Py_DECREF(b);
@@ -4199,7 +4272,8 @@ nm_mpd_qpow(PyObject *base, PyObject *exp, PyObject *mod)
         }
     }
 
-    result = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    result = dec_alloc(state);
     if (result == NULL) {
         Py_DECREF(a);
         Py_DECREF(b);
@@ -4300,7 +4374,8 @@ dec_mpd_radix(PyObject *self UNUSED, PyObject *dummy UNUSED)
 {
     PyObject *result;
 
-    result = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    result = dec_alloc(state);
     if (result == NULL) {
         return NULL;
     }
@@ -4315,7 +4390,8 @@ dec_mpd_qcopy_abs(PyObject *self, PyObject *dummy UNUSED)
     PyObject *result;
     uint32_t status = 0;
 
-    if ((result = dec_alloc()) == NULL) {
+    decimal_state *state = GLOBAL_STATE();
+    if ((result = dec_alloc(state)) == NULL) {
         return NULL;
     }
 
@@ -4335,7 +4411,8 @@ dec_mpd_qcopy_negate(PyObject *self, PyObject *dummy UNUSED)
     PyObject *result;
     uint32_t status = 0;
 
-    if ((result = dec_alloc()) == NULL) {
+    decimal_state *state = GLOBAL_STATE();
+    if ((result = dec_alloc(state)) == NULL) {
         return NULL;
     }
 
@@ -4364,7 +4441,8 @@ dec_mpd_class(PyObject *self, PyObject *args, PyObject *kwds)
                                      &context)) {
         return NULL;
     }
-    CONTEXT_CHECK_VA(context);
+    decimal_state *state = GLOBAL_STATE();
+    CONTEXT_CHECK_VA(state, context);
 
     cp = mpd_class(MPD(self), CTX(context));
     return PyUnicode_FromString(cp);
@@ -4383,7 +4461,8 @@ dec_mpd_to_eng(PyObject *self, PyObject *args, PyObject *kwds)
                                      &context)) {
         return NULL;
     }
-    CONTEXT_CHECK_VA(context);
+    decimal_state *state = GLOBAL_STATE();
+    CONTEXT_CHECK_VA(state, context);
 
     size = mpd_to_eng_size(&s, MPD(self), CtxCaps(context));
     if (size < 0) {
@@ -4415,10 +4494,11 @@ dec_mpd_qcopy_sign(PyObject *self, PyObject *args, PyObject *kwds)
                                      &other, &context)) {
         return NULL;
     }
-    CONTEXT_CHECK_VA(context);
+    decimal_state *state = GLOBAL_STATE();
+    CONTEXT_CHECK_VA(state, context);
     CONVERT_BINOP_RAISE(&a, &b, self, other, context);
 
-    result = dec_alloc();
+    result = dec_alloc(state);
     if (result == NULL) {
         Py_DECREF(a);
         Py_DECREF(b);
@@ -4449,7 +4529,8 @@ dec_mpd_same_quantum(PyObject *self, PyObject *args, PyObject *kwds)
                                      &other, &context)) {
         return NULL;
     }
-    CONTEXT_CHECK_VA(context);
+    decimal_state *state = GLOBAL_STATE();
+    CONTEXT_CHECK_VA(state, context);
     CONVERT_BINOP_RAISE(&a, &b, self, other, context);
 
     result = mpd_same_quantum(MPD(a), MPD(b)) ? incr_true() : incr_false();
@@ -4483,7 +4564,8 @@ dec_mpd_qquantize(PyObject *v, PyObject *args, PyObject *kwds)
                                      &w, &rounding, &context)) {
         return NULL;
     }
-    CONTEXT_CHECK_VA(context);
+    decimal_state *state = GLOBAL_STATE();
+    CONTEXT_CHECK_VA(state, context);
 
     workctx = *CTX(context);
     if (rounding != Py_None) {
@@ -4498,7 +4580,7 @@ dec_mpd_qquantize(PyObject *v, PyObject *args, PyObject *kwds)
 
     CONVERT_BINOP_RAISE(&a, &b, v, w, context);
 
-    result = dec_alloc();
+    result = dec_alloc(state);
     if (result == NULL) {
         Py_DECREF(a);
         Py_DECREF(b);
@@ -4527,8 +4609,10 @@ dec_richcompare(PyObject *v, PyObject *w, int op)
     int a_issnan, b_issnan;
     int r;
 
-    assert(PyDec_Check(v));
-
+#ifdef Py_DEBUG
+    decimal_state *state = GLOBAL_STATE();
+    assert(PyDec_Check(state, v));
+#endif
     CURRENT_CONTEXT(context);
     CONVERT_BINOP_CMP(&a, &b, v, w, op, context);
 
@@ -4798,7 +4882,8 @@ dec_imag(PyObject *self UNUSED, void *closure UNUSED)
 {
     PyObject *result;
 
-    result = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    result = dec_alloc(state);
     if (result == NULL) {
         return NULL;
     }
@@ -4815,43 +4900,6 @@ static PyGetSetDef dec_getsets [] =
   {NULL}
 };
 
-static PyNumberMethods dec_number_methods =
-{
-    (binaryfunc) nm_mpd_qadd,
-    (binaryfunc) nm_mpd_qsub,
-    (binaryfunc) nm_mpd_qmul,
-    (binaryfunc) nm_mpd_qrem,
-    (binaryfunc) nm_mpd_qdivmod,
-    (ternaryfunc) nm_mpd_qpow,
-    (unaryfunc) nm_mpd_qminus,
-    (unaryfunc) nm_mpd_qplus,
-    (unaryfunc) nm_mpd_qabs,
-    (inquiry) nm_nonzero,
-    (unaryfunc) 0,   /* no bit-complement */
-    (binaryfunc) 0,  /* no shiftl */
-    (binaryfunc) 0,  /* no shiftr */
-    (binaryfunc) 0,  /* no bit-and */
-    (binaryfunc) 0,  /* no bit-xor */
-    (binaryfunc) 0,  /* no bit-ior */
-    (unaryfunc) nm_dec_as_long,
-    0,               /* nb_reserved */
-    (unaryfunc) PyDec_AsFloat,
-    0,               /* binaryfunc nb_inplace_add; */
-    0,               /* binaryfunc nb_inplace_subtract; */
-    0,               /* binaryfunc nb_inplace_multiply; */
-    0,               /* binaryfunc nb_inplace_remainder; */
-    0,               /* ternaryfunc nb_inplace_power; */
-    0,               /* binaryfunc nb_inplace_lshift; */
-    0,               /* binaryfunc nb_inplace_rshift; */
-    0,               /* binaryfunc nb_inplace_and; */
-    0,               /* binaryfunc nb_inplace_xor; */
-    0,               /* binaryfunc nb_inplace_or; */
-    (binaryfunc) nm_mpd_qdivint,  /* binaryfunc nb_floor_divide; */
-    (binaryfunc) nm_mpd_qdiv,     /* binaryfunc nb_true_divide; */
-    0,               /* binaryfunc nb_inplace_floor_divide; */
-    0,               /* binaryfunc nb_inplace_true_divide; */
-};
-
 static PyMethodDef dec_methods [] =
 {
   /* Unary arithmetic functions, optional context arg */
@@ -4944,48 +4992,44 @@ static PyMethodDef dec_methods [] =
   { NULL, NULL, 1 }
 };
 
-static PyTypeObject PyDec_Type =
-{
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "decimal.Decimal",                      /* tp_name */
-    sizeof(PyDecObject),                    /* tp_basicsize */
-    0,                                      /* tp_itemsize */
-    (destructor) dec_dealloc,               /* tp_dealloc */
-    0,                                      /* tp_vectorcall_offset */
-    (getattrfunc) 0,                        /* tp_getattr */
-    (setattrfunc) 0,                        /* tp_setattr */
-    0,                                      /* tp_as_async */
-    (reprfunc) dec_repr,                    /* tp_repr */
-    &dec_number_methods,                    /* tp_as_number */
-    0,                                      /* tp_as_sequence */
-    0,                                      /* tp_as_mapping */
-    (hashfunc) dec_hash,                    /* tp_hash */
-    0,                                      /* tp_call */
-    (reprfunc) dec_str,                     /* tp_str */
-    (getattrofunc) PyObject_GenericGetAttr, /* tp_getattro */
-    (setattrofunc) 0,                       /* tp_setattro */
-    (PyBufferProcs *) 0,                    /* tp_as_buffer */
-    (Py_TPFLAGS_DEFAULT|
-     Py_TPFLAGS_BASETYPE),                  /* tp_flags */
-    doc_decimal,                            /* tp_doc */
-    0,                                      /* tp_traverse */
-    0,                                      /* tp_clear */
-    dec_richcompare,                        /* tp_richcompare */
-    0,                                      /* tp_weaklistoffset */
-    0,                                      /* tp_iter */
-    0,                                      /* tp_iternext */
-    dec_methods,                            /* tp_methods */
-    0,                                      /* tp_members */
-    dec_getsets,                            /* tp_getset */
-    0,                                      /* tp_base */
-    0,                                      /* tp_dict */
-    0,                                      /* tp_descr_get */
-    0,                                      /* tp_descr_set */
-    0,                                      /* tp_dictoffset */
-    0,                                      /* tp_init */
-    0,                                      /* tp_alloc */
-    dec_new,                                /* tp_new */
-    PyObject_Del,                           /* tp_free */
+static PyType_Slot dec_slots[] = {
+    {Py_tp_dealloc, dec_dealloc},
+    {Py_tp_getattro, PyObject_GenericGetAttr},
+    {Py_tp_traverse, dec_traverse},
+    {Py_tp_repr, dec_repr},
+    {Py_tp_hash, dec_hash},
+    {Py_tp_str, dec_str},
+    {Py_tp_doc, (void *)doc_decimal},
+    {Py_tp_richcompare, dec_richcompare},
+    {Py_tp_methods, dec_methods},
+    {Py_tp_getset, dec_getsets},
+    {Py_tp_new, dec_new},
+
+    // Number protocol
+    {Py_nb_add, nm_mpd_qadd},
+    {Py_nb_subtract, nm_mpd_qsub},
+    {Py_nb_multiply, nm_mpd_qmul},
+    {Py_nb_remainder, nm_mpd_qrem},
+    {Py_nb_divmod, nm_mpd_qdivmod},
+    {Py_nb_power, nm_mpd_qpow},
+    {Py_nb_negative, nm_mpd_qminus},
+    {Py_nb_positive, nm_mpd_qplus},
+    {Py_nb_absolute, nm_mpd_qabs},
+    {Py_nb_bool, nm_nonzero},
+    {Py_nb_int, nm_dec_as_long},
+    {Py_nb_float, PyDec_AsFloat},
+    {Py_nb_floor_divide, nm_mpd_qdivint},
+    {Py_nb_true_divide, nm_mpd_qdiv},
+    {0, NULL},
+};
+
+
+static PyType_Spec dec_spec = {
+    .name = "decimal.Decimal",
+    .basicsize = sizeof(PyDecObject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+              Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = dec_slots,
 };
 
 
@@ -5037,8 +5081,8 @@ ctx_##MPDFUNC(PyObject *context, PyObject *v)            \
     uint32_t status = 0;                                 \
                                                          \
     CONVERT_OP_RAISE(&a, v, context);                    \
-                                                         \
-    if ((result = dec_alloc()) == NULL) {                \
+    decimal_state *state = GLOBAL_STATE();               \
+    if ((result = dec_alloc(state)) == NULL) {           \
         Py_DECREF(a);                                    \
         return NULL;                                     \
     }                                                    \
@@ -5068,8 +5112,8 @@ ctx_##MPDFUNC(PyObject *context, PyObject *args)                 \
     }                                                            \
                                                                  \
     CONVERT_BINOP_RAISE(&a, &b, v, w, context);                  \
-                                                                 \
-    if ((result = dec_alloc()) == NULL) {                        \
+    decimal_state *state = GLOBAL_STATE();                       \
+    if ((result = dec_alloc(state)) == NULL) {                   \
         Py_DECREF(a);                                            \
         Py_DECREF(b);                                            \
         return NULL;                                             \
@@ -5103,8 +5147,8 @@ ctx_##MPDFUNC(PyObject *context, PyObject *args) \
     }                                            \
                                                  \
     CONVERT_BINOP_RAISE(&a, &b, v, w, context);  \
-                                                 \
-    if ((result = dec_alloc()) == NULL) {        \
+    decimal_state *state = GLOBAL_STATE();       \
+    if ((result = dec_alloc(state)) == NULL) {   \
         Py_DECREF(a);                            \
         Py_DECREF(b);                            \
         return NULL;                             \
@@ -5132,8 +5176,8 @@ ctx_##MPDFUNC(PyObject *context, PyObject *args)                         \
     }                                                                    \
                                                                          \
     CONVERT_TERNOP_RAISE(&a, &b, &c, v, w, x, context);                  \
-                                                                         \
-    if ((result = dec_alloc()) == NULL) {                                \
+    decimal_state *state = GLOBAL_STATE();                               \
+    if ((result = dec_alloc(state)) == NULL) {                           \
         Py_DECREF(a);                                                    \
         Py_DECREF(b);                                                    \
         Py_DECREF(c);                                                    \
@@ -5198,14 +5242,14 @@ ctx_mpd_qdivmod(PyObject *context, PyObject *args)
     }
 
     CONVERT_BINOP_RAISE(&a, &b, v, w, context);
-
-    q = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    q = dec_alloc(state);
     if (q == NULL) {
         Py_DECREF(a);
         Py_DECREF(b);
         return NULL;
     }
-    r = dec_alloc();
+    r = dec_alloc(state);
     if (r == NULL) {
         Py_DECREF(a);
         Py_DECREF(b);
@@ -5253,7 +5297,8 @@ ctx_mpd_qpow(PyObject *context, PyObject *args, PyObject *kwds)
         }
     }
 
-    result = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    result = dec_alloc(state);
     if (result == NULL) {
         Py_DECREF(a);
         Py_DECREF(b);
@@ -5304,7 +5349,8 @@ DecCtx_BoolFunc_NO_CTX(mpd_iszero)
 static PyObject *
 ctx_iscanonical(PyObject *context UNUSED, PyObject *v)
 {
-    if (!PyDec_Check(v)) {
+    decimal_state *state = GLOBAL_STATE();
+    if (!PyDec_Check(state, v)) {
         PyErr_SetString(PyExc_TypeError,
             "argument must be a Decimal");
         return NULL;
@@ -5329,7 +5375,8 @@ PyDecContext_Apply(PyObject *context, PyObject *v)
 static PyObject *
 ctx_canonical(PyObject *context UNUSED, PyObject *v)
 {
-    if (!PyDec_Check(v)) {
+    decimal_state *state = GLOBAL_STATE();
+    if (!PyDec_Check(state, v)) {
         PyErr_SetString(PyExc_TypeError,
             "argument must be a Decimal");
         return NULL;
@@ -5345,8 +5392,8 @@ ctx_mpd_qcopy_abs(PyObject *context, PyObject *v)
     uint32_t status = 0;
 
     CONVERT_OP_RAISE(&a, v, context);
-
-    result = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    result = dec_alloc(state);
     if (result == NULL) {
         Py_DECREF(a);
         return NULL;
@@ -5378,8 +5425,8 @@ ctx_mpd_qcopy_negate(PyObject *context, PyObject *v)
     uint32_t status = 0;
 
     CONVERT_OP_RAISE(&a, v, context);
-
-    result = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    result = dec_alloc(state);
     if (result == NULL) {
         Py_DECREF(a);
         return NULL;
@@ -5475,8 +5522,8 @@ ctx_mpd_qcopy_sign(PyObject *context, PyObject *args)
     }
 
     CONVERT_BINOP_RAISE(&a, &b, v, w, context);
-
-    result = dec_alloc();
+    decimal_state *state = GLOBAL_STATE();
+    result = dec_alloc(state);
     if (result == NULL) {
         Py_DECREF(a);
         Py_DECREF(b);
@@ -5629,47 +5676,27 @@ static PyMethodDef context_methods [] =
   { NULL, NULL, 1 }
 };
 
-static PyTypeObject PyDecContext_Type =
-{
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "decimal.Context",                         /* tp_name */
-    sizeof(PyDecContextObject),                /* tp_basicsize */
-    0,                                         /* tp_itemsize */
-    (destructor) context_dealloc,              /* tp_dealloc */
-    0,                                         /* tp_vectorcall_offset */
-    (getattrfunc) 0,                           /* tp_getattr */
-    (setattrfunc) 0,                           /* tp_setattr */
-    0,                                         /* tp_as_async */
-    (reprfunc) context_repr,                   /* tp_repr */
-    0,                                         /* tp_as_number */
-    0,                                         /* tp_as_sequence */
-    0,                                         /* tp_as_mapping */
-    (hashfunc) 0,                              /* tp_hash */
-    0,                                         /* tp_call */
-    0,                                         /* tp_str */
-    (getattrofunc) context_getattr,            /* tp_getattro */
-    (setattrofunc) context_setattr,            /* tp_setattro */
-    (PyBufferProcs *) 0,                       /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,    /* tp_flags */
-    doc_context,                               /* tp_doc */
-    0,                                         /* tp_traverse */
-    0,                                         /* tp_clear */
-    0,                                         /* tp_richcompare */
-    0,                                         /* tp_weaklistoffset */
-    0,                                         /* tp_iter */
-    0,                                         /* tp_iternext */
-    context_methods,                           /* tp_methods */
-    0,                                         /* tp_members */
-    context_getsets,                           /* tp_getset */
-    0,                                         /* tp_base */
-    0,                                         /* tp_dict */
-    0,                                         /* tp_descr_get */
-    0,                                         /* tp_descr_set */
-    0,                                         /* tp_dictoffset */
-    context_init,                              /* tp_init */
-    0,                                         /* tp_alloc */
-    context_new,                               /* tp_new */
-    PyObject_Del,                              /* tp_free */
+static PyType_Slot context_slots[] = {
+    {Py_tp_dealloc, context_dealloc},
+    {Py_tp_traverse, context_traverse},
+    {Py_tp_clear, context_clear},
+    {Py_tp_repr, context_repr},
+    {Py_tp_getattro, context_getattr},
+    {Py_tp_setattro, context_setattr},
+    {Py_tp_doc, (void *)doc_context},
+    {Py_tp_methods, context_methods},
+    {Py_tp_getset, context_getsets},
+    {Py_tp_init, context_init},
+    {Py_tp_new, context_new},
+    {0, NULL},
+};
+
+static PyType_Spec context_spec = {
+    .name = "decimal.Context",
+    .basicsize = sizeof(PyDecContextObject),
+    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+              Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
+    .slots = context_slots,
 };
 
 
@@ -5794,6 +5821,7 @@ PyInit__decimal(void)
     mpd_free = PyMem_Free;
     mpd_setminalloc(_Py_DEC_MINALLOC);
 
+    decimal_state *state = GLOBAL_STATE();
 
     /* Init external C-API functions */
     _py_long_multiply = PyLong_Type.tp_as_number->nb_multiply;
@@ -5806,19 +5834,21 @@ PyInit__decimal(void)
 
 
     /* Init types */
-    PyDec_Type.tp_base = &PyBaseObject_Type;
-    PyDecContext_Type.tp_base = &PyBaseObject_Type;
-    PyDecContextManager_Type.tp_base = &PyBaseObject_Type;
-    PyDecSignalDictMixin_Type.tp_base = &PyBaseObject_Type;
+#define CREATE_TYPE(mod, tp, spec) do {                               \
+    tp = (PyTypeObject *)PyType_FromMetaclass(NULL, mod, spec, NULL); \
+    CHECK_PTR(tp);                                                    \
+} while (0)
+
+    CREATE_TYPE(m, state->PyDec_Type, &dec_spec);
+    CREATE_TYPE(m, state->PyDecContext_Type, &context_spec);
+    CREATE_TYPE(m, state->PyDecContextManager_Type, &ctxmanager_spec);
+    CREATE_TYPE(m, state->PyDecSignalDictMixin_Type, &signaldict_spec);
 
-    CHECK_INT(PyType_Ready(&PyDec_Type));
-    CHECK_INT(PyType_Ready(&PyDecContext_Type));
-    CHECK_INT(PyType_Ready(&PyDecSignalDictMixin_Type));
-    CHECK_INT(PyType_Ready(&PyDecContextManager_Type));
+#undef CREATE_TYPE
 
     ASSIGN_PTR(obj, PyUnicode_FromString("decimal"));
-    CHECK_INT(PyDict_SetItemString(PyDec_Type.tp_dict, "__module__", obj));
-    CHECK_INT(PyDict_SetItemString(PyDecContext_Type.tp_dict,
+    CHECK_INT(PyDict_SetItemString(state->PyDec_Type->tp_dict, "__module__", obj));
+    CHECK_INT(PyDict_SetItemString(state->PyDecContext_Type->tp_dict,
                                    "__module__", obj));
     Py_CLEAR(obj);
 
@@ -5828,7 +5858,7 @@ PyInit__decimal(void)
     ASSIGN_PTR(Number, PyObject_GetAttrString(numbers, "Number"));
     /* Register Decimal with the Number abstract base class */
     ASSIGN_PTR(obj, PyObject_CallMethod(Number, "register", "(O)",
-                                        (PyObject *)&PyDec_Type));
+                                        (PyObject *)state->PyDec_Type));
     Py_CLEAR(obj);
     /* Rational is a global variable used for fraction comparisons. */
     ASSIGN_PTR(Rational, PyObject_GetAttrString(numbers, "Rational"));
@@ -5838,12 +5868,12 @@ PyInit__decimal(void)
 
     /* DecimalTuple */
     ASSIGN_PTR(collections, PyImport_ImportModule("collections"));
-    ASSIGN_PTR(DecimalTuple, (PyTypeObject *)PyObject_CallMethod(collections,
+    ASSIGN_PTR(state->DecimalTuple, (PyTypeObject *)PyObject_CallMethod(collections,
                                  "namedtuple", "(ss)", "DecimalTuple",
                                  "sign digits exponent"));
 
     ASSIGN_PTR(obj, PyUnicode_FromString("decimal"));
-    CHECK_INT(PyDict_SetItemString(DecimalTuple->tp_dict, "__module__", obj));
+    CHECK_INT(PyDict_SetItemString(state->DecimalTuple->tp_dict, "__module__", obj));
     Py_CLEAR(obj);
 
     /* MutableMapping */
@@ -5851,10 +5881,10 @@ PyInit__decimal(void)
     ASSIGN_PTR(MutableMapping, PyObject_GetAttrString(collections_abc,
                                                       "MutableMapping"));
     /* Create SignalDict type */
-    ASSIGN_PTR(PyDecSignalDict_Type,
+    ASSIGN_PTR(state->PyDecSignalDict_Type,
                    (PyTypeObject *)PyObject_CallFunction(
                    (PyObject *)&PyType_Type, "s(OO){}",
-                   "SignalDict", &PyDecSignalDictMixin_Type,
+                   "SignalDict", state->PyDecSignalDictMixin_Type,
                    MutableMapping));
 
     /* Done with collections, MutableMapping */
@@ -5867,10 +5897,9 @@ PyInit__decimal(void)
     ASSIGN_PTR(m, PyModule_Create(&_decimal_module));
 
     /* Add types to the module */
-    CHECK_INT(PyModule_AddObject(m, "Decimal", Py_NewRef(&PyDec_Type)));
-    CHECK_INT(PyModule_AddObject(m, "Context",
-                                 Py_NewRef(&PyDecContext_Type)));
-    CHECK_INT(PyModule_AddObject(m, "DecimalTuple", Py_NewRef(DecimalTuple)));
+    CHECK_INT(PyModule_AddType(m, state->PyDec_Type));
+    CHECK_INT(PyModule_AddType(m, state->PyDecContext_Type));
+    CHECK_INT(PyModule_AddType(m, state->DecimalTuple));
 
     /* Create top level exception */
     ASSIGN_PTR(DecimalException, PyErr_NewException(
@@ -5951,7 +5980,7 @@ PyInit__decimal(void)
 
     /* Init default context template first */
     ASSIGN_PTR(default_context_template,
-               PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
+               PyObject_CallObject((PyObject *)state->PyDecContext_Type, NULL));
     CHECK_INT(PyModule_AddObject(m, "DefaultContext",
                                  Py_NewRef(default_context_template)));
 
@@ -5966,14 +5995,14 @@ PyInit__decimal(void)
 
     /* Init basic context template */
     ASSIGN_PTR(basic_context_template,
-               PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
+               PyObject_CallObject((PyObject *)state->PyDecContext_Type, NULL));
     init_basic_context(basic_context_template);
     CHECK_INT(PyModule_AddObject(m, "BasicContext",
                                  Py_NewRef(basic_context_template)));
 
     /* Init extended context template */
     ASSIGN_PTR(extended_context_template,
-               PyObject_CallObject((PyObject *)&PyDecContext_Type, NULL));
+               PyObject_CallObject((PyObject *)state->PyDecContext_Type, NULL));
     init_extended_context(extended_context_template);
     CHECK_INT(PyModule_AddObject(m, "ExtendedContext",
                                  Py_NewRef(extended_context_template)));
@@ -6015,7 +6044,7 @@ PyInit__decimal(void)
     Py_CLEAR(collections_abc); /* GCOV_NOT_REACHED */
     Py_CLEAR(MutableMapping); /* GCOV_NOT_REACHED */
     Py_CLEAR(SignalTuple); /* GCOV_NOT_REACHED */
-    Py_CLEAR(DecimalTuple); /* GCOV_NOT_REACHED */
+    Py_CLEAR(state->DecimalTuple); /* GCOV_NOT_REACHED */
     Py_CLEAR(default_context_template); /* GCOV_NOT_REACHED */
 #ifndef WITH_DECIMAL_CONTEXTVAR
     Py_CLEAR(tls_context_key); /* GCOV_NOT_REACHED */
diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv
index 7ca14b91c841d..1131edff265ee 100644
--- a/Tools/c-analyzer/cpython/globals-to-fix.tsv
+++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv
@@ -375,10 +375,6 @@ Modules/_datetimemodule.c	-	PyDateTime_IsoCalendarDateType	-
 Modules/_datetimemodule.c	-	PyDateTime_TZInfoType	-
 Modules/_datetimemodule.c	-	PyDateTime_TimeType	-
 Modules/_datetimemodule.c	-	PyDateTime_TimeZoneType	-
-Modules/_decimal/_decimal.c	-	PyDecContextManager_Type	-
-Modules/_decimal/_decimal.c	-	PyDecContext_Type	-
-Modules/_decimal/_decimal.c	-	PyDecSignalDictMixin_Type	-
-Modules/_decimal/_decimal.c	-	PyDec_Type	-
 Modules/xxmodule.c	-	Null_Type	-
 Modules/xxmodule.c	-	Str_Type	-
 Modules/xxmodule.c	-	Xxo_Type	-
@@ -389,8 +385,6 @@ Modules/xxsubtype.c	-	spamlist_type	-
 ## non-static types - initialized once
 
 ## heap types
-Modules/_decimal/_decimal.c	-	DecimalTuple	-
-Modules/_decimal/_decimal.c	-	PyDecSignalDict_Type	-
 Modules/_tkinter.c	-	PyTclObject_Type	-
 Modules/_tkinter.c	-	Tkapp_Type	-
 Modules/_tkinter.c	-	Tktt_Type	-
@@ -428,6 +422,7 @@ Modules/_datetimemodule.c	-	us_per_hour	-
 Modules/_datetimemodule.c	-	us_per_day	-
 Modules/_datetimemodule.c	-	us_per_week	-
 Modules/_datetimemodule.c	-	seconds_per_day	-
+Modules/_decimal/_decimal.c	-	global_state	-
 Modules/_decimal/_decimal.c	-	basic_context_template	-
 Modules/_decimal/_decimal.c	-	current_context_var	-
 Modules/_decimal/_decimal.c	-	default_context_template	-



More information about the Python-checkins mailing list