_embedding.h 17.2 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527

/***** Support code for embedding *****/

#ifdef __cplusplus
extern "C" {
#endif


#if defined(_WIN32)
#  define CFFI_DLLEXPORT  __declspec(dllexport)
#elif defined(__GNUC__)
#  define CFFI_DLLEXPORT  __attribute__((visibility("default")))
#else
#  define CFFI_DLLEXPORT  /* nothing */
#endif


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

   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.
*/
#undef _cffi_call_python
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;


#ifndef _MSC_VER
   /* --- Assuming a GCC not infinitely old --- */
# define cffi_compare_and_swap(l,o,n)  __sync_bool_compare_and_swap(l,o,n)
# define cffi_write_barrier()          __sync_synchronize()
# if !defined(__amd64__) && !defined(__x86_64__) &&   \
     !defined(__i386__) && !defined(__i386)
#   define cffi_read_barrier()         __sync_synchronize()
# else
#   define cffi_read_barrier()         (void)0
# endif
#else
   /* --- Windows threads version --- */
# include <Windows.h>
# define cffi_compare_and_swap(l,o,n) \
                               (InterlockedCompareExchangePointer(l,n,o) == (o))
# define cffi_write_barrier()       InterlockedCompareExchange(&_cffi_dummy,0,0)
# define cffi_read_barrier()           (void)0
static volatile LONG _cffi_dummy;
#endif

#ifdef WITH_THREAD
# ifndef _MSC_VER
#  include <pthread.h>
   static pthread_mutex_t _cffi_embed_startup_lock;
# else
   static CRITICAL_SECTION _cffi_embed_startup_lock;
# endif
  static char _cffi_embed_startup_lock_ready = 0;
#endif

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

#ifdef WITH_THREAD
    if (!_cffi_embed_startup_lock_ready) {
# ifndef _MSC_VER
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        pthread_mutex_init(&_cffi_embed_startup_lock, &attr);
# else
        InitializeCriticalSection(&_cffi_embed_startup_lock);
# endif
        _cffi_embed_startup_lock_ready = 1;
    }
#endif

    while (!cffi_compare_and_swap(&lock, (void *)1, NULL))
        ;

#ifndef _MSC_VER
    pthread_mutex_lock(&_cffi_embed_startup_lock);
#else
    EnterCriticalSection(&_cffi_embed_startup_lock);
#endif
}

static void _cffi_release_reentrant_mutex(void)
{
#ifndef _MSC_VER
    pthread_mutex_unlock(&_cffi_embed_startup_lock);
#else
    LeaveCriticalSection(&_cffi_embed_startup_lock);
#endif
}


/**********  CPython-specific section  **********/
#ifndef PYPY_VERSION

#include "_cffi_errors.h"


#define _cffi_call_python_org  _cffi_exports[_CFFI_CPIDX]

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(
#if PY_MAJOR_VERSION < 3
                        (PyCodeObject *)
#endif
                        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.0"
                               "\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;
}

#if PY_VERSION_HEX < 0x03080000
PyAPI_DATA(char *) _PyParser_TokenNames[];  /* from CPython */
#endif

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

#ifdef WITH_THREAD
# if PY_VERSION_HEX < 0x03080000
    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. */
        }
    }
# else
    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. */
        }
    }
# endif
#endif

    /* call Py_InitializeEx() */
    if (!Py_IsInitialized()) {
        _cffi_py_initialize();
#if PY_VERSION_HEX < 0x03070000
        PyEval_InitThreads();
#endif
        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 {
#if PY_VERSION_HEX < 0x03070000
        /* PyEval_InitThreads() is always a no-op from CPython 3.7 */
        PyGILState_STATE state = PyGILState_Ensure();
        PyEval_InitThreads();
        PyGILState_Release(state);
#endif
    }

#ifdef WITH_THREAD
    /* release the lock */
    while (!cffi_compare_and_swap(lock, locked_value, old_value))
        ;
#endif

    return 0;
}

/**********  end CPython-specific section  **********/


#else


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


#endif


#ifdef __GNUC__
__attribute__((noinline))
#endif
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;
#ifdef _MSC_VER
    int current_lasterr = GetLastError();
#endif
    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);
    }
#ifdef _MSC_VER
    SetLastError(current_lasterr);
#endif
    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;
}

#undef cffi_compare_and_swap
#undef cffi_write_barrier
#undef cffi_read_barrier

#ifdef __cplusplus
}
#endif