Spaces:
Runtime error
Runtime error
/***** Support code for embedding *****/ | |
extern "C" { | |
/* There are two global variables of type _cffi_call_python_fnptr: | |
* _cffi_call_python, which we declare just below, is the one called | |
by ``extern "Python"`` implementations. | |
* _cffi_call_python_org, which on CPython is actually part of the | |
_cffi_exports[] array, is the function pointer copied from | |
_cffi_backend. If _cffi_start_python() fails, then this is set | |
to NULL; otherwise, it should never be NULL. | |
After initialization is complete, both are equal. However, the | |
first one remains equal to &_cffi_start_and_call_python until the | |
very end of initialization, when we are (or should be) sure that | |
concurrent threads also see a completely initialized world, and | |
only then is it changed. | |
*/ | |
typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *); | |
static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *); | |
static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python; | |
/* --- Assuming a GCC not infinitely old --- */ | |
/* --- Windows threads version --- */ | |
static volatile LONG _cffi_dummy; | |
static pthread_mutex_t _cffi_embed_startup_lock; | |
static CRITICAL_SECTION _cffi_embed_startup_lock; | |
static char _cffi_embed_startup_lock_ready = 0; | |
static void _cffi_acquire_reentrant_mutex(void) | |
{ | |
static void *volatile lock = NULL; | |
while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) { | |
/* should ideally do a spin loop instruction here, but | |
hard to do it portably and doesn't really matter I | |
think: pthread_mutex_init() should be very fast, and | |
this is only run at start-up anyway. */ | |
} | |
if (!_cffi_embed_startup_lock_ready) { | |
pthread_mutexattr_t attr; | |
pthread_mutexattr_init(&attr); | |
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); | |
pthread_mutex_init(&_cffi_embed_startup_lock, &attr); | |
InitializeCriticalSection(&_cffi_embed_startup_lock); | |
_cffi_embed_startup_lock_ready = 1; | |
} | |
while (!cffi_compare_and_swap(&lock, (void *)1, NULL)) | |
; | |
pthread_mutex_lock(&_cffi_embed_startup_lock); | |
EnterCriticalSection(&_cffi_embed_startup_lock); | |
} | |
static void _cffi_release_reentrant_mutex(void) | |
{ | |
pthread_mutex_unlock(&_cffi_embed_startup_lock); | |
LeaveCriticalSection(&_cffi_embed_startup_lock); | |
} | |
/********** CPython-specific section **********/ | |
PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void); /* forward */ | |
static void _cffi_py_initialize(void) | |
{ | |
/* XXX use initsigs=0, which "skips initialization registration of | |
signal handlers, which might be useful when Python is | |
embedded" according to the Python docs. But review and think | |
if it should be a user-controllable setting. | |
XXX we should also give a way to write errors to a buffer | |
instead of to stderr. | |
XXX if importing 'site' fails, CPython (any version) calls | |
exit(). Should we try to work around this behavior here? | |
*/ | |
Py_InitializeEx(0); | |
} | |
static int _cffi_initialize_python(void) | |
{ | |
/* This initializes Python, imports _cffi_backend, and then the | |
present .dll/.so is set up as a CPython C extension module. | |
*/ | |
int result; | |
PyGILState_STATE state; | |
PyObject *pycode=NULL, *global_dict=NULL, *x; | |
PyObject *builtins; | |
state = PyGILState_Ensure(); | |
/* Call the initxxx() function from the present module. It will | |
create and initialize us as a CPython extension module, instead | |
of letting the startup Python code do it---it might reimport | |
the same .dll/.so and get maybe confused on some platforms. | |
It might also have troubles locating the .dll/.so again for all | |
I know. | |
*/ | |
(void)_CFFI_PYTHON_STARTUP_FUNC(); | |
if (PyErr_Occurred()) | |
goto error; | |
/* Now run the Python code provided to ffi.embedding_init_code(). | |
*/ | |
pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE, | |
"<init code for '" _CFFI_MODULE_NAME "'>", | |
Py_file_input); | |
if (pycode == NULL) | |
goto error; | |
global_dict = PyDict_New(); | |
if (global_dict == NULL) | |
goto error; | |
builtins = PyEval_GetBuiltins(); | |
if (builtins == NULL) | |
goto error; | |
if (PyDict_SetItemString(global_dict, "__builtins__", builtins) < 0) | |
goto error; | |
x = PyEval_EvalCode( | |
(PyCodeObject *) | |
pycode, global_dict, global_dict); | |
if (x == NULL) | |
goto error; | |
Py_DECREF(x); | |
/* Done! Now if we've been called from | |
_cffi_start_and_call_python() in an ``extern "Python"``, we can | |
only hope that the Python code did correctly set up the | |
corresponding @ffi.def_extern() function. Otherwise, the | |
general logic of ``extern "Python"`` functions (inside the | |
_cffi_backend module) will find that the reference is still | |
missing and print an error. | |
*/ | |
result = 0; | |
done: | |
Py_XDECREF(pycode); | |
Py_XDECREF(global_dict); | |
PyGILState_Release(state); | |
return result; | |
error:; | |
{ | |
/* Print as much information as potentially useful. | |
Debugging load-time failures with embedding is not fun | |
*/ | |
PyObject *ecap; | |
PyObject *exception, *v, *tb, *f, *modules, *mod; | |
PyErr_Fetch(&exception, &v, &tb); | |
ecap = _cffi_start_error_capture(); | |
f = PySys_GetObject((char *)"stderr"); | |
if (f != NULL && f != Py_None) { | |
PyFile_WriteString( | |
"Failed to initialize the Python-CFFI embedding logic:\n\n", f); | |
} | |
if (exception != NULL) { | |
PyErr_NormalizeException(&exception, &v, &tb); | |
PyErr_Display(exception, v, tb); | |
} | |
Py_XDECREF(exception); | |
Py_XDECREF(v); | |
Py_XDECREF(tb); | |
if (f != NULL && f != Py_None) { | |
PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME | |
"\ncompiled with cffi version: 1.15.1" | |
"\n_cffi_backend module: ", f); | |
modules = PyImport_GetModuleDict(); | |
mod = PyDict_GetItemString(modules, "_cffi_backend"); | |
if (mod == NULL) { | |
PyFile_WriteString("not loaded", f); | |
} | |
else { | |
v = PyObject_GetAttrString(mod, "__file__"); | |
PyFile_WriteObject(v, f, 0); | |
Py_XDECREF(v); | |
} | |
PyFile_WriteString("\nsys.path: ", f); | |
PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0); | |
PyFile_WriteString("\n\n", f); | |
} | |
_cffi_stop_error_capture(ecap); | |
} | |
result = -1; | |
goto done; | |
} | |
PyAPI_DATA(char *) _PyParser_TokenNames[]; /* from CPython */ | |
static int _cffi_carefully_make_gil(void) | |
{ | |
/* This does the basic initialization of Python. It can be called | |
completely concurrently from unrelated threads. It assumes | |
that we don't hold the GIL before (if it exists), and we don't | |
hold it afterwards. | |
(What it really does used to be completely different in Python 2 | |
and Python 3, with the Python 2 solution avoiding the spin-lock | |
around the Py_InitializeEx() call. However, after recent changes | |
to CPython 2.7 (issue #358) it no longer works. So we use the | |
Python 3 solution everywhere.) | |
This initializes Python by calling Py_InitializeEx(). | |
Important: this must not be called concurrently at all. | |
So we use a global variable as a simple spin lock. This global | |
variable must be from 'libpythonX.Y.so', not from this | |
cffi-based extension module, because it must be shared from | |
different cffi-based extension modules. | |
In Python < 3.8, we choose | |
_PyParser_TokenNames[0] as a completely arbitrary pointer value | |
that is never written to. The default is to point to the | |
string "ENDMARKER". We change it temporarily to point to the | |
next character in that string. (Yes, I know it's REALLY | |
obscure.) | |
In Python >= 3.8, this string array is no longer writable, so | |
instead we pick PyCapsuleType.tp_version_tag. We can't change | |
Python < 3.8 because someone might use a mixture of cffi | |
embedded modules, some of which were compiled before this file | |
changed. | |
*/ | |
char *volatile *lock = (char *volatile *)_PyParser_TokenNames; | |
char *old_value, *locked_value; | |
while (1) { /* spin loop */ | |
old_value = *lock; | |
locked_value = old_value + 1; | |
if (old_value[0] == 'E') { | |
assert(old_value[1] == 'N'); | |
if (cffi_compare_and_swap(lock, old_value, locked_value)) | |
break; | |
} | |
else { | |
assert(old_value[0] == 'N'); | |
/* should ideally do a spin loop instruction here, but | |
hard to do it portably and doesn't really matter I | |
think: PyEval_InitThreads() should be very fast, and | |
this is only run at start-up anyway. */ | |
} | |
} | |
int volatile *lock = (int volatile *)&PyCapsule_Type.tp_version_tag; | |
int old_value, locked_value; | |
assert(!(PyCapsule_Type.tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG)); | |
while (1) { /* spin loop */ | |
old_value = *lock; | |
locked_value = -42; | |
if (old_value == 0) { | |
if (cffi_compare_and_swap(lock, old_value, locked_value)) | |
break; | |
} | |
else { | |
assert(old_value == locked_value); | |
/* should ideally do a spin loop instruction here, but | |
hard to do it portably and doesn't really matter I | |
think: PyEval_InitThreads() should be very fast, and | |
this is only run at start-up anyway. */ | |
} | |
} | |
/* call Py_InitializeEx() */ | |
if (!Py_IsInitialized()) { | |
_cffi_py_initialize(); | |
PyEval_InitThreads(); | |
PyEval_SaveThread(); /* release the GIL */ | |
/* the returned tstate must be the one that has been stored into the | |
autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */ | |
} | |
else { | |
/* PyEval_InitThreads() is always a no-op from CPython 3.7 */ | |
PyGILState_STATE state = PyGILState_Ensure(); | |
PyEval_InitThreads(); | |
PyGILState_Release(state); | |
} | |
/* release the lock */ | |
while (!cffi_compare_and_swap(lock, locked_value, old_value)) | |
; | |
return 0; | |
} | |
/********** end CPython-specific section **********/ | |
/********** PyPy-specific section **********/ | |
PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ | |
static struct _cffi_pypy_init_s { | |
const char *name; | |
void *func; /* function pointer */ | |
const char *code; | |
} _cffi_pypy_init = { | |
_CFFI_MODULE_NAME, | |
_CFFI_PYTHON_STARTUP_FUNC, | |
_CFFI_PYTHON_STARTUP_CODE, | |
}; | |
extern int pypy_carefully_make_gil(const char *); | |
extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *); | |
static int _cffi_carefully_make_gil(void) | |
{ | |
return pypy_carefully_make_gil(_CFFI_MODULE_NAME); | |
} | |
static int _cffi_initialize_python(void) | |
{ | |
return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init); | |
} | |
/********** end PyPy-specific section **********/ | |
__attribute__((noinline)) | |
static _cffi_call_python_fnptr _cffi_start_python(void) | |
{ | |
/* Delicate logic to initialize Python. This function can be | |
called multiple times concurrently, e.g. when the process calls | |
its first ``extern "Python"`` functions in multiple threads at | |
once. It can also be called recursively, in which case we must | |
ignore it. We also have to consider what occurs if several | |
different cffi-based extensions reach this code in parallel | |
threads---it is a different copy of the code, then, and we | |
can't have any shared global variable unless it comes from | |
'libpythonX.Y.so'. | |
Idea: | |
* _cffi_carefully_make_gil(): "carefully" call | |
PyEval_InitThreads() (possibly with Py_InitializeEx() first). | |
* then we use a (local) custom lock to make sure that a call to this | |
cffi-based extension will wait if another call to the *same* | |
extension is running the initialization in another thread. | |
It is reentrant, so that a recursive call will not block, but | |
only one from a different thread. | |
* then we grab the GIL and (Python 2) we call Py_InitializeEx(). | |
At this point, concurrent calls to Py_InitializeEx() are not | |
possible: we have the GIL. | |
* do the rest of the specific initialization, which may | |
temporarily release the GIL but not the custom lock. | |
Only release the custom lock when we are done. | |
*/ | |
static char called = 0; | |
if (_cffi_carefully_make_gil() != 0) | |
return NULL; | |
_cffi_acquire_reentrant_mutex(); | |
/* Here the GIL exists, but we don't have it. We're only protected | |
from concurrency by the reentrant mutex. */ | |
/* This file only initializes the embedded module once, the first | |
time this is called, even if there are subinterpreters. */ | |
if (!called) { | |
called = 1; /* invoke _cffi_initialize_python() only once, | |
but don't set '_cffi_call_python' right now, | |
otherwise concurrent threads won't call | |
this function at all (we need them to wait) */ | |
if (_cffi_initialize_python() == 0) { | |
/* now initialization is finished. Switch to the fast-path. */ | |
/* We would like nobody to see the new value of | |
'_cffi_call_python' without also seeing the rest of the | |
data initialized. However, this is not possible. But | |
the new value of '_cffi_call_python' is the function | |
'cffi_call_python()' from _cffi_backend. So: */ | |
cffi_write_barrier(); | |
/* ^^^ we put a write barrier here, and a corresponding | |
read barrier at the start of cffi_call_python(). This | |
ensures that after that read barrier, we see everything | |
done here before the write barrier. | |
*/ | |
assert(_cffi_call_python_org != NULL); | |
_cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org; | |
} | |
else { | |
/* initialization failed. Reset this to NULL, even if it was | |
already set to some other value. Future calls to | |
_cffi_start_python() are still forced to occur, and will | |
always return NULL from now on. */ | |
_cffi_call_python_org = NULL; | |
} | |
} | |
_cffi_release_reentrant_mutex(); | |
return (_cffi_call_python_fnptr)_cffi_call_python_org; | |
} | |
static | |
void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args) | |
{ | |
_cffi_call_python_fnptr fnptr; | |
int current_err = errno; | |
int current_lasterr = GetLastError(); | |
fnptr = _cffi_start_python(); | |
if (fnptr == NULL) { | |
fprintf(stderr, "function %s() called, but initialization code " | |
"failed. Returning 0.\n", externpy->name); | |
memset(args, 0, externpy->size_of_result); | |
} | |
SetLastError(current_lasterr); | |
errno = current_err; | |
if (fnptr != NULL) | |
fnptr(externpy, args); | |
} | |
/* The cffi_start_python() function makes sure Python is initialized | |
and our cffi module is set up. It can be called manually from the | |
user C code. The same effect is obtained automatically from any | |
dll-exported ``extern "Python"`` function. This function returns | |
-1 if initialization failed, 0 if all is OK. */ | |
_CFFI_UNUSED_FN | |
static int cffi_start_python(void) | |
{ | |
if (_cffi_call_python == &_cffi_start_and_call_python) { | |
if (_cffi_start_python() == NULL) | |
return -1; | |
} | |
cffi_read_barrier(); | |
return 0; | |
} | |
} | |