aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Fulton <jim@zope.com>2004-07-14 19:11:50 +0000
committerJim Fulton <jim@zope.com>2004-07-14 19:11:50 +0000
commitd15dc06df062fdf0fe8badec2982c6c5e0e28eb0 (patch)
treee0295c2c22fa7c2e702b37a1d7afd77547a8894f /Modules/threadmodule.c
parentSummarized changes: threading.local, Py_CLEAR, Py_VISIT, improved type (diff)
downloadcpython-d15dc06df062fdf0fe8badec2982c6c5e0e28eb0.tar.gz
cpython-d15dc06df062fdf0fe8badec2982c6c5e0e28eb0.tar.bz2
cpython-d15dc06df062fdf0fe8badec2982c6c5e0e28eb0.zip
Implemented thread-local data as proposed on python-dev:
http://mail.python.org/pipermail/python-dev/2004-June/045785.html
Diffstat (limited to 'Modules/threadmodule.c')
-rw-r--r--Modules/threadmodule.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/Modules/threadmodule.c b/Modules/threadmodule.c
index b6b32f7c855..b398a25d894 100644
--- a/Modules/threadmodule.c
+++ b/Modules/threadmodule.c
@@ -158,6 +158,259 @@ static PyTypeObject Locktype = {
0, /*tp_repr*/
};
+/* Thread-local objects */
+
+#include "structmember.h"
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *key;
+ PyObject *args;
+ PyObject *kw;
+ PyObject *dict;
+} localobject;
+
+static PyTypeObject localtype;
+
+static PyObject *
+local_new(PyTypeObject *type, PyObject *args, PyObject *kw)
+{
+ localobject *self;
+ PyObject *tdict;
+
+ if (type->tp_init == PyBaseObject_Type.tp_init
+ && ((args && PyObject_IsTrue(args))
+ ||
+ (kw && PyObject_IsTrue(kw))
+ )
+ ) {
+ PyErr_SetString(PyExc_TypeError,
+ "Initialization arguments are not supported");
+ return NULL;
+ }
+
+ self = (localobject *)type->tp_alloc(type, 0);
+ if (self == NULL)
+ return NULL;
+
+ Py_XINCREF(args);
+ self->args = args;
+ Py_XINCREF(kw);
+ self->kw = kw;
+ self->dict = NULL; /* making sure */
+ self->key = PyString_FromFormat("thread.local.%p", self);
+ if (self->key == NULL)
+ goto err;
+
+ self->dict = PyDict_New();
+ if (self->dict == NULL)
+ goto err;
+
+ tdict = PyThreadState_GetDict();
+ if (tdict == NULL) {
+ PyErr_SetString(PyExc_SystemError,
+ "Couldn't get thread-state dictionary");
+ goto err;
+ }
+
+ if (PyDict_SetItem(tdict, self->key, self->dict) < 0)
+ goto err;
+
+ return (PyObject *)self;
+
+ err:
+ Py_DECREF(self);
+ return NULL;
+}
+
+static int
+local_traverse(localobject *self, visitproc visit, void *arg)
+{
+ Py_VISIT(self->args);
+ Py_VISIT(self->kw);
+ Py_VISIT(self->dict);
+ return 0;
+}
+
+static int
+local_clear(localobject *self)
+{
+ Py_CLEAR(self->key);
+ Py_CLEAR(self->args);
+ Py_CLEAR(self->kw);
+ Py_CLEAR(self->dict);
+ return 0;
+}
+
+static void
+local_dealloc(localobject *self)
+{
+ PyThreadState *tstate;
+ if (self->key
+ && (tstate = PyThreadState_Get())
+ && tstate->interp) {
+ for(tstate = PyInterpreterState_ThreadHead(tstate->interp);
+ tstate;
+ tstate = PyThreadState_Next(tstate)
+ )
+ if (tstate->dict &&
+ PyDict_GetItem(tstate->dict, self->key))
+ PyDict_DelItem(tstate->dict, self->key);
+ }
+
+ local_clear(self);
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *
+_ldict(localobject *self)
+{
+ PyObject *tdict, *ldict;
+
+ tdict = PyThreadState_GetDict();
+ if (tdict == NULL) {
+ PyErr_SetString(PyExc_SystemError,
+ "Couldn't get thread-state dictionary");
+ return NULL;
+ }
+
+ ldict = PyDict_GetItem(tdict, self->key);
+ if (ldict == NULL) {
+ ldict = PyDict_New(); /* we own ldict */
+
+ if (ldict == NULL)
+ return NULL;
+ else {
+ int i = PyDict_SetItem(tdict, self->key, ldict);
+ Py_DECREF(ldict); /* now ldict is borowed */
+ if (i < 0)
+ return NULL;
+ }
+
+ Py_CLEAR(self->dict);
+ Py_INCREF(ldict);
+ self->dict = ldict; /* still borrowed */
+
+ if (self->ob_type->tp_init != PyBaseObject_Type.tp_init &&
+ self->ob_type->tp_init((PyObject*)self,
+ self->args, self->kw) < 0
+ ) {
+ /* we need to get rid of ldict from thread so
+ we create a new one the next time we do an attr
+ acces */
+ PyDict_DelItem(tdict, self->key);
+ return NULL;
+ }
+
+ }
+ else if (self->dict != ldict) {
+ Py_CLEAR(self->dict);
+ Py_INCREF(ldict);
+ self->dict = ldict;
+ }
+
+ return ldict;
+}
+
+static PyObject *
+local_getattro(localobject *self, PyObject *name)
+{
+ PyObject *ldict, *value;
+
+ ldict = _ldict(self);
+ if (ldict == NULL)
+ return NULL;
+
+ if (self->ob_type != &localtype)
+ /* use generic lookup for subtypes */
+ return PyObject_GenericGetAttr((PyObject *)self, name);
+
+ /* Optimization: just look in dict ourselves */
+ value = PyDict_GetItem(ldict, name);
+ if (value == NULL)
+ /* Fall back on generic to get __class__ and __dict__ */
+ return PyObject_GenericGetAttr((PyObject *)self, name);
+
+ Py_INCREF(value);
+ return value;
+}
+
+static int
+local_setattro(localobject *self, PyObject *name, PyObject *v)
+{
+ PyObject *ldict;
+
+ ldict = _ldict(self);
+ if (ldict == NULL)
+ return -1;
+
+ return PyObject_GenericSetAttr((PyObject *)self, name, v);
+}
+
+static PyObject *
+local_getdict(localobject *self, void *closure)
+{
+ if (self->dict == NULL) {
+ PyErr_SetString(PyExc_AttributeError, "__dict__");
+ return NULL;
+ }
+
+ Py_INCREF(self->dict);
+ return self->dict;
+}
+
+static PyGetSetDef local_getset[] = {
+ {"__dict__",
+ (getter)local_getdict, (setter)0,
+ "Local-data dictionary",
+ NULL},
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject localtype = {
+ PyObject_HEAD_INIT(NULL)
+ /* ob_size */ 0,
+ /* tp_name */ "thread._local",
+ /* tp_basicsize */ sizeof(localobject),
+ /* tp_itemsize */ 0,
+ /* tp_dealloc */ (destructor)local_dealloc,
+ /* tp_print */ (printfunc)0,
+ /* tp_getattr */ (getattrfunc)0,
+ /* tp_setattr */ (setattrfunc)0,
+ /* tp_compare */ (cmpfunc)0,
+ /* tp_repr */ (reprfunc)0,
+ /* tp_as_number */ 0,
+ /* tp_as_sequence */ 0,
+ /* tp_as_mapping */ 0,
+ /* tp_hash */ (hashfunc)0,
+ /* tp_call */ (ternaryfunc)0,
+ /* tp_str */ (reprfunc)0,
+ /* tp_getattro */ (getattrofunc)local_getattro,
+ /* tp_setattro */ (setattrofunc)local_setattro,
+ /* tp_as_buffer */ 0,
+ /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ /* tp_doc */ "Thread-local data",
+ /* tp_traverse */ (traverseproc)local_traverse,
+ /* tp_clear */ (inquiry)local_clear,
+ /* tp_richcompare */ (richcmpfunc)0,
+ /* tp_weaklistoffset */ (long)0,
+ /* tp_iter */ (getiterfunc)0,
+ /* tp_iternext */ (iternextfunc)0,
+ /* tp_methods */ 0,
+ /* tp_members */ 0,
+ /* tp_getset */ local_getset,
+ /* tp_base */ 0,
+ /* tp_dict */ 0, /* internal use */
+ /* tp_descr_get */ (descrgetfunc)0,
+ /* tp_descr_set */ (descrsetfunc)0,
+ /* tp_dictoffset */ offsetof(localobject, dict),
+ /* tp_init */ (initproc)0,
+ /* tp_alloc */ (allocfunc)0,
+ /* tp_new */ (newfunc)local_new,
+ /* tp_free */ 0, /* Low-level free-mem routine */
+ /* tp_is_gc */ (inquiry)0, /* For PyObject_IS_GC */
+};
+
/* Module functions */
@@ -389,6 +642,10 @@ PyMODINIT_FUNC
initthread(void)
{
PyObject *m, *d;
+
+ /* Initialize types: */
+ if (PyType_Ready(&localtype) < 0)
+ return;
/* Create the module and add the functions */
m = Py_InitModule3("thread", thread_methods, thread_doc);
@@ -401,6 +658,9 @@ initthread(void)
Py_INCREF(&Locktype);
PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype);
+ if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0)
+ return;
+
/* Initialize the C thread library */
PyThread_init_thread();
}