You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebindings/python/sip/siplib/threads.c

224 lines
4.6 KiB

/*
* 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