xref: /unit/src/python/nxt_python.c (revision 1681)
11592Smax.romanov@nginx.com 
21592Smax.romanov@nginx.com /*
31592Smax.romanov@nginx.com  * Copyright (C) NGINX, Inc.
41592Smax.romanov@nginx.com  */
51592Smax.romanov@nginx.com 
61592Smax.romanov@nginx.com 
71592Smax.romanov@nginx.com #include <Python.h>
81592Smax.romanov@nginx.com 
91592Smax.romanov@nginx.com #include <nxt_main.h>
101592Smax.romanov@nginx.com #include <nxt_router.h>
111592Smax.romanov@nginx.com #include <nxt_unit.h>
121592Smax.romanov@nginx.com 
131592Smax.romanov@nginx.com #include <python/nxt_python.h>
141592Smax.romanov@nginx.com 
151592Smax.romanov@nginx.com #include NXT_PYTHON_MOUNTS_H
161592Smax.romanov@nginx.com 
171592Smax.romanov@nginx.com 
18*1681Smax.romanov@nginx.com typedef struct {
19*1681Smax.romanov@nginx.com     pthread_t       thread;
20*1681Smax.romanov@nginx.com     nxt_unit_ctx_t  *ctx;
21*1681Smax.romanov@nginx.com     void            *ctx_data;
22*1681Smax.romanov@nginx.com } nxt_py_thread_info_t;
23*1681Smax.romanov@nginx.com 
24*1681Smax.romanov@nginx.com 
251592Smax.romanov@nginx.com static nxt_int_t nxt_python_start(nxt_task_t *task,
261592Smax.romanov@nginx.com     nxt_process_data_t *data);
27*1681Smax.romanov@nginx.com static int nxt_python_init_threads(nxt_python_app_conf_t *c);
28*1681Smax.romanov@nginx.com static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
29*1681Smax.romanov@nginx.com static void *nxt_python_thread_func(void *main_ctx);
30*1681Smax.romanov@nginx.com static void nxt_python_join_threads(nxt_unit_ctx_t *ctx,
31*1681Smax.romanov@nginx.com     nxt_python_app_conf_t *c);
321592Smax.romanov@nginx.com static void nxt_python_atexit(void);
331592Smax.romanov@nginx.com 
341592Smax.romanov@nginx.com static uint32_t  compat[] = {
351592Smax.romanov@nginx.com     NXT_VERNUM, NXT_DEBUG,
361592Smax.romanov@nginx.com };
371592Smax.romanov@nginx.com 
381592Smax.romanov@nginx.com 
391592Smax.romanov@nginx.com NXT_EXPORT nxt_app_module_t  nxt_app_module = {
401592Smax.romanov@nginx.com     sizeof(compat),
411592Smax.romanov@nginx.com     compat,
421592Smax.romanov@nginx.com     nxt_string("python"),
431592Smax.romanov@nginx.com     PY_VERSION,
441592Smax.romanov@nginx.com     nxt_python_mounts,
451592Smax.romanov@nginx.com     nxt_nitems(nxt_python_mounts),
461592Smax.romanov@nginx.com     NULL,
471592Smax.romanov@nginx.com     nxt_python_start,
481592Smax.romanov@nginx.com };
491592Smax.romanov@nginx.com 
501592Smax.romanov@nginx.com static PyObject           *nxt_py_stderr_flush;
511592Smax.romanov@nginx.com PyObject                  *nxt_py_application;
521592Smax.romanov@nginx.com 
531592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
541592Smax.romanov@nginx.com static wchar_t            *nxt_py_home;
551592Smax.romanov@nginx.com #else
561592Smax.romanov@nginx.com static char               *nxt_py_home;
571592Smax.romanov@nginx.com #endif
581592Smax.romanov@nginx.com 
59*1681Smax.romanov@nginx.com static pthread_attr_t        *nxt_py_thread_attr;
60*1681Smax.romanov@nginx.com static nxt_py_thread_info_t  *nxt_py_threads;
61*1681Smax.romanov@nginx.com static nxt_python_proto_t    nxt_py_proto;
62*1681Smax.romanov@nginx.com 
631592Smax.romanov@nginx.com 
641592Smax.romanov@nginx.com static nxt_int_t
651592Smax.romanov@nginx.com nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
661592Smax.romanov@nginx.com {
67*1681Smax.romanov@nginx.com     int                    rc;
681592Smax.romanov@nginx.com     char                   *nxt_py_module;
691592Smax.romanov@nginx.com     size_t                 len;
701592Smax.romanov@nginx.com     PyObject               *obj, *pypath, *module;
711601Smax.romanov@nginx.com     const char             *callable;
721592Smax.romanov@nginx.com     nxt_unit_ctx_t         *unit_ctx;
731592Smax.romanov@nginx.com     nxt_unit_init_t        python_init;
741592Smax.romanov@nginx.com     nxt_common_app_conf_t  *app_conf;
751592Smax.romanov@nginx.com     nxt_python_app_conf_t  *c;
761592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
771592Smax.romanov@nginx.com     char                   *path;
781592Smax.romanov@nginx.com     size_t                 size;
791592Smax.romanov@nginx.com     nxt_int_t              pep405;
801592Smax.romanov@nginx.com 
811592Smax.romanov@nginx.com     static const char pyvenv[] = "/pyvenv.cfg";
821592Smax.romanov@nginx.com     static const char bin_python[] = "/bin/python";
831592Smax.romanov@nginx.com #endif
841592Smax.romanov@nginx.com 
851592Smax.romanov@nginx.com     app_conf = data->app;
861592Smax.romanov@nginx.com     c = &app_conf->u.python;
871592Smax.romanov@nginx.com 
881592Smax.romanov@nginx.com     if (c->home != NULL) {
891592Smax.romanov@nginx.com         len = nxt_strlen(c->home);
901592Smax.romanov@nginx.com 
911592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
921592Smax.romanov@nginx.com 
931592Smax.romanov@nginx.com         path = nxt_malloc(len + sizeof(pyvenv));
941592Smax.romanov@nginx.com         if (nxt_slow_path(path == NULL)) {
951592Smax.romanov@nginx.com             nxt_alert(task, "Failed to allocate memory");
961592Smax.romanov@nginx.com             return NXT_ERROR;
971592Smax.romanov@nginx.com         }
981592Smax.romanov@nginx.com 
991592Smax.romanov@nginx.com         nxt_memcpy(path, c->home, len);
1001592Smax.romanov@nginx.com         nxt_memcpy(path + len, pyvenv, sizeof(pyvenv));
1011592Smax.romanov@nginx.com 
1021592Smax.romanov@nginx.com         pep405 = (access(path, R_OK) == 0);
1031592Smax.romanov@nginx.com 
1041592Smax.romanov@nginx.com         nxt_free(path);
1051592Smax.romanov@nginx.com 
1061592Smax.romanov@nginx.com         if (pep405) {
1071592Smax.romanov@nginx.com             size = (len + sizeof(bin_python)) * sizeof(wchar_t);
1081592Smax.romanov@nginx.com 
1091592Smax.romanov@nginx.com         } else {
1101592Smax.romanov@nginx.com             size = (len + 1) * sizeof(wchar_t);
1111592Smax.romanov@nginx.com         }
1121592Smax.romanov@nginx.com 
1131592Smax.romanov@nginx.com         nxt_py_home = nxt_malloc(size);
1141592Smax.romanov@nginx.com         if (nxt_slow_path(nxt_py_home == NULL)) {
1151592Smax.romanov@nginx.com             nxt_alert(task, "Failed to allocate memory");
1161592Smax.romanov@nginx.com             return NXT_ERROR;
1171592Smax.romanov@nginx.com         }
1181592Smax.romanov@nginx.com 
1191592Smax.romanov@nginx.com         if (pep405) {
1201592Smax.romanov@nginx.com             mbstowcs(nxt_py_home, c->home, len);
1211592Smax.romanov@nginx.com             mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python));
1221592Smax.romanov@nginx.com             Py_SetProgramName(nxt_py_home);
1231592Smax.romanov@nginx.com 
1241592Smax.romanov@nginx.com         } else {
1251592Smax.romanov@nginx.com             mbstowcs(nxt_py_home, c->home, len + 1);
1261592Smax.romanov@nginx.com             Py_SetPythonHome(nxt_py_home);
1271592Smax.romanov@nginx.com         }
1281592Smax.romanov@nginx.com 
1291592Smax.romanov@nginx.com #else
1301592Smax.romanov@nginx.com         nxt_py_home = nxt_malloc(len + 1);
1311592Smax.romanov@nginx.com         if (nxt_slow_path(nxt_py_home == NULL)) {
1321592Smax.romanov@nginx.com             nxt_alert(task, "Failed to allocate memory");
1331592Smax.romanov@nginx.com             return NXT_ERROR;
1341592Smax.romanov@nginx.com         }
1351592Smax.romanov@nginx.com 
1361592Smax.romanov@nginx.com         nxt_memcpy(nxt_py_home, c->home, len + 1);
1371592Smax.romanov@nginx.com         Py_SetPythonHome(nxt_py_home);
1381592Smax.romanov@nginx.com #endif
1391592Smax.romanov@nginx.com     }
1401592Smax.romanov@nginx.com 
1411592Smax.romanov@nginx.com     Py_InitializeEx(0);
1421592Smax.romanov@nginx.com 
143*1681Smax.romanov@nginx.com #if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7)
144*1681Smax.romanov@nginx.com     if (c->threads > 1) {
145*1681Smax.romanov@nginx.com         PyEval_InitThreads();
146*1681Smax.romanov@nginx.com     }
147*1681Smax.romanov@nginx.com #endif
148*1681Smax.romanov@nginx.com 
1491592Smax.romanov@nginx.com     module = NULL;
1501592Smax.romanov@nginx.com     obj = NULL;
1511592Smax.romanov@nginx.com 
152*1681Smax.romanov@nginx.com     python_init.ctx_data = NULL;
153*1681Smax.romanov@nginx.com 
1541592Smax.romanov@nginx.com     obj = PySys_GetObject((char *) "stderr");
1551592Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
1561592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to get \"sys.stderr\" object");
1571592Smax.romanov@nginx.com         goto fail;
1581592Smax.romanov@nginx.com     }
1591592Smax.romanov@nginx.com 
1601592Smax.romanov@nginx.com     nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush");
1611592Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_stderr_flush == NULL)) {
1621592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to get \"flush\" attribute of "
1631592Smax.romanov@nginx.com                         "\"sys.stderr\" object");
1641592Smax.romanov@nginx.com         goto fail;
1651592Smax.romanov@nginx.com     }
1661592Smax.romanov@nginx.com 
1671592Smax.romanov@nginx.com     /* obj is a Borrowed reference. */
1681592Smax.romanov@nginx.com 
1691592Smax.romanov@nginx.com     if (c->path.length > 0) {
1701592Smax.romanov@nginx.com         obj = PyString_FromStringAndSize((char *) c->path.start,
1711592Smax.romanov@nginx.com                                          c->path.length);
1721592Smax.romanov@nginx.com 
1731592Smax.romanov@nginx.com         if (nxt_slow_path(obj == NULL)) {
1741592Smax.romanov@nginx.com             nxt_alert(task, "Python failed to create string object \"%V\"",
1751592Smax.romanov@nginx.com                       &c->path);
1761592Smax.romanov@nginx.com             goto fail;
1771592Smax.romanov@nginx.com         }
1781592Smax.romanov@nginx.com 
1791592Smax.romanov@nginx.com         pypath = PySys_GetObject((char *) "path");
1801592Smax.romanov@nginx.com 
1811592Smax.romanov@nginx.com         if (nxt_slow_path(pypath == NULL)) {
1821592Smax.romanov@nginx.com             nxt_alert(task, "Python failed to get \"sys.path\" list");
1831592Smax.romanov@nginx.com             goto fail;
1841592Smax.romanov@nginx.com         }
1851592Smax.romanov@nginx.com 
1861592Smax.romanov@nginx.com         if (nxt_slow_path(PyList_Insert(pypath, 0, obj) != 0)) {
1871592Smax.romanov@nginx.com             nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"",
1881592Smax.romanov@nginx.com                       &c->path);
1891592Smax.romanov@nginx.com             goto fail;
1901592Smax.romanov@nginx.com         }
1911592Smax.romanov@nginx.com 
1921592Smax.romanov@nginx.com         Py_DECREF(obj);
1931592Smax.romanov@nginx.com     }
1941592Smax.romanov@nginx.com 
1951592Smax.romanov@nginx.com     obj = Py_BuildValue("[s]", "unit");
1961592Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
1971592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to create the \"sys.argv\" list");
1981592Smax.romanov@nginx.com         goto fail;
1991592Smax.romanov@nginx.com     }
2001592Smax.romanov@nginx.com 
2011592Smax.romanov@nginx.com     if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) {
2021592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to set the \"sys.argv\" list");
2031592Smax.romanov@nginx.com         goto fail;
2041592Smax.romanov@nginx.com     }
2051592Smax.romanov@nginx.com 
2061592Smax.romanov@nginx.com     Py_CLEAR(obj);
2071592Smax.romanov@nginx.com 
2081592Smax.romanov@nginx.com     nxt_py_module = nxt_alloca(c->module.length + 1);
2091592Smax.romanov@nginx.com     nxt_memcpy(nxt_py_module, c->module.start, c->module.length);
2101592Smax.romanov@nginx.com     nxt_py_module[c->module.length] = '\0';
2111592Smax.romanov@nginx.com 
2121592Smax.romanov@nginx.com     module = PyImport_ImportModule(nxt_py_module);
2131592Smax.romanov@nginx.com     if (nxt_slow_path(module == NULL)) {
2141592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to import module \"%s\"", nxt_py_module);
2151592Smax.romanov@nginx.com         nxt_python_print_exception();
2161592Smax.romanov@nginx.com         goto fail;
2171592Smax.romanov@nginx.com     }
2181592Smax.romanov@nginx.com 
2191601Smax.romanov@nginx.com     callable = (c->callable != NULL) ? c->callable : "application";
2201601Smax.romanov@nginx.com 
2211601Smax.romanov@nginx.com     obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
2221592Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
2231601Smax.romanov@nginx.com         nxt_alert(task, "Python failed to get \"%s\" "
2241601Smax.romanov@nginx.com                   "from module \"%s\"", callable, nxt_py_module);
2251592Smax.romanov@nginx.com         goto fail;
2261592Smax.romanov@nginx.com     }
2271592Smax.romanov@nginx.com 
2281592Smax.romanov@nginx.com     if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
2291601Smax.romanov@nginx.com         nxt_alert(task, "\"%s\" in module \"%s\" "
2301601Smax.romanov@nginx.com                   "is not a callable object", callable, nxt_py_module);
2311592Smax.romanov@nginx.com         goto fail;
2321592Smax.romanov@nginx.com     }
2331592Smax.romanov@nginx.com 
2341592Smax.romanov@nginx.com     nxt_py_application = obj;
2351592Smax.romanov@nginx.com     obj = NULL;
2361592Smax.romanov@nginx.com 
2371592Smax.romanov@nginx.com     Py_INCREF(nxt_py_application);
2381592Smax.romanov@nginx.com 
2391592Smax.romanov@nginx.com     Py_CLEAR(module);
2401592Smax.romanov@nginx.com 
2411592Smax.romanov@nginx.com     nxt_unit_default_init(task, &python_init);
2421592Smax.romanov@nginx.com 
243*1681Smax.romanov@nginx.com     python_init.data = c;
2441592Smax.romanov@nginx.com     python_init.shm_limit = data->app->shm_limit;
245*1681Smax.romanov@nginx.com     python_init.callbacks.ready_handler = nxt_python_ready_handler;
2461592Smax.romanov@nginx.com 
247*1681Smax.romanov@nginx.com     if (nxt_python_asgi_check(nxt_py_application)) {
248*1681Smax.romanov@nginx.com         rc = nxt_python_asgi_init(&python_init, &nxt_py_proto);
2491624Smax.romanov@nginx.com 
2501624Smax.romanov@nginx.com     } else {
251*1681Smax.romanov@nginx.com         rc = nxt_python_wsgi_init(&python_init, &nxt_py_proto);
252*1681Smax.romanov@nginx.com     }
253*1681Smax.romanov@nginx.com 
254*1681Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
255*1681Smax.romanov@nginx.com         goto fail;
2561624Smax.romanov@nginx.com     }
2571624Smax.romanov@nginx.com 
258*1681Smax.romanov@nginx.com     rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data);
259*1681Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
2601592Smax.romanov@nginx.com         goto fail;
2611592Smax.romanov@nginx.com     }
2621592Smax.romanov@nginx.com 
263*1681Smax.romanov@nginx.com     rc = nxt_python_init_threads(c);
264*1681Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
265*1681Smax.romanov@nginx.com         goto fail;
266*1681Smax.romanov@nginx.com     }
267*1681Smax.romanov@nginx.com 
268*1681Smax.romanov@nginx.com     if (nxt_py_proto.startup != NULL) {
269*1681Smax.romanov@nginx.com         if (nxt_py_proto.startup(python_init.ctx_data) != NXT_UNIT_OK) {
270*1681Smax.romanov@nginx.com             goto fail;
271*1681Smax.romanov@nginx.com         }
272*1681Smax.romanov@nginx.com     }
273*1681Smax.romanov@nginx.com 
2741592Smax.romanov@nginx.com     unit_ctx = nxt_unit_init(&python_init);
2751592Smax.romanov@nginx.com     if (nxt_slow_path(unit_ctx == NULL)) {
2761592Smax.romanov@nginx.com         goto fail;
2771592Smax.romanov@nginx.com     }
2781592Smax.romanov@nginx.com 
279*1681Smax.romanov@nginx.com     rc = nxt_py_proto.run(unit_ctx);
2801624Smax.romanov@nginx.com 
281*1681Smax.romanov@nginx.com     nxt_python_join_threads(unit_ctx, c);
2821592Smax.romanov@nginx.com 
2831592Smax.romanov@nginx.com     nxt_unit_done(unit_ctx);
2841592Smax.romanov@nginx.com 
285*1681Smax.romanov@nginx.com     nxt_py_proto.ctx_data_free(python_init.ctx_data);
286*1681Smax.romanov@nginx.com 
2871592Smax.romanov@nginx.com     nxt_python_atexit();
2881592Smax.romanov@nginx.com 
2891592Smax.romanov@nginx.com     exit(rc);
2901592Smax.romanov@nginx.com 
2911592Smax.romanov@nginx.com     return NXT_OK;
2921592Smax.romanov@nginx.com 
2931592Smax.romanov@nginx.com fail:
2941592Smax.romanov@nginx.com 
295*1681Smax.romanov@nginx.com     nxt_python_join_threads(NULL, c);
296*1681Smax.romanov@nginx.com 
297*1681Smax.romanov@nginx.com     if (python_init.ctx_data != NULL) {
298*1681Smax.romanov@nginx.com         nxt_py_proto.ctx_data_free(python_init.ctx_data);
299*1681Smax.romanov@nginx.com     }
300*1681Smax.romanov@nginx.com 
3011592Smax.romanov@nginx.com     Py_XDECREF(obj);
3021592Smax.romanov@nginx.com     Py_XDECREF(module);
3031592Smax.romanov@nginx.com 
3041592Smax.romanov@nginx.com     nxt_python_atexit();
3051592Smax.romanov@nginx.com 
3061592Smax.romanov@nginx.com     return NXT_ERROR;
3071592Smax.romanov@nginx.com }
3081592Smax.romanov@nginx.com 
3091592Smax.romanov@nginx.com 
310*1681Smax.romanov@nginx.com static int
311*1681Smax.romanov@nginx.com nxt_python_init_threads(nxt_python_app_conf_t *c)
312*1681Smax.romanov@nginx.com {
313*1681Smax.romanov@nginx.com     int                    res;
314*1681Smax.romanov@nginx.com     uint32_t               i;
315*1681Smax.romanov@nginx.com     nxt_py_thread_info_t   *ti;
316*1681Smax.romanov@nginx.com     static pthread_attr_t  attr;
317*1681Smax.romanov@nginx.com 
318*1681Smax.romanov@nginx.com     if (c->threads <= 1) {
319*1681Smax.romanov@nginx.com         return NXT_UNIT_OK;
320*1681Smax.romanov@nginx.com     }
321*1681Smax.romanov@nginx.com 
322*1681Smax.romanov@nginx.com     if (c->thread_stack_size > 0) {
323*1681Smax.romanov@nginx.com         res = pthread_attr_init(&attr);
324*1681Smax.romanov@nginx.com         if (nxt_slow_path(res != 0)) {
325*1681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "thread attr init failed: %s (%d)",
326*1681Smax.romanov@nginx.com                            strerror(res), res);
327*1681Smax.romanov@nginx.com 
328*1681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
329*1681Smax.romanov@nginx.com         }
330*1681Smax.romanov@nginx.com 
331*1681Smax.romanov@nginx.com         res = pthread_attr_setstacksize(&attr, c->thread_stack_size);
332*1681Smax.romanov@nginx.com         if (nxt_slow_path(res != 0)) {
333*1681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)",
334*1681Smax.romanov@nginx.com                            strerror(res), res);
335*1681Smax.romanov@nginx.com 
336*1681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
337*1681Smax.romanov@nginx.com         }
338*1681Smax.romanov@nginx.com 
339*1681Smax.romanov@nginx.com         nxt_py_thread_attr = &attr;
340*1681Smax.romanov@nginx.com     }
341*1681Smax.romanov@nginx.com 
342*1681Smax.romanov@nginx.com     nxt_py_threads = nxt_unit_malloc(NULL, sizeof(nxt_py_thread_info_t)
343*1681Smax.romanov@nginx.com                                            * (c->threads - 1));
344*1681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_threads == NULL)) {
345*1681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Failed to allocate thread info array");
346*1681Smax.romanov@nginx.com 
347*1681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
348*1681Smax.romanov@nginx.com     }
349*1681Smax.romanov@nginx.com 
350*1681Smax.romanov@nginx.com     memset(nxt_py_threads, 0, sizeof(nxt_py_thread_info_t) * (c->threads - 1));
351*1681Smax.romanov@nginx.com 
352*1681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
353*1681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
354*1681Smax.romanov@nginx.com 
355*1681Smax.romanov@nginx.com         res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data);
356*1681Smax.romanov@nginx.com         if (nxt_slow_path(res != NXT_UNIT_OK)) {
357*1681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
358*1681Smax.romanov@nginx.com         }
359*1681Smax.romanov@nginx.com     }
360*1681Smax.romanov@nginx.com 
361*1681Smax.romanov@nginx.com     return NXT_UNIT_OK;
362*1681Smax.romanov@nginx.com }
363*1681Smax.romanov@nginx.com 
364*1681Smax.romanov@nginx.com 
365*1681Smax.romanov@nginx.com static int
366*1681Smax.romanov@nginx.com nxt_python_ready_handler(nxt_unit_ctx_t *ctx)
367*1681Smax.romanov@nginx.com {
368*1681Smax.romanov@nginx.com     int                    res;
369*1681Smax.romanov@nginx.com     uint32_t               i;
370*1681Smax.romanov@nginx.com     nxt_py_thread_info_t   *ti;
371*1681Smax.romanov@nginx.com     nxt_python_app_conf_t  *c;
372*1681Smax.romanov@nginx.com 
373*1681Smax.romanov@nginx.com     if (nxt_py_proto.ready != NULL) {
374*1681Smax.romanov@nginx.com         res = nxt_py_proto.ready(ctx);
375*1681Smax.romanov@nginx.com         if (nxt_slow_path(res != NXT_UNIT_OK)) {
376*1681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
377*1681Smax.romanov@nginx.com         }
378*1681Smax.romanov@nginx.com     }
379*1681Smax.romanov@nginx.com 
380*1681Smax.romanov@nginx.com     /* Worker thread context. */
381*1681Smax.romanov@nginx.com     if (!nxt_unit_is_main_ctx(ctx)) {
382*1681Smax.romanov@nginx.com         return NXT_UNIT_OK;
383*1681Smax.romanov@nginx.com     }
384*1681Smax.romanov@nginx.com 
385*1681Smax.romanov@nginx.com     c = ctx->unit->data;
386*1681Smax.romanov@nginx.com 
387*1681Smax.romanov@nginx.com     if (c->threads <= 1) {
388*1681Smax.romanov@nginx.com         return NXT_UNIT_OK;
389*1681Smax.romanov@nginx.com     }
390*1681Smax.romanov@nginx.com 
391*1681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
392*1681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
393*1681Smax.romanov@nginx.com 
394*1681Smax.romanov@nginx.com         ti->ctx = ctx;
395*1681Smax.romanov@nginx.com 
396*1681Smax.romanov@nginx.com         res = pthread_create(&ti->thread, nxt_py_thread_attr,
397*1681Smax.romanov@nginx.com                              nxt_python_thread_func, ti);
398*1681Smax.romanov@nginx.com 
399*1681Smax.romanov@nginx.com         if (nxt_fast_path(res == 0)) {
400*1681Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
401*1681Smax.romanov@nginx.com 
402*1681Smax.romanov@nginx.com         } else {
403*1681Smax.romanov@nginx.com             nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)",
404*1681Smax.romanov@nginx.com                            (int) (i + 1), strerror(res), res);
405*1681Smax.romanov@nginx.com         }
406*1681Smax.romanov@nginx.com     }
407*1681Smax.romanov@nginx.com 
408*1681Smax.romanov@nginx.com     return NXT_UNIT_OK;
409*1681Smax.romanov@nginx.com }
410*1681Smax.romanov@nginx.com 
411*1681Smax.romanov@nginx.com 
412*1681Smax.romanov@nginx.com static void *
413*1681Smax.romanov@nginx.com nxt_python_thread_func(void *data)
414*1681Smax.romanov@nginx.com {
415*1681Smax.romanov@nginx.com     nxt_unit_ctx_t        *ctx;
416*1681Smax.romanov@nginx.com     PyGILState_STATE      gstate;
417*1681Smax.romanov@nginx.com     nxt_py_thread_info_t  *ti;
418*1681Smax.romanov@nginx.com 
419*1681Smax.romanov@nginx.com     ti = data;
420*1681Smax.romanov@nginx.com 
421*1681Smax.romanov@nginx.com     nxt_unit_debug(ti->ctx, "worker thread #%d start",
422*1681Smax.romanov@nginx.com                    (int) (ti - nxt_py_threads + 1));
423*1681Smax.romanov@nginx.com 
424*1681Smax.romanov@nginx.com     gstate = PyGILState_Ensure();
425*1681Smax.romanov@nginx.com 
426*1681Smax.romanov@nginx.com     if (nxt_py_proto.startup != NULL) {
427*1681Smax.romanov@nginx.com         if (nxt_py_proto.startup(ti->ctx_data) != NXT_UNIT_OK) {
428*1681Smax.romanov@nginx.com             goto fail;
429*1681Smax.romanov@nginx.com         }
430*1681Smax.romanov@nginx.com     }
431*1681Smax.romanov@nginx.com 
432*1681Smax.romanov@nginx.com     ctx = nxt_unit_ctx_alloc(ti->ctx, ti->ctx_data);
433*1681Smax.romanov@nginx.com     if (nxt_slow_path(ctx == NULL)) {
434*1681Smax.romanov@nginx.com         goto fail;
435*1681Smax.romanov@nginx.com     }
436*1681Smax.romanov@nginx.com 
437*1681Smax.romanov@nginx.com     (void) nxt_py_proto.run(ctx);
438*1681Smax.romanov@nginx.com 
439*1681Smax.romanov@nginx.com     nxt_unit_done(ctx);
440*1681Smax.romanov@nginx.com 
441*1681Smax.romanov@nginx.com fail:
442*1681Smax.romanov@nginx.com 
443*1681Smax.romanov@nginx.com     PyGILState_Release(gstate);
444*1681Smax.romanov@nginx.com 
445*1681Smax.romanov@nginx.com     nxt_unit_debug(NULL, "worker thread #%d end",
446*1681Smax.romanov@nginx.com                    (int) (ti - nxt_py_threads + 1));
447*1681Smax.romanov@nginx.com 
448*1681Smax.romanov@nginx.com     return NULL;
449*1681Smax.romanov@nginx.com }
450*1681Smax.romanov@nginx.com 
451*1681Smax.romanov@nginx.com 
452*1681Smax.romanov@nginx.com static void
453*1681Smax.romanov@nginx.com nxt_python_join_threads(nxt_unit_ctx_t *ctx, nxt_python_app_conf_t *c)
454*1681Smax.romanov@nginx.com {
455*1681Smax.romanov@nginx.com     int                   res;
456*1681Smax.romanov@nginx.com     uint32_t              i;
457*1681Smax.romanov@nginx.com     PyThreadState         *thread_state;
458*1681Smax.romanov@nginx.com     nxt_py_thread_info_t  *ti;
459*1681Smax.romanov@nginx.com 
460*1681Smax.romanov@nginx.com     if (nxt_py_threads == NULL) {
461*1681Smax.romanov@nginx.com         return;
462*1681Smax.romanov@nginx.com     }
463*1681Smax.romanov@nginx.com 
464*1681Smax.romanov@nginx.com     thread_state = PyEval_SaveThread();
465*1681Smax.romanov@nginx.com 
466*1681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
467*1681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
468*1681Smax.romanov@nginx.com 
469*1681Smax.romanov@nginx.com         if ((uintptr_t) ti->thread == 0) {
470*1681Smax.romanov@nginx.com             continue;
471*1681Smax.romanov@nginx.com         }
472*1681Smax.romanov@nginx.com 
473*1681Smax.romanov@nginx.com         res = pthread_join(ti->thread, NULL);
474*1681Smax.romanov@nginx.com 
475*1681Smax.romanov@nginx.com         if (nxt_fast_path(res == 0)) {
476*1681Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1));
477*1681Smax.romanov@nginx.com 
478*1681Smax.romanov@nginx.com         } else {
479*1681Smax.romanov@nginx.com             nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)",
480*1681Smax.romanov@nginx.com                            (int) (i + 1), strerror(res), res);
481*1681Smax.romanov@nginx.com         }
482*1681Smax.romanov@nginx.com     }
483*1681Smax.romanov@nginx.com 
484*1681Smax.romanov@nginx.com     PyEval_RestoreThread(thread_state);
485*1681Smax.romanov@nginx.com 
486*1681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
487*1681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
488*1681Smax.romanov@nginx.com 
489*1681Smax.romanov@nginx.com         if (ti->ctx_data != NULL) {
490*1681Smax.romanov@nginx.com             nxt_py_proto.ctx_data_free(ti->ctx_data);
491*1681Smax.romanov@nginx.com         }
492*1681Smax.romanov@nginx.com     }
493*1681Smax.romanov@nginx.com 
494*1681Smax.romanov@nginx.com     nxt_unit_free(NULL, nxt_py_threads);
495*1681Smax.romanov@nginx.com }
496*1681Smax.romanov@nginx.com 
497*1681Smax.romanov@nginx.com 
498*1681Smax.romanov@nginx.com int
4991592Smax.romanov@nginx.com nxt_python_init_strings(nxt_python_string_t *pstr)
5001592Smax.romanov@nginx.com {
5011592Smax.romanov@nginx.com     PyObject  *obj;
5021592Smax.romanov@nginx.com 
5031592Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
5041592Smax.romanov@nginx.com         obj = PyString_FromStringAndSize((char *) pstr->string.start,
5051592Smax.romanov@nginx.com                                          pstr->string.length);
5061592Smax.romanov@nginx.com         if (nxt_slow_path(obj == NULL)) {
507*1681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
5081592Smax.romanov@nginx.com         }
5091592Smax.romanov@nginx.com 
5101592Smax.romanov@nginx.com         PyUnicode_InternInPlace(&obj);
5111592Smax.romanov@nginx.com 
5121592Smax.romanov@nginx.com         *pstr->object_p = obj;
5131592Smax.romanov@nginx.com 
5141592Smax.romanov@nginx.com         pstr++;
5151592Smax.romanov@nginx.com     }
5161592Smax.romanov@nginx.com 
517*1681Smax.romanov@nginx.com     return NXT_UNIT_OK;
5181592Smax.romanov@nginx.com }
5191592Smax.romanov@nginx.com 
5201592Smax.romanov@nginx.com 
5211592Smax.romanov@nginx.com void
5221592Smax.romanov@nginx.com nxt_python_done_strings(nxt_python_string_t *pstr)
5231592Smax.romanov@nginx.com {
5241592Smax.romanov@nginx.com     PyObject  *obj;
5251592Smax.romanov@nginx.com 
5261592Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
5271592Smax.romanov@nginx.com         obj = *pstr->object_p;
5281592Smax.romanov@nginx.com 
5291592Smax.romanov@nginx.com         Py_XDECREF(obj);
5301592Smax.romanov@nginx.com         *pstr->object_p = NULL;
5311592Smax.romanov@nginx.com 
5321592Smax.romanov@nginx.com         pstr++;
5331592Smax.romanov@nginx.com     }
5341592Smax.romanov@nginx.com }
5351592Smax.romanov@nginx.com 
5361592Smax.romanov@nginx.com 
5371592Smax.romanov@nginx.com static void
5381592Smax.romanov@nginx.com nxt_python_atexit(void)
5391592Smax.romanov@nginx.com {
540*1681Smax.romanov@nginx.com     if (nxt_py_proto.done != NULL) {
541*1681Smax.romanov@nginx.com         nxt_py_proto.done();
542*1681Smax.romanov@nginx.com     }
5431592Smax.romanov@nginx.com 
5441592Smax.romanov@nginx.com     Py_XDECREF(nxt_py_stderr_flush);
5451592Smax.romanov@nginx.com     Py_XDECREF(nxt_py_application);
5461592Smax.romanov@nginx.com 
5471592Smax.romanov@nginx.com     Py_Finalize();
5481592Smax.romanov@nginx.com 
5491592Smax.romanov@nginx.com     if (nxt_py_home != NULL) {
5501592Smax.romanov@nginx.com         nxt_free(nxt_py_home);
5511592Smax.romanov@nginx.com     }
5521592Smax.romanov@nginx.com }
5531592Smax.romanov@nginx.com 
5541592Smax.romanov@nginx.com 
5551592Smax.romanov@nginx.com void
5561592Smax.romanov@nginx.com nxt_python_print_exception(void)
5571592Smax.romanov@nginx.com {
5581592Smax.romanov@nginx.com     PyErr_Print();
5591592Smax.romanov@nginx.com 
5601592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
5611592Smax.romanov@nginx.com     /* The backtrace may be buffered in sys.stderr file object. */
5621592Smax.romanov@nginx.com     {
5631592Smax.romanov@nginx.com         PyObject  *result;
5641592Smax.romanov@nginx.com 
5651592Smax.romanov@nginx.com         result = PyObject_CallFunction(nxt_py_stderr_flush, NULL);
5661592Smax.romanov@nginx.com         if (nxt_slow_path(result == NULL)) {
5671592Smax.romanov@nginx.com             PyErr_Clear();
5681592Smax.romanov@nginx.com             return;
5691592Smax.romanov@nginx.com         }
5701592Smax.romanov@nginx.com 
5711592Smax.romanov@nginx.com         Py_DECREF(result);
5721592Smax.romanov@nginx.com     }
5731592Smax.romanov@nginx.com #endif
5741592Smax.romanov@nginx.com }
575