• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #if PY_VERSION_HEX >= 0x03080000
2 # define Py_BUILD_CORE
3 /* for access to the fields of PyInterpreterState */
4 #  include "internal/pycore_pystate.h"
5 # undef Py_BUILD_CORE
6 #endif
7 
_get_interpstate_dict(void)8 static PyObject *_get_interpstate_dict(void)
9 {
10     /* Hack around to return a dict that is subinterpreter-local.
11        Does not return a new reference.  Returns NULL in case of
12        error, but without setting any exception.  (If called late
13        during shutdown, we *can't* set an exception!)
14     */
15     static PyObject *attr_name = NULL;
16     PyThreadState *tstate;
17     PyObject *d, *builtins;
18     int err;
19 
20     tstate = PyThreadState_GET();
21     if (tstate == NULL) {
22         /* no thread state! */
23         return NULL;
24     }
25 
26     builtins = tstate->interp->builtins;
27     if (builtins == NULL) {
28         /* subinterpreter was cleared already, or is being cleared right now,
29            to a point that is too much for us to continue */
30         return NULL;
31     }
32 
33     /* from there on, we know the (sub-)interpreter is still valid */
34 
35     if (attr_name == NULL) {
36         attr_name = PyText_InternFromString("__cffi_backend_extern_py");
37         if (attr_name == NULL)
38             goto error;
39     }
40 
41     d = PyDict_GetItem(builtins, attr_name);
42     if (d == NULL) {
43         d = PyDict_New();
44         if (d == NULL)
45             goto error;
46         err = PyDict_SetItem(builtins, attr_name, d);
47         Py_DECREF(d);    /* if successful, there is one ref left in builtins */
48         if (err < 0)
49             goto error;
50     }
51     return d;
52 
53  error:
54     PyErr_Clear();    /* typically a MemoryError */
55     return NULL;
56 }
57 
_ffi_def_extern_decorator(PyObject * outer_args,PyObject * fn)58 static PyObject *_ffi_def_extern_decorator(PyObject *outer_args, PyObject *fn)
59 {
60     const char *s;
61     PyObject *error, *onerror, *infotuple, *old1;
62     int index, err;
63     const struct _cffi_global_s *g;
64     struct _cffi_externpy_s *externpy;
65     CTypeDescrObject *ct;
66     FFIObject *ffi;
67     builder_c_t *types_builder;
68     PyObject *name = NULL;
69     PyObject *interpstate_dict;
70     PyObject *interpstate_key;
71 
72     if (!PyArg_ParseTuple(outer_args, "OzOO", &ffi, &s, &error, &onerror))
73         return NULL;
74 
75     if (s == NULL) {
76         name = PyObject_GetAttrString(fn, "__name__");
77         if (name == NULL)
78             return NULL;
79         s = PyText_AsUTF8(name);
80         if (s == NULL) {
81             Py_DECREF(name);
82             return NULL;
83         }
84     }
85 
86     types_builder = &ffi->types_builder;
87     index = search_in_globals(&types_builder->ctx, s, strlen(s));
88     if (index < 0)
89         goto not_found;
90     g = &types_builder->ctx.globals[index];
91     if (_CFFI_GETOP(g->type_op) != _CFFI_OP_EXTERN_PYTHON)
92         goto not_found;
93     Py_XDECREF(name);
94 
95     ct = realize_c_type(types_builder, types_builder->ctx.types,
96                         _CFFI_GETARG(g->type_op));
97     if (ct == NULL)
98         return NULL;
99 
100     infotuple = prepare_callback_info_tuple(ct, fn, error, onerror, 0);
101     Py_DECREF(ct);
102     if (infotuple == NULL)
103         return NULL;
104 
105     /* don't directly attach infotuple to externpy: in the presence of
106        subinterpreters, each time we switch to a different
107        subinterpreter and call the C function, it will notice the
108        change and look up infotuple from the interpstate_dict.
109     */
110     interpstate_dict = _get_interpstate_dict();
111     if (interpstate_dict == NULL) {
112         Py_DECREF(infotuple);
113         return PyErr_NoMemory();
114     }
115 
116     externpy = (struct _cffi_externpy_s *)g->address;
117     interpstate_key = PyLong_FromVoidPtr((void *)externpy);
118     if (interpstate_key == NULL) {
119         Py_DECREF(infotuple);
120         return NULL;
121     }
122 
123     err = PyDict_SetItem(interpstate_dict, interpstate_key, infotuple);
124     Py_DECREF(interpstate_key);
125     Py_DECREF(infotuple);    /* interpstate_dict owns the last ref */
126     if (err < 0)
127         return NULL;
128 
129     /* force _update_cache_to_call_python() to be called the next time
130        the C function invokes cffi_call_python, to update the cache */
131     old1 = externpy->reserved1;
132     externpy->reserved1 = Py_None;   /* a non-NULL value */
133     Py_INCREF(Py_None);
134     Py_XDECREF(old1);
135 
136     /* return the function object unmodified */
137     Py_INCREF(fn);
138     return fn;
139 
140  not_found:
141     PyErr_Format(FFIError, "ffi.def_extern('%s'): no 'extern \"Python\"' "
142                  "function with this name", s);
143     Py_XDECREF(name);
144     return NULL;
145 }
146 
147 
_update_cache_to_call_python(struct _cffi_externpy_s * externpy)148 static int _update_cache_to_call_python(struct _cffi_externpy_s *externpy)
149 {
150     PyObject *interpstate_dict, *interpstate_key, *infotuple, *old1, *new1;
151     PyObject *old2;
152 
153     interpstate_dict = _get_interpstate_dict();
154     if (interpstate_dict == NULL)
155         return 4;    /* oops, shutdown issue? */
156 
157     interpstate_key = PyLong_FromVoidPtr((void *)externpy);
158     if (interpstate_key == NULL)
159         goto error;
160 
161     infotuple = PyDict_GetItem(interpstate_dict, interpstate_key);
162     Py_DECREF(interpstate_key);
163     if (infotuple == NULL)
164         return 3;    /* no ffi.def_extern() from this subinterpreter */
165 
166     new1 = PyThreadState_GET()->interp->modules;
167     Py_INCREF(new1);
168     Py_INCREF(infotuple);
169     old1 = (PyObject *)externpy->reserved1;
170     old2 = (PyObject *)externpy->reserved2;
171     externpy->reserved1 = new1;         /* holds a reference        */
172     externpy->reserved2 = infotuple;    /* holds a reference (issue #246) */
173     Py_XDECREF(old1);
174     Py_XDECREF(old2);
175 
176     return 0;   /* no error */
177 
178  error:
179     PyErr_Clear();
180     return 2;   /* out of memory? */
181 }
182 
183 #if (defined(WITH_THREAD) && !defined(_MSC_VER) &&   \
184      !defined(__amd64__) && !defined(__x86_64__) &&   \
185      !defined(__i386__) && !defined(__i386))
186 # if defined(HAVE_SYNC_SYNCHRONIZE)
187 #   define read_barrier()  __sync_synchronize()
188 # elif defined(_AIX)
189 #   define read_barrier()  __lwsync()
190 # elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
191 #   include <mbarrier.h>
192 #   define read_barrier()  __compiler_barrier()
193 # elif defined(__hpux)
194 #   define read_barrier()  _Asm_mf()
195 # else
196 #   define read_barrier()  /* missing */
197 #   warning "no definition for read_barrier(), missing synchronization for\
198  multi-thread initialization in embedded mode"
199 # endif
200 #else
201 # define read_barrier()  (void)0
202 #endif
203 
cffi_call_python(struct _cffi_externpy_s * externpy,char * args)204 static void cffi_call_python(struct _cffi_externpy_s *externpy, char *args)
205 {
206     /* Invoked by the helpers generated from extern "Python" in the cdef.
207 
208        'externpy' is a static structure that describes which of the
209        extern "Python" functions is called.  It has got fields 'name' and
210        'type_index' describing the function, and more reserved fields
211        that are initially zero.  These reserved fields are set up by
212        ffi.def_extern(), which invokes _ffi_def_extern_decorator() above.
213 
214        'args' is a pointer to an array of 8-byte entries.  Each entry
215        contains an argument.  If an argument is less than 8 bytes, only
216        the part at the beginning of the entry is initialized.  If an
217        argument is 'long double' or a struct/union, then it is passed
218        by reference.
219 
220        'args' is also used as the place to write the result to
221        (directly, even if more than 8 bytes).  In all cases, 'args' is
222        at least 8 bytes in size.
223     */
224     int err = 0;
225 
226     /* This read barrier is needed for _embedding.h.  It is paired
227        with the write_barrier() there.  Without this barrier, we can
228        in theory see the following situation: the Python
229        initialization code already ran (in another thread), and the
230        '_cffi_call_python' function pointer directed execution here;
231        but any number of other data could still be seen as
232        uninitialized below.  For example, 'externpy' would still
233        contain NULLs even though it was correctly set up, or
234        'interpreter_lock' (the GIL inside CPython) would still be seen
235        as NULL, or 'autoInterpreterState' (used by
236        PyGILState_Ensure()) would be NULL or contain bogus fields.
237     */
238     read_barrier();
239 
240     save_errno();
241 
242     /* We need the infotuple here.  We could always go through
243        _update_cache_to_call_python(), but to avoid the extra dict
244        lookups, we cache in (reserved1, reserved2) the last seen pair
245        (interp->modules, infotuple).  The first item in this tuple is
246        a random PyObject that identifies the subinterpreter.
247     */
248     if (externpy->reserved1 == NULL) {
249         /* Not initialized!  We didn't call @ffi.def_extern() on this
250            externpy object from any subinterpreter at all. */
251         err = 1;
252     }
253     else {
254         PyGILState_STATE state = gil_ensure();
255         if (externpy->reserved1 != PyThreadState_GET()->interp->modules) {
256             /* Update the (reserved1, reserved2) cache.  This will fail
257                if we didn't call @ffi.def_extern() in this particular
258                subinterpreter. */
259             err = _update_cache_to_call_python(externpy);
260         }
261         if (!err) {
262             general_invoke_callback(0, args, args, externpy->reserved2);
263         }
264         gil_release(state);
265     }
266     if (err) {
267         static const char *msg[] = {
268             "no code was attached to it yet with @ffi.def_extern()",
269             "got internal exception (out of memory?)",
270             "@ffi.def_extern() was not called in the current subinterpreter",
271             "got internal exception (shutdown issue?)",
272         };
273         fprintf(stderr, "extern \"Python\": function %s() called, "
274                         "but %s.  Returning 0.\n", externpy->name, msg[err-1]);
275         memset(args, 0, externpy->size_of_result);
276     }
277     restore_errno();
278 }
279