diff options
Diffstat (limited to 'python/sip/siplib/threads.c')
-rw-r--r-- | python/sip/siplib/threads.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/python/sip/siplib/threads.c b/python/sip/siplib/threads.c new file mode 100644 index 00000000..c4ee75ff --- /dev/null +++ b/python/sip/siplib/threads.c @@ -0,0 +1,223 @@ +/* + * Thread support for the SIP library. This module provides the hooks for + * C++ classes that provide a thread interface to interact properly with the + * Python threading infrastructure. + * + * Copyright (c) 2007 + * Riverbank Computing Limited <info@riverbankcomputing.co.uk> + * + * This file is part of SIP. + * + * This copy of SIP is licensed for use under the terms of the SIP License + * Agreement. See the file LICENSE for more details. + * + * SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#include "sip.h" +#include "sipint.h" + + +/* + * The data associated with pending request to wrap an object. + */ +typedef struct _pendingDef { + void *cpp; /* The C/C++ object ot be wrapped. */ + sipWrapper *owner; /* The owner of the object. */ + int flags; /* The flags. */ +} pendingDef; + + +#ifdef WITH_THREAD + +#include <pythread.h> + + +/* + * The per thread data we need to maintain. + */ +typedef struct _threadDef { + long thr_ident; /* The thread identifier. */ + pendingDef pending; /* An object waiting to be wrapped. */ + struct _threadDef *next; /* Next in the list. */ +} threadDef; + + +static threadDef *threads = NULL; /* Linked list of threads. */ + + +static threadDef *currentThreadDef(void); + +#endif + + +static pendingDef pending; /* An object waiting to be wrapped. */ + + +/* + * Get the address of any C/C++ object waiting to be wrapped. + */ +void *sipGetPending(sipWrapper **op, int *fp) +{ + pendingDef *pp; + +#ifdef WITH_THREAD + threadDef *td; + + if ((td = currentThreadDef()) != NULL) + pp = &td->pending; + else + pp = &pending; +#else + pp = &pending; +#endif + + if (pp->cpp != NULL) + { + if (op != NULL) + *op = pp->owner; + + if (fp != NULL) + *fp = pp->flags; + } + + return pp->cpp; +} + + +/* + * Convert a new C/C++ pointer to a Python instance. + */ +PyObject *sipWrapSimpleInstance(void *cppPtr, sipWrapperType *type, + sipWrapper *owner, int flags) +{ + static PyObject *nullargs = NULL; + + pendingDef old_pending; + PyObject *self; +#ifdef WITH_THREAD + threadDef *td; +#endif + + if (nullargs == NULL && (nullargs = PyTuple_New(0)) == NULL) + return NULL; + + if (cppPtr == NULL) + { + Py_INCREF(Py_None); + return Py_None; + } + + /* + * Object creation can trigger the Python garbage collector which in turn + * can execute arbitrary Python code which can then call this function + * recursively. Therefore we save any existing pending object before + * setting the new one. + */ +#ifdef WITH_THREAD + if ((td = currentThreadDef()) != NULL) + { + old_pending = td->pending; + + td->pending.cpp = cppPtr; + td->pending.owner = owner; + td->pending.flags = flags; + } + else + { + old_pending = pending; + + pending.cpp = cppPtr; + pending.owner = owner; + pending.flags = flags; + } +#else + old_pending = pending; + + pending.cpp = cppPtr; + pending.owner = owner; + pending.flags = flags; +#endif + + self = PyObject_Call((PyObject *)type, nullargs, NULL); + +#ifdef WITH_THREAD + if (td != NULL) + td->pending = old_pending; + else + pending = old_pending; +#else + pending = old_pending; +#endif + + return self; +} + + +/* + * This is called from a newly created thread to initialise some thread local + * storage. + */ +void sip_api_start_thread(void) +{ +#ifdef WITH_THREAD + threadDef *td; + + /* Save the thread ID. First, find an empty slot in the list. */ + for (td = threads; td != NULL; td = td->next) + if (td->thr_ident == 0) + break; + + if (td == NULL) + { + td = sip_api_malloc(sizeof (threadDef)); + td->next = threads; + threads = td; + } + + if (td != NULL) + { + td->thr_ident = PyThread_get_thread_ident(); + td->pending.cpp = NULL; + } +#endif +} + + +/* + * Handle the termination of a thread. The thread state should already have + * been handled by the last call to PyGILState_Release(). + */ +void sip_api_end_thread(void) +{ +#ifdef WITH_THREAD + threadDef *td; + + /* We have the GIL at this point. */ + if ((td = currentThreadDef()) != NULL) + td->thr_ident = 0; +#endif +} + + +#ifdef WITH_THREAD + +/* + * Return the thread data for the current thread or NULL if it wasn't + * recognised. + */ +static threadDef *currentThreadDef(void) +{ + threadDef *td; + long ident = PyThread_get_thread_ident(); + + for (td = threads; td != NULL; td = td->next) + if (td->thr_ident == ident) + break; + + return td; +} + +#endif |