Files
Phoenix/sip/siplib/threads.c
Robin Dunn 7e90570bbd Update to sip 4.14.4
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@73655 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2013-03-14 01:15:43 +00:00

238 lines
5.1 KiB
C

/*
* 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) 2013 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* 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.
*
* This copy of SIP may also used under the terms of the GNU General Public
* License v2 or v3 as published by the Free Software Foundation which can be
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
*
* 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(int auto_alloc);
#endif
static pendingDef *get_pending(int auto_alloc);
/*
* Get the address etc. of any C/C++ object waiting to be wrapped.
*/
int sipGetPending(void **pp, sipWrapper **op, int *fp)
{
pendingDef *pd;
if ((pd = get_pending(TRUE)) == NULL)
return -1;
*pp = pd->cpp;
*op = pd->owner;
*fp = pd->flags;
return 0;
}
/*
* Return TRUE if anything is pending.
*/
int sipIsPending()
{
pendingDef *pd;
if ((pd = get_pending(FALSE)) == NULL)
return FALSE;
return (pd->cpp != NULL);
}
/*
* Convert a new C/C++ pointer to a Python instance.
*/
PyObject *sipWrapSimpleInstance(void *cppPtr, const sipTypeDef *td,
sipWrapper *owner, int flags)
{
static PyObject *nullargs = NULL;
pendingDef old_pending, *pd;
PyObject *self;
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.
*/
if ((pd = get_pending(TRUE)) == NULL)
return NULL;
old_pending = *pd;
pd->cpp = cppPtr;
pd->owner = owner;
pd->flags = flags;
self = PyObject_Call((PyObject *)sipTypeAsPyTypeObject(td), nullargs, NULL);
*pd = old_pending;
return self;
}
/*
* This is called from a newly created thread to initialise some thread local
* storage.
*/
#if SIP_API_MAJOR_NR != 9
#error Remove deprecated sip_api_start_thread().
#endif
void sip_api_start_thread(void)
{
/*
* The thread local storage allocation now happens when it is needed, so
* this is now redundant.
*/
}
/*
* Handle the termination of a thread.
*/
void sip_api_end_thread(void)
{
#ifdef WITH_THREAD
threadDef *thread;
PyGILState_STATE gil = PyGILState_Ensure();
if ((thread = currentThreadDef(FALSE)) != NULL)
thread->thr_ident = 0;
PyGILState_Release(gil);
#endif
}
/*
* Return the pending data for the current thread, allocating it if necessary,
* or NULL if there was an error.
*/
static pendingDef *get_pending(int auto_alloc)
{
#ifdef WITH_THREAD
threadDef *thread;
if ((thread = currentThreadDef(auto_alloc)) == NULL)
return NULL;
return &thread->pending;
#else
static pendingDef pending;
return &pending;
#endif
}
#ifdef WITH_THREAD
/*
* Return the thread data for the current thread, allocating it if necessary,
* or NULL if there was an error.
*/
static threadDef *currentThreadDef(int auto_alloc)
{
threadDef *thread, *empty = NULL;
long ident = PyThread_get_thread_ident();
/* See if we already know about the thread. */
for (thread = threads; thread != NULL; thread = thread->next)
{
if (thread->thr_ident == ident)
return thread;
if (thread->thr_ident == 0)
empty = thread;
}
if (!auto_alloc)
{
/* This is not an error. */
return NULL;
}
if (empty != NULL)
{
/* Use an empty entry in the list. */
thread = empty;
}
else if ((thread = sip_api_malloc(sizeof (threadDef))) == NULL)
{
return NULL;
}
else
{
thread->next = threads;
threads = thread;
}
thread->thr_ident = ident;
thread->pending.cpp = NULL;
return thread;
}
#endif