/* Copyright 2000-2001 ActiveState
*/
/* #define NO_PERL_LOCK /**/
/* #define LOCK_DEBUG /**/
#if !defined(NO_PERL_LOCK) && defined(WITH_THREAD)
#define DO_THREAD /* a simpler macro to test for in files that include this */
#endif
#ifdef LOCK_DEBUG
#ifdef DO_THREAD
/* XXX these test are not valid in a multi-thread environment.
* Should test if the current thread has the given lock.
*/
#error "LOCK_DEBUG with real threads doesn't work"
extern int perl_lock_held();
#define PERL_LOCK_HELD perl_lock_held()
#define PYTHON_LOCK_HELD (last_py_tstate == NULL)
#else /* DO_THREAD */
#define PERL_LOCK_HELD perl_lock_held
#define PYTHON_LOCK_HELD python_lock_held
#endif /* DO_THREAD */
extern void Lock_Fatal_Error(const char*, const char*, int);
#define LOCK_FATAL_ERROR(msg) Lock_Fatal_Error(msg, __FILE__, __LINE__)
/* Poor mans Eiffel :-) */
#define ASSERT_LOCK_PERL \
do { \
if (!PERL_LOCK_HELD || PYTHON_LOCK_HELD) \
LOCK_FATAL_ERROR("Only perl lock should be held"); \
} while (0)
#define ASSERT_LOCK_PYTHON \
do { \
if (PERL_LOCK_HELD || !PYTHON_LOCK_HELD) \
LOCK_FATAL_ERROR("Only python lock should be held"); \
} while (0)
#define ASSERT_LOCK_BOTH \
do { \
if (!PERL_LOCK_HELD || !PYTHON_LOCK_HELD) \
LOCK_FATAL_ERROR("Both perl and python lock should be held"); \
} while (0)
#else /* LOCK_DEBUG */
/* All assertions expand to nothing */
#define ASSERT_LOCK_PERL
#define ASSERT_LOCK_PYTHON
#define ASSERT_LOCK_BOTH
extern void lang_lock_init(void);
#endif /* LOCK_DEBUG */
#ifdef DO_THREAD
#ifdef MULTI_PERL
extern perl_key last_py_tstate;
/* These assume that the thread specific 'ctx' pointer is
* in scope.
*/
#define ENTER_PERL do { ctx->last_py_state = PyEval_SaveThread(); \
} while (0)
#define ENTER_PYTHON do { PyEval_RestoreThread(ctx->last_py_state); \
} while (0)
#define PERL_LOCK /* nothing */
#define PERL_UNLOCK /* nothing */
#define PYTHON_LOCK ENTER_PYTHON
#define PYTHON_UNLOCK ENTER_PERL
#else /* MULTI_PERL */
/* This stuff is similar to the threading macros you find in _tkinter.c,
* but not exactly the same :-)
*/
#include "pythread.h"
extern PyThread_type_lock perl_lock;
extern PyThreadState *last_py_tstate;
/* The locking rules are:
1) All code should be protected by one (or more) locks.
2) When we execute python code we should have the python lock and
*not* have the perl lock
3) When we execute perl code we should have the perl lock and *not*
the python lock
4) You can have both locks while executing API functions that does
not risk invoking arbitrary perl/python code.
5) Lot of API functions might invoke perl/python callbacks through
hooks, overloading or tying. These must only be invoked with
the correct single lock.
*/
/* This one should only be called when you are sure you have the python lock,
and not the perl lock. The result is that you have perl lock.
*/
#define ENTER_PERL do { PyThreadState *tstate; \
ASSERT_LOCK_PYTHON; \
tstate = PyEval_SaveThread(); \
PyThread_acquire_lock(perl_lock, 1); \
last_py_tstate = tstate; \
} while (0)
/* This one should only be called when you are sure you have the perl lock,
and not the python lock. The result is that you have the python lock.
*/
#define ENTER_PYTHON do { PyThreadState *tstate; \
ASSERT_LOCK_PERL; \
tstate = last_py_tstate; \
last_py_tstate = NULL; \
PyThread_release_lock(perl_lock); \
PyEval_RestoreThread(tstate); \
} while (0)
/* These can only be called while you have the python lock */
#define PERL_LOCK do { ASSERT_LOCK_PYTHON; \
while (!PyThread_acquire_lock(perl_lock, 0)) { \
ENTER_PERL; \
ENTER_PYTHON; \
} \
} while (0)
#define PERL_UNLOCK do { ASSERT_LOCK_BOTH; \
PyThread_release_lock(perl_lock); \
} while (0)
/* These can only be called while you have the perl lock */
#define PYTHON_LOCK do { ENTER_PYTHON; PERL_LOCK; } while (0)
#define PYTHON_UNLOCK \
do { ASSERT_LOCK_BOTH; \
if (last_py_tstate != NULL) \
Py_FatalError("PYTHON_UNLOCK: non-NULL tstate"); \
last_py_tstate = PyEval_SaveThread(); \
} while (0)
/* The reason PYTHON_LOCK is defined as it is, is that we should always
* obtain locks in the same sequence in order to avoid potential deadlock.
*/
#endif /* MULTI_PERL */
#else /* DO_THREAD */
#ifdef LOCK_DEBUG
extern int perl_lock_held;
extern int python_lock_held;
#define ENTER_PERL \
do { \
ASSERT_LOCK_PYTHON; \
python_lock_held = 0; \
perl_lock_held = 1; \
} while (0) \
#define ENTER_PYTHON \
do { \
ASSERT_LOCK_PERL; \
python_lock_held = 1; \
perl_lock_held = 0; \
} while (0)
#define PERL_LOCK \
do { \
ASSERT_LOCK_PYTHON; \
perl_lock_held = 1; \
} while (0)
#define PERL_UNLOCK \
do { \
ASSERT_LOCK_BOTH; \
perl_lock_held = 0; \
} while (0)
#define PYTHON_LOCK \
do { \
ASSERT_LOCK_PERL; \
python_lock_held = 1; \
} while (0)
#define PYTHON_UNLOCK \
do { \
ASSERT_LOCK_BOTH; \
python_lock_held = 0; \
} while (0)
#else /* LOCK_DEBUG */
/* No threads and no debug, all macros expand to nothing */
#define ENTER_PERL
#define ENTER_PYTHON
#define PERL_LOCK
#define PERL_UNLOCK
#define PYTHON_UNLOCK
#define PYTHON_LOCK
#endif /* LOCK_DEBUG */
#endif /* DO_THREAD */