github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/packaging/ci/lambda/GitPullS3/cffi/_embedding.h (about) 1 2 /***** Support code for embedding *****/ 3 4 #if defined(_MSC_VER) 5 # define CFFI_DLLEXPORT __declspec(dllexport) 6 #elif defined(__GNUC__) 7 # define CFFI_DLLEXPORT __attribute__((visibility("default"))) 8 #else 9 # define CFFI_DLLEXPORT /* nothing */ 10 #endif 11 12 13 /* There are two global variables of type _cffi_call_python_fnptr: 14 15 * _cffi_call_python, which we declare just below, is the one called 16 by ``extern "Python"`` implementations. 17 18 * _cffi_call_python_org, which on CPython is actually part of the 19 _cffi_exports[] array, is the function pointer copied from 20 _cffi_backend. 21 22 After initialization is complete, both are equal. However, the 23 first one remains equal to &_cffi_start_and_call_python until the 24 very end of initialization, when we are (or should be) sure that 25 concurrent threads also see a completely initialized world, and 26 only then is it changed. 27 */ 28 #undef _cffi_call_python 29 typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *); 30 static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *); 31 static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python; 32 33 34 #ifndef _MSC_VER 35 /* --- Assuming a GCC not infinitely old --- */ 36 # define cffi_compare_and_swap(l,o,n) __sync_bool_compare_and_swap(l,o,n) 37 # define cffi_write_barrier() __sync_synchronize() 38 # if !defined(__amd64__) && !defined(__x86_64__) && \ 39 !defined(__i386__) && !defined(__i386) 40 # define cffi_read_barrier() __sync_synchronize() 41 # else 42 # define cffi_read_barrier() (void)0 43 # endif 44 #else 45 /* --- Windows threads version --- */ 46 # include <Windows.h> 47 # define cffi_compare_and_swap(l,o,n) \ 48 (InterlockedCompareExchangePointer(l,n,o) == (o)) 49 # define cffi_write_barrier() InterlockedCompareExchange(&_cffi_dummy,0,0) 50 # define cffi_read_barrier() (void)0 51 static volatile LONG _cffi_dummy; 52 #endif 53 54 #ifdef WITH_THREAD 55 # ifndef _MSC_VER 56 # include <pthread.h> 57 static pthread_mutex_t _cffi_embed_startup_lock; 58 # else 59 static CRITICAL_SECTION _cffi_embed_startup_lock; 60 # endif 61 static char _cffi_embed_startup_lock_ready = 0; 62 #endif 63 64 static void _cffi_acquire_reentrant_mutex(void) 65 { 66 static void *volatile lock = NULL; 67 68 while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) { 69 /* should ideally do a spin loop instruction here, but 70 hard to do it portably and doesn't really matter I 71 think: pthread_mutex_init() should be very fast, and 72 this is only run at start-up anyway. */ 73 } 74 75 #ifdef WITH_THREAD 76 if (!_cffi_embed_startup_lock_ready) { 77 # ifndef _MSC_VER 78 pthread_mutexattr_t attr; 79 pthread_mutexattr_init(&attr); 80 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 81 pthread_mutex_init(&_cffi_embed_startup_lock, &attr); 82 # else 83 InitializeCriticalSection(&_cffi_embed_startup_lock); 84 # endif 85 _cffi_embed_startup_lock_ready = 1; 86 } 87 #endif 88 89 while (!cffi_compare_and_swap(&lock, (void *)1, NULL)) 90 ; 91 92 #ifndef _MSC_VER 93 pthread_mutex_lock(&_cffi_embed_startup_lock); 94 #else 95 EnterCriticalSection(&_cffi_embed_startup_lock); 96 #endif 97 } 98 99 static void _cffi_release_reentrant_mutex(void) 100 { 101 #ifndef _MSC_VER 102 pthread_mutex_unlock(&_cffi_embed_startup_lock); 103 #else 104 LeaveCriticalSection(&_cffi_embed_startup_lock); 105 #endif 106 } 107 108 109 /********** CPython-specific section **********/ 110 #ifndef PYPY_VERSION 111 112 113 #define _cffi_call_python_org _cffi_exports[_CFFI_CPIDX] 114 115 PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void); /* forward */ 116 117 static void _cffi_py_initialize(void) 118 { 119 /* XXX use initsigs=0, which "skips initialization registration of 120 signal handlers, which might be useful when Python is 121 embedded" according to the Python docs. But review and think 122 if it should be a user-controllable setting. 123 124 XXX we should also give a way to write errors to a buffer 125 instead of to stderr. 126 127 XXX if importing 'site' fails, CPython (any version) calls 128 exit(). Should we try to work around this behavior here? 129 */ 130 Py_InitializeEx(0); 131 } 132 133 static int _cffi_initialize_python(void) 134 { 135 /* This initializes Python, imports _cffi_backend, and then the 136 present .dll/.so is set up as a CPython C extension module. 137 */ 138 int result; 139 PyGILState_STATE state; 140 PyObject *pycode=NULL, *global_dict=NULL, *x; 141 142 #if PY_MAJOR_VERSION >= 3 143 /* see comments in _cffi_carefully_make_gil() about the 144 Python2/Python3 difference 145 */ 146 #else 147 /* Acquire the GIL. We have no threadstate here. If Python is 148 already initialized, it is possible that there is already one 149 existing for this thread, but it is not made current now. 150 */ 151 PyEval_AcquireLock(); 152 153 _cffi_py_initialize(); 154 155 /* The Py_InitializeEx() sometimes made a threadstate for us, but 156 not always. Indeed Py_InitializeEx() could be called and do 157 nothing. So do we have a threadstate, or not? We don't know, 158 but we can replace it with NULL in all cases. 159 */ 160 (void)PyThreadState_Swap(NULL); 161 162 /* Now we can release the GIL and re-acquire immediately using the 163 logic of PyGILState(), which handles making or installing the 164 correct threadstate. 165 */ 166 PyEval_ReleaseLock(); 167 #endif 168 state = PyGILState_Ensure(); 169 170 /* Call the initxxx() function from the present module. It will 171 create and initialize us as a CPython extension module, instead 172 of letting the startup Python code do it---it might reimport 173 the same .dll/.so and get maybe confused on some platforms. 174 It might also have troubles locating the .dll/.so again for all 175 I know. 176 */ 177 (void)_CFFI_PYTHON_STARTUP_FUNC(); 178 if (PyErr_Occurred()) 179 goto error; 180 181 /* Now run the Python code provided to ffi.embedding_init_code(). 182 */ 183 pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE, 184 "<init code for '" _CFFI_MODULE_NAME "'>", 185 Py_file_input); 186 if (pycode == NULL) 187 goto error; 188 global_dict = PyDict_New(); 189 if (global_dict == NULL) 190 goto error; 191 if (PyDict_SetItemString(global_dict, "__builtins__", 192 PyThreadState_GET()->interp->builtins) < 0) 193 goto error; 194 x = PyEval_EvalCode( 195 #if PY_MAJOR_VERSION < 3 196 (PyCodeObject *) 197 #endif 198 pycode, global_dict, global_dict); 199 if (x == NULL) 200 goto error; 201 Py_DECREF(x); 202 203 /* Done! Now if we've been called from 204 _cffi_start_and_call_python() in an ``extern "Python"``, we can 205 only hope that the Python code did correctly set up the 206 corresponding @ffi.def_extern() function. Otherwise, the 207 general logic of ``extern "Python"`` functions (inside the 208 _cffi_backend module) will find that the reference is still 209 missing and print an error. 210 */ 211 result = 0; 212 done: 213 Py_XDECREF(pycode); 214 Py_XDECREF(global_dict); 215 PyGILState_Release(state); 216 return result; 217 218 error:; 219 { 220 /* Print as much information as potentially useful. 221 Debugging load-time failures with embedding is not fun 222 */ 223 PyObject *exception, *v, *tb, *f, *modules, *mod; 224 PyErr_Fetch(&exception, &v, &tb); 225 if (exception != NULL) { 226 PyErr_NormalizeException(&exception, &v, &tb); 227 PyErr_Display(exception, v, tb); 228 } 229 Py_XDECREF(exception); 230 Py_XDECREF(v); 231 Py_XDECREF(tb); 232 233 f = PySys_GetObject((char *)"stderr"); 234 if (f != NULL && f != Py_None) { 235 PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME 236 "\ncompiled with cffi version: 1.7.0" 237 "\n_cffi_backend module: ", f); 238 modules = PyImport_GetModuleDict(); 239 mod = PyDict_GetItemString(modules, "_cffi_backend"); 240 if (mod == NULL) { 241 PyFile_WriteString("not loaded", f); 242 } 243 else { 244 v = PyObject_GetAttrString(mod, "__file__"); 245 PyFile_WriteObject(v, f, 0); 246 Py_XDECREF(v); 247 } 248 PyFile_WriteString("\nsys.path: ", f); 249 PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0); 250 PyFile_WriteString("\n\n", f); 251 } 252 } 253 result = -1; 254 goto done; 255 } 256 257 PyAPI_DATA(char *) _PyParser_TokenNames[]; /* from CPython */ 258 259 static int _cffi_carefully_make_gil(void) 260 { 261 /* This does the basic initialization of Python. It can be called 262 completely concurrently from unrelated threads. It assumes 263 that we don't hold the GIL before (if it exists), and we don't 264 hold it afterwards. 265 266 What it really does is completely different in Python 2 and 267 Python 3. 268 269 Python 2 270 ======== 271 272 Initialize the GIL, without initializing the rest of Python, 273 by calling PyEval_InitThreads(). 274 275 PyEval_InitThreads() must not be called concurrently at all. 276 So we use a global variable as a simple spin lock. This global 277 variable must be from 'libpythonX.Y.so', not from this 278 cffi-based extension module, because it must be shared from 279 different cffi-based extension modules. We choose 280 _PyParser_TokenNames[0] as a completely arbitrary pointer value 281 that is never written to. The default is to point to the 282 string "ENDMARKER". We change it temporarily to point to the 283 next character in that string. (Yes, I know it's REALLY 284 obscure.) 285 286 Python 3 287 ======== 288 289 In Python 3, PyEval_InitThreads() cannot be called before 290 Py_InitializeEx() any more. So this function calls 291 Py_InitializeEx() first. It uses the same obscure logic to 292 make sure we never call it concurrently. 293 294 Arguably, this is less good on the spinlock, because 295 Py_InitializeEx() takes much longer to run than 296 PyEval_InitThreads(). But I didn't find a way around it. 297 */ 298 299 #ifdef WITH_THREAD 300 char *volatile *lock = (char *volatile *)_PyParser_TokenNames; 301 char *old_value; 302 303 while (1) { /* spin loop */ 304 old_value = *lock; 305 if (old_value[0] == 'E') { 306 assert(old_value[1] == 'N'); 307 if (cffi_compare_and_swap(lock, old_value, old_value + 1)) 308 break; 309 } 310 else { 311 assert(old_value[0] == 'N'); 312 /* should ideally do a spin loop instruction here, but 313 hard to do it portably and doesn't really matter I 314 think: PyEval_InitThreads() should be very fast, and 315 this is only run at start-up anyway. */ 316 } 317 } 318 #endif 319 320 #if PY_MAJOR_VERSION >= 3 321 /* Python 3: call Py_InitializeEx() */ 322 { 323 PyGILState_STATE state = PyGILState_UNLOCKED; 324 if (!Py_IsInitialized()) 325 _cffi_py_initialize(); 326 else 327 state = PyGILState_Ensure(); 328 329 PyEval_InitThreads(); 330 PyGILState_Release(state); 331 } 332 #else 333 /* Python 2: call PyEval_InitThreads() */ 334 # ifdef WITH_THREAD 335 if (!PyEval_ThreadsInitialized()) { 336 PyEval_InitThreads(); /* makes the GIL */ 337 PyEval_ReleaseLock(); /* then release it */ 338 } 339 /* else: there is already a GIL, but we still needed to do the 340 spinlock dance to make sure that we see it as fully ready */ 341 # endif 342 #endif 343 344 #ifdef WITH_THREAD 345 /* release the lock */ 346 while (!cffi_compare_and_swap(lock, old_value + 1, old_value)) 347 ; 348 #endif 349 350 return 0; 351 } 352 353 /********** end CPython-specific section **********/ 354 355 356 #else 357 358 359 /********** PyPy-specific section **********/ 360 361 PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ 362 363 static struct _cffi_pypy_init_s { 364 const char *name; 365 void (*func)(const void *[]); 366 const char *code; 367 } _cffi_pypy_init = { 368 _CFFI_MODULE_NAME, 369 _CFFI_PYTHON_STARTUP_FUNC, 370 _CFFI_PYTHON_STARTUP_CODE, 371 }; 372 373 extern int pypy_carefully_make_gil(const char *); 374 extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *); 375 376 static int _cffi_carefully_make_gil(void) 377 { 378 return pypy_carefully_make_gil(_CFFI_MODULE_NAME); 379 } 380 381 static int _cffi_initialize_python(void) 382 { 383 return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init); 384 } 385 386 /********** end PyPy-specific section **********/ 387 388 389 #endif 390 391 392 #ifdef __GNUC__ 393 __attribute__((noinline)) 394 #endif 395 static _cffi_call_python_fnptr _cffi_start_python(void) 396 { 397 /* Delicate logic to initialize Python. This function can be 398 called multiple times concurrently, e.g. when the process calls 399 its first ``extern "Python"`` functions in multiple threads at 400 once. It can also be called recursively, in which case we must 401 ignore it. We also have to consider what occurs if several 402 different cffi-based extensions reach this code in parallel 403 threads---it is a different copy of the code, then, and we 404 can't have any shared global variable unless it comes from 405 'libpythonX.Y.so'. 406 407 Idea: 408 409 * _cffi_carefully_make_gil(): "carefully" call 410 PyEval_InitThreads() (possibly with Py_InitializeEx() first). 411 412 * then we use a (local) custom lock to make sure that a call to this 413 cffi-based extension will wait if another call to the *same* 414 extension is running the initialization in another thread. 415 It is reentrant, so that a recursive call will not block, but 416 only one from a different thread. 417 418 * then we grab the GIL and (Python 2) we call Py_InitializeEx(). 419 At this point, concurrent calls to Py_InitializeEx() are not 420 possible: we have the GIL. 421 422 * do the rest of the specific initialization, which may 423 temporarily release the GIL but not the custom lock. 424 Only release the custom lock when we are done. 425 */ 426 static char called = 0; 427 428 if (_cffi_carefully_make_gil() != 0) 429 return NULL; 430 431 _cffi_acquire_reentrant_mutex(); 432 433 /* Here the GIL exists, but we don't have it. We're only protected 434 from concurrency by the reentrant mutex. */ 435 436 /* This file only initializes the embedded module once, the first 437 time this is called, even if there are subinterpreters. */ 438 if (!called) { 439 called = 1; /* invoke _cffi_initialize_python() only once, 440 but don't set '_cffi_call_python' right now, 441 otherwise concurrent threads won't call 442 this function at all (we need them to wait) */ 443 if (_cffi_initialize_python() == 0) { 444 /* now initialization is finished. Switch to the fast-path. */ 445 446 /* We would like nobody to see the new value of 447 '_cffi_call_python' without also seeing the rest of the 448 data initialized. However, this is not possible. But 449 the new value of '_cffi_call_python' is the function 450 'cffi_call_python()' from _cffi_backend. So: */ 451 cffi_write_barrier(); 452 /* ^^^ we put a write barrier here, and a corresponding 453 read barrier at the start of cffi_call_python(). This 454 ensures that after that read barrier, we see everything 455 done here before the write barrier. 456 */ 457 458 assert(_cffi_call_python_org != NULL); 459 _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org; 460 } 461 else { 462 /* initialization failed. Reset this to NULL, even if it was 463 already set to some other value. Future calls to 464 _cffi_start_python() are still forced to occur, and will 465 always return NULL from now on. */ 466 _cffi_call_python_org = NULL; 467 } 468 } 469 470 _cffi_release_reentrant_mutex(); 471 472 return (_cffi_call_python_fnptr)_cffi_call_python_org; 473 } 474 475 static 476 void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args) 477 { 478 _cffi_call_python_fnptr fnptr; 479 int current_err = errno; 480 #ifdef _MSC_VER 481 int current_lasterr = GetLastError(); 482 #endif 483 fnptr = _cffi_start_python(); 484 if (fnptr == NULL) { 485 fprintf(stderr, "function %s() called, but initialization code " 486 "failed. Returning 0.\n", externpy->name); 487 memset(args, 0, externpy->size_of_result); 488 } 489 #ifdef _MSC_VER 490 SetLastError(current_lasterr); 491 #endif 492 errno = current_err; 493 494 if (fnptr != NULL) 495 fnptr(externpy, args); 496 } 497 498 499 /* The cffi_start_python() function makes sure Python is initialized 500 and our cffi module is set up. It can be called manually from the 501 user C code. The same effect is obtained automatically from any 502 dll-exported ``extern "Python"`` function. This function returns 503 -1 if initialization failed, 0 if all is OK. */ 504 _CFFI_UNUSED_FN 505 static int cffi_start_python(void) 506 { 507 if (_cffi_call_python == &_cffi_start_and_call_python) { 508 if (_cffi_start_python() == NULL) 509 return -1; 510 } 511 cffi_read_barrier(); 512 return 0; 513 } 514 515 #undef cffi_compare_and_swap 516 #undef cffi_write_barrier 517 #undef cffi_read_barrier