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