xref: /unit/src/python/nxt_python.c (revision 2338:a5f3bc4abb97)
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 
181681Smax.romanov@nginx.com typedef struct {
191681Smax.romanov@nginx.com     pthread_t       thread;
201681Smax.romanov@nginx.com     nxt_unit_ctx_t  *ctx;
211681Smax.romanov@nginx.com     void            *ctx_data;
221681Smax.romanov@nginx.com } nxt_py_thread_info_t;
231681Smax.romanov@nginx.com 
241681Smax.romanov@nginx.com 
252251Sa.clayton@nginx.com #if PY_MAJOR_VERSION == 3
262251Sa.clayton@nginx.com static nxt_int_t nxt_python3_init_config(nxt_int_t pep405);
272251Sa.clayton@nginx.com #endif
282251Sa.clayton@nginx.com 
291592Smax.romanov@nginx.com static nxt_int_t nxt_python_start(nxt_task_t *task,
301592Smax.romanov@nginx.com     nxt_process_data_t *data);
311872So.canty@f5.com static nxt_int_t nxt_python_set_target(nxt_task_t *task,
321872So.canty@f5.com     nxt_python_target_t *target, nxt_conf_value_t *conf);
33*2273Sjeff.iadarola@gmail.com nxt_inline nxt_int_t nxt_python_set_prefix(nxt_task_t *task,
34*2273Sjeff.iadarola@gmail.com     nxt_python_target_t *target, nxt_conf_value_t *value);
351759Svbart@nginx.com static nxt_int_t nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value);
361681Smax.romanov@nginx.com static int nxt_python_init_threads(nxt_python_app_conf_t *c);
371681Smax.romanov@nginx.com static int nxt_python_ready_handler(nxt_unit_ctx_t *ctx);
381681Smax.romanov@nginx.com static void *nxt_python_thread_func(void *main_ctx);
391681Smax.romanov@nginx.com static void nxt_python_join_threads(nxt_unit_ctx_t *ctx,
401681Smax.romanov@nginx.com     nxt_python_app_conf_t *c);
411592Smax.romanov@nginx.com static void nxt_python_atexit(void);
421592Smax.romanov@nginx.com 
431592Smax.romanov@nginx.com static uint32_t  compat[] = {
441592Smax.romanov@nginx.com     NXT_VERNUM, NXT_DEBUG,
451592Smax.romanov@nginx.com };
461592Smax.romanov@nginx.com 
471592Smax.romanov@nginx.com 
481592Smax.romanov@nginx.com NXT_EXPORT nxt_app_module_t  nxt_app_module = {
491592Smax.romanov@nginx.com     sizeof(compat),
501592Smax.romanov@nginx.com     compat,
511592Smax.romanov@nginx.com     nxt_string("python"),
521592Smax.romanov@nginx.com     PY_VERSION,
531592Smax.romanov@nginx.com     nxt_python_mounts,
541592Smax.romanov@nginx.com     nxt_nitems(nxt_python_mounts),
551592Smax.romanov@nginx.com     NULL,
561592Smax.romanov@nginx.com     nxt_python_start,
571592Smax.romanov@nginx.com };
581592Smax.romanov@nginx.com 
591592Smax.romanov@nginx.com static PyObject           *nxt_py_stderr_flush;
601872So.canty@f5.com nxt_python_targets_t      *nxt_py_targets;
611592Smax.romanov@nginx.com 
621592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
631592Smax.romanov@nginx.com static wchar_t            *nxt_py_home;
641592Smax.romanov@nginx.com #else
651592Smax.romanov@nginx.com static char               *nxt_py_home;
661592Smax.romanov@nginx.com #endif
671592Smax.romanov@nginx.com 
681681Smax.romanov@nginx.com static pthread_attr_t        *nxt_py_thread_attr;
691681Smax.romanov@nginx.com static nxt_py_thread_info_t  *nxt_py_threads;
701681Smax.romanov@nginx.com static nxt_python_proto_t    nxt_py_proto;
711681Smax.romanov@nginx.com 
721592Smax.romanov@nginx.com 
732251Sa.clayton@nginx.com #if PY_VERSION_HEX >= NXT_PYTHON_VER(3, 8)
742251Sa.clayton@nginx.com 
752251Sa.clayton@nginx.com static nxt_int_t
nxt_python3_init_config(nxt_int_t pep405)762251Sa.clayton@nginx.com nxt_python3_init_config(nxt_int_t pep405)
772251Sa.clayton@nginx.com {
782337Sa.clayton@nginx.com     PyConfig     config;
792337Sa.clayton@nginx.com     PyStatus     status;
802337Sa.clayton@nginx.com     nxt_int_t    ret;
812338Sa.clayton@nginx.com     PyPreConfig  preconfig;
822337Sa.clayton@nginx.com 
832337Sa.clayton@nginx.com     ret = NXT_ERROR;
842251Sa.clayton@nginx.com 
852338Sa.clayton@nginx.com     PyPreConfig_InitIsolatedConfig(&preconfig);
862338Sa.clayton@nginx.com     /*
872338Sa.clayton@nginx.com      * Determine whether to use UTF-8 mode or not, UTF-8
882338Sa.clayton@nginx.com      * will be enabled if LC_CTYPE is C, POSIX or some
892338Sa.clayton@nginx.com      * specific UTF-8 locale.
902338Sa.clayton@nginx.com      */
912338Sa.clayton@nginx.com     preconfig.utf8_mode = -1;
922338Sa.clayton@nginx.com 
932338Sa.clayton@nginx.com     status = Py_PreInitialize(&preconfig);
942338Sa.clayton@nginx.com     if (PyStatus_Exception(status)) {
952338Sa.clayton@nginx.com         return ret;
962338Sa.clayton@nginx.com     }
972338Sa.clayton@nginx.com 
982251Sa.clayton@nginx.com     PyConfig_InitIsolatedConfig(&config);
992251Sa.clayton@nginx.com 
1002251Sa.clayton@nginx.com     if (pep405) {
1012251Sa.clayton@nginx.com         status = PyConfig_SetString(&config, &config.program_name,
1022251Sa.clayton@nginx.com                                     nxt_py_home);
1032251Sa.clayton@nginx.com         if (PyStatus_Exception(status)) {
1042337Sa.clayton@nginx.com             goto out_config_clear;
1052251Sa.clayton@nginx.com         }
1062251Sa.clayton@nginx.com 
1072251Sa.clayton@nginx.com     } else {
1082337Sa.clayton@nginx.com         status = PyConfig_SetString(&config, &config.home, nxt_py_home);
1092251Sa.clayton@nginx.com         if (PyStatus_Exception(status)) {
1102337Sa.clayton@nginx.com             goto out_config_clear;
1112251Sa.clayton@nginx.com         }
1122251Sa.clayton@nginx.com     }
1132251Sa.clayton@nginx.com 
1142251Sa.clayton@nginx.com     status = Py_InitializeFromConfig(&config);
1152251Sa.clayton@nginx.com     if (PyStatus_Exception(status)) {
1162337Sa.clayton@nginx.com         goto out_config_clear;
1172251Sa.clayton@nginx.com     }
1182251Sa.clayton@nginx.com 
1192337Sa.clayton@nginx.com     ret = NXT_OK;
1202251Sa.clayton@nginx.com 
1212337Sa.clayton@nginx.com out_config_clear:
1222251Sa.clayton@nginx.com 
1232251Sa.clayton@nginx.com     PyConfig_Clear(&config);
1242251Sa.clayton@nginx.com 
1252337Sa.clayton@nginx.com     return ret;
1262251Sa.clayton@nginx.com }
1272251Sa.clayton@nginx.com 
1282251Sa.clayton@nginx.com #elif PY_MAJOR_VERSION == 3
1292251Sa.clayton@nginx.com 
1302251Sa.clayton@nginx.com static nxt_int_t
nxt_python3_init_config(nxt_int_t pep405)1312251Sa.clayton@nginx.com nxt_python3_init_config(nxt_int_t pep405)
1322251Sa.clayton@nginx.com {
1332251Sa.clayton@nginx.com     if (pep405) {
1342251Sa.clayton@nginx.com         Py_SetProgramName(nxt_py_home);
1352251Sa.clayton@nginx.com 
1362251Sa.clayton@nginx.com     } else {
1372251Sa.clayton@nginx.com         Py_SetPythonHome(nxt_py_home);
1382251Sa.clayton@nginx.com     }
1392251Sa.clayton@nginx.com 
1402251Sa.clayton@nginx.com     return NXT_OK;
1412251Sa.clayton@nginx.com }
1422251Sa.clayton@nginx.com 
1432251Sa.clayton@nginx.com #endif
1442251Sa.clayton@nginx.com 
1452251Sa.clayton@nginx.com 
1461592Smax.romanov@nginx.com static nxt_int_t
nxt_python_start(nxt_task_t * task,nxt_process_data_t * data)1471592Smax.romanov@nginx.com nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
1481592Smax.romanov@nginx.com {
1491681Smax.romanov@nginx.com     int                    rc;
1501872So.canty@f5.com     size_t                 len, size;
1511872So.canty@f5.com     uint32_t               next;
1522240Sjeff.iadarola@gmail.com     PyObject               *obj;
1531872So.canty@f5.com     nxt_str_t              proto, probe_proto, name;
1541872So.canty@f5.com     nxt_int_t              ret, n, i;
1551592Smax.romanov@nginx.com     nxt_unit_ctx_t         *unit_ctx;
1561592Smax.romanov@nginx.com     nxt_unit_init_t        python_init;
1571872So.canty@f5.com     nxt_conf_value_t       *cv;
1581872So.canty@f5.com     nxt_python_targets_t   *targets;
1591592Smax.romanov@nginx.com     nxt_common_app_conf_t  *app_conf;
1601592Smax.romanov@nginx.com     nxt_python_app_conf_t  *c;
1611592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
1621592Smax.romanov@nginx.com     char                   *path;
1631592Smax.romanov@nginx.com     nxt_int_t              pep405;
1641592Smax.romanov@nginx.com 
1651592Smax.romanov@nginx.com     static const char pyvenv[] = "/pyvenv.cfg";
1661592Smax.romanov@nginx.com     static const char bin_python[] = "/bin/python";
1671592Smax.romanov@nginx.com #endif
1681592Smax.romanov@nginx.com 
1691697Smax.romanov@nginx.com     static const nxt_str_t  wsgi = nxt_string("wsgi");
1701697Smax.romanov@nginx.com     static const nxt_str_t  asgi = nxt_string("asgi");
1711697Smax.romanov@nginx.com 
1721592Smax.romanov@nginx.com     app_conf = data->app;
1731592Smax.romanov@nginx.com     c = &app_conf->u.python;
1741592Smax.romanov@nginx.com 
1751592Smax.romanov@nginx.com     if (c->home != NULL) {
1761592Smax.romanov@nginx.com         len = nxt_strlen(c->home);
1771592Smax.romanov@nginx.com 
1781592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
1791592Smax.romanov@nginx.com 
1801592Smax.romanov@nginx.com         path = nxt_malloc(len + sizeof(pyvenv));
1811592Smax.romanov@nginx.com         if (nxt_slow_path(path == NULL)) {
1821592Smax.romanov@nginx.com             nxt_alert(task, "Failed to allocate memory");
1831592Smax.romanov@nginx.com             return NXT_ERROR;
1841592Smax.romanov@nginx.com         }
1851592Smax.romanov@nginx.com 
1861592Smax.romanov@nginx.com         nxt_memcpy(path, c->home, len);
1871592Smax.romanov@nginx.com         nxt_memcpy(path + len, pyvenv, sizeof(pyvenv));
1881592Smax.romanov@nginx.com 
1891592Smax.romanov@nginx.com         pep405 = (access(path, R_OK) == 0);
1901592Smax.romanov@nginx.com 
1911592Smax.romanov@nginx.com         nxt_free(path);
1921592Smax.romanov@nginx.com 
1931592Smax.romanov@nginx.com         if (pep405) {
1941592Smax.romanov@nginx.com             size = (len + sizeof(bin_python)) * sizeof(wchar_t);
1951592Smax.romanov@nginx.com 
1961592Smax.romanov@nginx.com         } else {
1971592Smax.romanov@nginx.com             size = (len + 1) * sizeof(wchar_t);
1981592Smax.romanov@nginx.com         }
1991592Smax.romanov@nginx.com 
2001592Smax.romanov@nginx.com         nxt_py_home = nxt_malloc(size);
2011592Smax.romanov@nginx.com         if (nxt_slow_path(nxt_py_home == NULL)) {
2021592Smax.romanov@nginx.com             nxt_alert(task, "Failed to allocate memory");
2031592Smax.romanov@nginx.com             return NXT_ERROR;
2041592Smax.romanov@nginx.com         }
2051592Smax.romanov@nginx.com 
2061592Smax.romanov@nginx.com         if (pep405) {
2071592Smax.romanov@nginx.com             mbstowcs(nxt_py_home, c->home, len);
2081592Smax.romanov@nginx.com             mbstowcs(nxt_py_home + len, bin_python, sizeof(bin_python));
2091592Smax.romanov@nginx.com 
2101592Smax.romanov@nginx.com         } else {
2111592Smax.romanov@nginx.com             mbstowcs(nxt_py_home, c->home, len + 1);
2122251Sa.clayton@nginx.com         }
2132251Sa.clayton@nginx.com 
2142251Sa.clayton@nginx.com         ret = nxt_python3_init_config(pep405);
2152251Sa.clayton@nginx.com         if (nxt_slow_path(ret == NXT_ERROR)) {
2162251Sa.clayton@nginx.com             nxt_alert(task, "Failed to initialise config");
2172251Sa.clayton@nginx.com             return NXT_ERROR;
2181592Smax.romanov@nginx.com         }
2191592Smax.romanov@nginx.com 
2201592Smax.romanov@nginx.com #else
2211592Smax.romanov@nginx.com         nxt_py_home = nxt_malloc(len + 1);
2221592Smax.romanov@nginx.com         if (nxt_slow_path(nxt_py_home == NULL)) {
2231592Smax.romanov@nginx.com             nxt_alert(task, "Failed to allocate memory");
2241592Smax.romanov@nginx.com             return NXT_ERROR;
2251592Smax.romanov@nginx.com         }
2261592Smax.romanov@nginx.com 
2271592Smax.romanov@nginx.com         nxt_memcpy(nxt_py_home, c->home, len + 1);
2281592Smax.romanov@nginx.com         Py_SetPythonHome(nxt_py_home);
2291592Smax.romanov@nginx.com #endif
2301592Smax.romanov@nginx.com     }
2311592Smax.romanov@nginx.com 
2321592Smax.romanov@nginx.com     Py_InitializeEx(0);
2331592Smax.romanov@nginx.com 
2341681Smax.romanov@nginx.com #if PY_VERSION_HEX < NXT_PYTHON_VER(3, 7)
2351681Smax.romanov@nginx.com     if (c->threads > 1) {
2361681Smax.romanov@nginx.com         PyEval_InitThreads();
2371681Smax.romanov@nginx.com     }
2381681Smax.romanov@nginx.com #endif
2391681Smax.romanov@nginx.com 
2401592Smax.romanov@nginx.com     obj = NULL;
2411592Smax.romanov@nginx.com 
2421681Smax.romanov@nginx.com     python_init.ctx_data = NULL;
2431681Smax.romanov@nginx.com 
2441592Smax.romanov@nginx.com     obj = PySys_GetObject((char *) "stderr");
2451592Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
2461592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to get \"sys.stderr\" object");
2471592Smax.romanov@nginx.com         goto fail;
2481592Smax.romanov@nginx.com     }
2491592Smax.romanov@nginx.com 
2501592Smax.romanov@nginx.com     nxt_py_stderr_flush = PyObject_GetAttrString(obj, "flush");
2511759Svbart@nginx.com 
2521759Svbart@nginx.com     /* obj is a Borrowed reference. */
2531759Svbart@nginx.com     obj = NULL;
2541759Svbart@nginx.com 
2551592Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_stderr_flush == NULL)) {
2561592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to get \"flush\" attribute of "
2571592Smax.romanov@nginx.com                         "\"sys.stderr\" object");
2581592Smax.romanov@nginx.com         goto fail;
2591592Smax.romanov@nginx.com     }
2601592Smax.romanov@nginx.com 
2611759Svbart@nginx.com     if (nxt_slow_path(nxt_python_set_path(task, c->path) != NXT_OK)) {
2621759Svbart@nginx.com         goto fail;
2631592Smax.romanov@nginx.com     }
2641592Smax.romanov@nginx.com 
2651592Smax.romanov@nginx.com     obj = Py_BuildValue("[s]", "unit");
2661592Smax.romanov@nginx.com     if (nxt_slow_path(obj == NULL)) {
2671592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to create the \"sys.argv\" list");
2681592Smax.romanov@nginx.com         goto fail;
2691592Smax.romanov@nginx.com     }
2701592Smax.romanov@nginx.com 
2711592Smax.romanov@nginx.com     if (nxt_slow_path(PySys_SetObject((char *) "argv", obj) != 0)) {
2721592Smax.romanov@nginx.com         nxt_alert(task, "Python failed to set the \"sys.argv\" list");
2731592Smax.romanov@nginx.com         goto fail;
2741592Smax.romanov@nginx.com     }
2751592Smax.romanov@nginx.com 
2761592Smax.romanov@nginx.com     Py_CLEAR(obj);
2771592Smax.romanov@nginx.com 
2781872So.canty@f5.com     n = (c->targets != NULL ? nxt_conf_object_members_count(c->targets) : 1);
2791872So.canty@f5.com 
2801872So.canty@f5.com     size = sizeof(nxt_python_targets_t) + n * sizeof(nxt_python_target_t);
2811592Smax.romanov@nginx.com 
2821872So.canty@f5.com     targets = nxt_unit_malloc(NULL, size);
2831872So.canty@f5.com     if (nxt_slow_path(targets == NULL)) {
2841872So.canty@f5.com         nxt_alert(task, "Could not allocate targets");
2851592Smax.romanov@nginx.com         goto fail;
2861592Smax.romanov@nginx.com     }
2871592Smax.romanov@nginx.com 
2881872So.canty@f5.com     memset(targets, 0, size);
2891601Smax.romanov@nginx.com 
2901872So.canty@f5.com     targets->count = n;
2911872So.canty@f5.com     nxt_py_targets = targets;
2921872So.canty@f5.com 
2931872So.canty@f5.com     if (c->targets != NULL) {
2941872So.canty@f5.com         next = 0;
2951592Smax.romanov@nginx.com 
2961872So.canty@f5.com         for (i = 0; /* void */; i++) {
2971872So.canty@f5.com             cv = nxt_conf_next_object_member(c->targets, &name, &next);
2981872So.canty@f5.com             if (cv == NULL) {
2991872So.canty@f5.com                 break;
3001872So.canty@f5.com             }
3011592Smax.romanov@nginx.com 
3021872So.canty@f5.com             ret = nxt_python_set_target(task, &targets->target[i], cv);
3031872So.canty@f5.com             if (nxt_slow_path(ret != NXT_OK)) {
3041872So.canty@f5.com                 goto fail;
3051872So.canty@f5.com             }
3061872So.canty@f5.com         }
3071592Smax.romanov@nginx.com 
3081872So.canty@f5.com     } else {
3091872So.canty@f5.com         ret = nxt_python_set_target(task, &targets->target[0], app_conf->self);
3101872So.canty@f5.com         if (nxt_slow_path(ret != NXT_OK)) {
3111872So.canty@f5.com             goto fail;
3121872So.canty@f5.com         }
3131872So.canty@f5.com     }
3141592Smax.romanov@nginx.com 
3151980Smax.romanov@nginx.com     nxt_unit_default_init(task, &python_init, data->app);
3161592Smax.romanov@nginx.com 
3171681Smax.romanov@nginx.com     python_init.data = c;
3181681Smax.romanov@nginx.com     python_init.callbacks.ready_handler = nxt_python_ready_handler;
3191592Smax.romanov@nginx.com 
3201697Smax.romanov@nginx.com     proto = c->protocol;
3211697Smax.romanov@nginx.com 
3221697Smax.romanov@nginx.com     if (proto.length == 0) {
3231872So.canty@f5.com         proto = nxt_python_asgi_check(targets->target[0].application)
3241872So.canty@f5.com                 ? asgi : wsgi;
3251872So.canty@f5.com 
3261872So.canty@f5.com         for (i = 1; i < targets->count; i++) {
3271872So.canty@f5.com             probe_proto = nxt_python_asgi_check(targets->target[i].application)
3281872So.canty@f5.com                           ? asgi : wsgi;
3291872So.canty@f5.com             if (probe_proto.start != proto.start) {
3301872So.canty@f5.com                 nxt_alert(task, "A mix of ASGI & WSGI targets is forbidden, "
3311872So.canty@f5.com                                 "specify protocol in config if incorrect");
3321872So.canty@f5.com                 goto fail;
3331872So.canty@f5.com             }
3341872So.canty@f5.com         }
3351697Smax.romanov@nginx.com     }
3361697Smax.romanov@nginx.com 
3371697Smax.romanov@nginx.com     if (nxt_strstr_eq(&proto, &asgi)) {
3381681Smax.romanov@nginx.com         rc = nxt_python_asgi_init(&python_init, &nxt_py_proto);
3391624Smax.romanov@nginx.com 
3401624Smax.romanov@nginx.com     } else {
3411681Smax.romanov@nginx.com         rc = nxt_python_wsgi_init(&python_init, &nxt_py_proto);
3421681Smax.romanov@nginx.com     }
3431681Smax.romanov@nginx.com 
3441681Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
3451681Smax.romanov@nginx.com         goto fail;
3461624Smax.romanov@nginx.com     }
3471624Smax.romanov@nginx.com 
3481918Smax.romanov@nginx.com     rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data, 1);
3491681Smax.romanov@nginx.com     if (nxt_slow_path(rc != NXT_UNIT_OK)) {
3501592Smax.romanov@nginx.com         goto fail;
3511592Smax.romanov@nginx.com     }
3521592Smax.romanov@nginx.com 
3531681Smax.romanov@nginx.com     rc = nxt_python_init_threads(c);
3541681Smax.romanov@nginx.com     if (nxt_slow_path(rc == NXT_UNIT_ERROR)) {
3551681Smax.romanov@nginx.com         goto fail;
3561681Smax.romanov@nginx.com     }
3571681Smax.romanov@nginx.com 
3581681Smax.romanov@nginx.com     if (nxt_py_proto.startup != NULL) {
3591681Smax.romanov@nginx.com         if (nxt_py_proto.startup(python_init.ctx_data) != NXT_UNIT_OK) {
3601681Smax.romanov@nginx.com             goto fail;
3611681Smax.romanov@nginx.com         }
3621681Smax.romanov@nginx.com     }
3631681Smax.romanov@nginx.com 
3641592Smax.romanov@nginx.com     unit_ctx = nxt_unit_init(&python_init);
3651592Smax.romanov@nginx.com     if (nxt_slow_path(unit_ctx == NULL)) {
3661592Smax.romanov@nginx.com         goto fail;
3671592Smax.romanov@nginx.com     }
3681592Smax.romanov@nginx.com 
3691681Smax.romanov@nginx.com     rc = nxt_py_proto.run(unit_ctx);
3701624Smax.romanov@nginx.com 
3711681Smax.romanov@nginx.com     nxt_python_join_threads(unit_ctx, c);
3721592Smax.romanov@nginx.com 
3731592Smax.romanov@nginx.com     nxt_unit_done(unit_ctx);
3741592Smax.romanov@nginx.com 
3751681Smax.romanov@nginx.com     nxt_py_proto.ctx_data_free(python_init.ctx_data);
3761681Smax.romanov@nginx.com 
3771592Smax.romanov@nginx.com     nxt_python_atexit();
3781592Smax.romanov@nginx.com 
3791592Smax.romanov@nginx.com     exit(rc);
3801592Smax.romanov@nginx.com 
3811592Smax.romanov@nginx.com     return NXT_OK;
3821592Smax.romanov@nginx.com 
3831592Smax.romanov@nginx.com fail:
3841592Smax.romanov@nginx.com 
3851681Smax.romanov@nginx.com     nxt_python_join_threads(NULL, c);
3861681Smax.romanov@nginx.com 
3871681Smax.romanov@nginx.com     if (python_init.ctx_data != NULL) {
3881681Smax.romanov@nginx.com         nxt_py_proto.ctx_data_free(python_init.ctx_data);
3891681Smax.romanov@nginx.com     }
3901681Smax.romanov@nginx.com 
3911592Smax.romanov@nginx.com     Py_XDECREF(obj);
3921592Smax.romanov@nginx.com 
3931592Smax.romanov@nginx.com     nxt_python_atexit();
3941592Smax.romanov@nginx.com 
3951592Smax.romanov@nginx.com     return NXT_ERROR;
3961592Smax.romanov@nginx.com }
3971592Smax.romanov@nginx.com 
3981592Smax.romanov@nginx.com 
3991759Svbart@nginx.com static nxt_int_t
nxt_python_set_target(nxt_task_t * task,nxt_python_target_t * target,nxt_conf_value_t * conf)4001872So.canty@f5.com nxt_python_set_target(nxt_task_t *task, nxt_python_target_t *target,
4011872So.canty@f5.com     nxt_conf_value_t *conf)
4021872So.canty@f5.com {
4031872So.canty@f5.com     char              *callable, *module_name;
4041872So.canty@f5.com     PyObject          *module, *obj;
4051872So.canty@f5.com     nxt_str_t         str;
4061872So.canty@f5.com     nxt_conf_value_t  *value;
4071872So.canty@f5.com 
4081872So.canty@f5.com     static nxt_str_t  module_str = nxt_string("module");
4091872So.canty@f5.com     static nxt_str_t  callable_str = nxt_string("callable");
410*2273Sjeff.iadarola@gmail.com     static nxt_str_t  prefix_str = nxt_string("prefix");
4111872So.canty@f5.com 
4121872So.canty@f5.com     module = obj = NULL;
4131872So.canty@f5.com 
4141872So.canty@f5.com     value = nxt_conf_get_object_member(conf, &module_str, NULL);
4151872So.canty@f5.com     if (nxt_slow_path(value == NULL)) {
4161872So.canty@f5.com         goto fail;
4171872So.canty@f5.com     }
4181872So.canty@f5.com 
4191872So.canty@f5.com     nxt_conf_get_string(value, &str);
4201872So.canty@f5.com 
4211872So.canty@f5.com     module_name = nxt_alloca(str.length + 1);
4221872So.canty@f5.com     nxt_memcpy(module_name, str.start, str.length);
4231872So.canty@f5.com     module_name[str.length] = '\0';
4241872So.canty@f5.com 
4251872So.canty@f5.com     module = PyImport_ImportModule(module_name);
4261872So.canty@f5.com     if (nxt_slow_path(module == NULL)) {
4271872So.canty@f5.com         nxt_alert(task, "Python failed to import module \"%s\"", module_name);
4281872So.canty@f5.com         nxt_python_print_exception();
4291872So.canty@f5.com         goto fail;
4301872So.canty@f5.com     }
4311872So.canty@f5.com 
4321872So.canty@f5.com     value = nxt_conf_get_object_member(conf, &callable_str, NULL);
4331872So.canty@f5.com     if (value == NULL) {
4341872So.canty@f5.com         callable = nxt_alloca(12);
4351872So.canty@f5.com         nxt_memcpy(callable, "application", 12);
4361872So.canty@f5.com 
4371872So.canty@f5.com     } else {
4381872So.canty@f5.com         nxt_conf_get_string(value, &str);
4391872So.canty@f5.com 
4401872So.canty@f5.com         callable = nxt_alloca(str.length + 1);
4411872So.canty@f5.com         nxt_memcpy(callable, str.start, str.length);
4421872So.canty@f5.com         callable[str.length] = '\0';
4431872So.canty@f5.com     }
4441872So.canty@f5.com 
4451872So.canty@f5.com     obj = PyDict_GetItemString(PyModule_GetDict(module), callable);
4461872So.canty@f5.com     if (nxt_slow_path(obj == NULL)) {
4471872So.canty@f5.com         nxt_alert(task, "Python failed to get \"%s\" from module \"%s\"",
4481932Smax.romanov@nginx.com                   callable, module_name);
4491872So.canty@f5.com         goto fail;
4501872So.canty@f5.com     }
4511872So.canty@f5.com 
4521872So.canty@f5.com     if (nxt_slow_path(PyCallable_Check(obj) == 0)) {
4531872So.canty@f5.com         nxt_alert(task, "\"%s\" in module \"%s\" is not a callable object",
4541932Smax.romanov@nginx.com                   callable, module_name);
4551872So.canty@f5.com         goto fail;
4561872So.canty@f5.com     }
4571872So.canty@f5.com 
458*2273Sjeff.iadarola@gmail.com     value = nxt_conf_get_object_member(conf, &prefix_str, NULL);
459*2273Sjeff.iadarola@gmail.com     if (nxt_slow_path(nxt_python_set_prefix(task, target, value) != NXT_OK)) {
460*2273Sjeff.iadarola@gmail.com         goto fail;
461*2273Sjeff.iadarola@gmail.com     }
462*2273Sjeff.iadarola@gmail.com 
4631872So.canty@f5.com     target->application = obj;
4641872So.canty@f5.com     obj = NULL;
4651872So.canty@f5.com 
4661872So.canty@f5.com     Py_INCREF(target->application);
4671872So.canty@f5.com     Py_CLEAR(module);
4681872So.canty@f5.com 
4691872So.canty@f5.com     return NXT_OK;
4701872So.canty@f5.com 
4711872So.canty@f5.com fail:
4721872So.canty@f5.com 
4731872So.canty@f5.com     Py_XDECREF(obj);
4741872So.canty@f5.com     Py_XDECREF(module);
4751872So.canty@f5.com 
4761872So.canty@f5.com     return NXT_ERROR;
4771872So.canty@f5.com }
4781872So.canty@f5.com 
4791872So.canty@f5.com 
480*2273Sjeff.iadarola@gmail.com nxt_inline nxt_int_t
nxt_python_set_prefix(nxt_task_t * task,nxt_python_target_t * target,nxt_conf_value_t * value)481*2273Sjeff.iadarola@gmail.com nxt_python_set_prefix(nxt_task_t *task, nxt_python_target_t *target,
482*2273Sjeff.iadarola@gmail.com     nxt_conf_value_t *value)
483*2273Sjeff.iadarola@gmail.com {
484*2273Sjeff.iadarola@gmail.com     u_char            *prefix;
485*2273Sjeff.iadarola@gmail.com     nxt_str_t         str;
486*2273Sjeff.iadarola@gmail.com 
487*2273Sjeff.iadarola@gmail.com     if (value == NULL) {
488*2273Sjeff.iadarola@gmail.com         return NXT_OK;
489*2273Sjeff.iadarola@gmail.com     }
490*2273Sjeff.iadarola@gmail.com 
491*2273Sjeff.iadarola@gmail.com     nxt_conf_get_string(value, &str);
492*2273Sjeff.iadarola@gmail.com 
493*2273Sjeff.iadarola@gmail.com     if (str.length == 0) {
494*2273Sjeff.iadarola@gmail.com         return NXT_OK;
495*2273Sjeff.iadarola@gmail.com     }
496*2273Sjeff.iadarola@gmail.com 
497*2273Sjeff.iadarola@gmail.com     if (str.start[str.length - 1] == '/') {
498*2273Sjeff.iadarola@gmail.com         str.length--;
499*2273Sjeff.iadarola@gmail.com     }
500*2273Sjeff.iadarola@gmail.com     target->prefix.length = str.length;
501*2273Sjeff.iadarola@gmail.com     prefix = nxt_malloc(str.length);
502*2273Sjeff.iadarola@gmail.com     if (nxt_slow_path(prefix == NULL)) {
503*2273Sjeff.iadarola@gmail.com         nxt_alert(task, "Failed to allocate target prefix string");
504*2273Sjeff.iadarola@gmail.com         return NXT_ERROR;
505*2273Sjeff.iadarola@gmail.com     }
506*2273Sjeff.iadarola@gmail.com 
507*2273Sjeff.iadarola@gmail.com     target->py_prefix = PyString_FromStringAndSize((char *)str.start,
508*2273Sjeff.iadarola@gmail.com                                                     str.length);
509*2273Sjeff.iadarola@gmail.com     if (nxt_slow_path(target->py_prefix == NULL)) {
510*2273Sjeff.iadarola@gmail.com         nxt_free(prefix);
511*2273Sjeff.iadarola@gmail.com         nxt_alert(task, "Python failed to allocate target prefix "
512*2273Sjeff.iadarola@gmail.com                         "string");
513*2273Sjeff.iadarola@gmail.com         return NXT_ERROR;
514*2273Sjeff.iadarola@gmail.com     }
515*2273Sjeff.iadarola@gmail.com     nxt_memcpy(prefix, str.start, str.length);
516*2273Sjeff.iadarola@gmail.com     target->prefix.start = prefix;
517*2273Sjeff.iadarola@gmail.com 
518*2273Sjeff.iadarola@gmail.com     return NXT_OK;
519*2273Sjeff.iadarola@gmail.com }
520*2273Sjeff.iadarola@gmail.com 
521*2273Sjeff.iadarola@gmail.com 
5221872So.canty@f5.com static nxt_int_t
nxt_python_set_path(nxt_task_t * task,nxt_conf_value_t * value)5231759Svbart@nginx.com nxt_python_set_path(nxt_task_t *task, nxt_conf_value_t *value)
5241759Svbart@nginx.com {
5251759Svbart@nginx.com     int               ret;
5261759Svbart@nginx.com     PyObject          *path, *sys;
5271759Svbart@nginx.com     nxt_str_t         str;
5281759Svbart@nginx.com     nxt_uint_t        n;
5291759Svbart@nginx.com     nxt_conf_value_t  *array;
5301759Svbart@nginx.com 
5311759Svbart@nginx.com     if (value == NULL) {
5321759Svbart@nginx.com         return NXT_OK;
5331759Svbart@nginx.com     }
5341759Svbart@nginx.com 
5351759Svbart@nginx.com     sys = PySys_GetObject((char *) "path");
5361759Svbart@nginx.com     if (nxt_slow_path(sys == NULL)) {
5371759Svbart@nginx.com         nxt_alert(task, "Python failed to get \"sys.path\" list");
5381759Svbart@nginx.com         return NXT_ERROR;
5391759Svbart@nginx.com     }
5401759Svbart@nginx.com 
5411759Svbart@nginx.com     /* sys is a Borrowed reference. */
5421759Svbart@nginx.com 
5431759Svbart@nginx.com     array = value;
5442077Salx.manpages@gmail.com     n = nxt_conf_array_elements_count_or_1(array);
5451759Svbart@nginx.com 
5461759Svbart@nginx.com     while (n != 0) {
5471759Svbart@nginx.com         n--;
5481759Svbart@nginx.com 
5491759Svbart@nginx.com         /*
5501759Svbart@nginx.com          * Insertion in front of existing paths starting from the last element
5511759Svbart@nginx.com          * to preserve original order while giving priority to the values
5521759Svbart@nginx.com          * specified in the "path" option.
5531759Svbart@nginx.com          */
5541759Svbart@nginx.com 
5552077Salx.manpages@gmail.com         value = nxt_conf_get_array_element_or_itself(array, n);
5561759Svbart@nginx.com 
5571759Svbart@nginx.com         nxt_conf_get_string(value, &str);
5581759Svbart@nginx.com 
5591759Svbart@nginx.com         path = PyString_FromStringAndSize((char *) str.start, str.length);
5601759Svbart@nginx.com         if (nxt_slow_path(path == NULL)) {
5611759Svbart@nginx.com             nxt_alert(task, "Python failed to create string object \"%V\"",
5621759Svbart@nginx.com                       &str);
5631759Svbart@nginx.com             return NXT_ERROR;
5641759Svbart@nginx.com         }
5651759Svbart@nginx.com 
5661759Svbart@nginx.com         ret = PyList_Insert(sys, 0, path);
5671759Svbart@nginx.com 
5681759Svbart@nginx.com         Py_DECREF(path);
5691759Svbart@nginx.com 
5701759Svbart@nginx.com         if (nxt_slow_path(ret != 0)) {
5711759Svbart@nginx.com             nxt_alert(task, "Python failed to insert \"%V\" into \"sys.path\"",
5721759Svbart@nginx.com                       &str);
5731759Svbart@nginx.com             return NXT_ERROR;
5741759Svbart@nginx.com         }
5751759Svbart@nginx.com     }
5761759Svbart@nginx.com 
5771759Svbart@nginx.com     return NXT_OK;
5781759Svbart@nginx.com }
5791759Svbart@nginx.com 
5801759Svbart@nginx.com 
5811681Smax.romanov@nginx.com static int
nxt_python_init_threads(nxt_python_app_conf_t * c)5821681Smax.romanov@nginx.com nxt_python_init_threads(nxt_python_app_conf_t *c)
5831681Smax.romanov@nginx.com {
5841681Smax.romanov@nginx.com     int                    res;
5851681Smax.romanov@nginx.com     uint32_t               i;
5861681Smax.romanov@nginx.com     nxt_py_thread_info_t   *ti;
5871681Smax.romanov@nginx.com     static pthread_attr_t  attr;
5881681Smax.romanov@nginx.com 
5891681Smax.romanov@nginx.com     if (c->threads <= 1) {
5901681Smax.romanov@nginx.com         return NXT_UNIT_OK;
5911681Smax.romanov@nginx.com     }
5921681Smax.romanov@nginx.com 
5931681Smax.romanov@nginx.com     if (c->thread_stack_size > 0) {
5941681Smax.romanov@nginx.com         res = pthread_attr_init(&attr);
5951681Smax.romanov@nginx.com         if (nxt_slow_path(res != 0)) {
5961681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "thread attr init failed: %s (%d)",
5971681Smax.romanov@nginx.com                            strerror(res), res);
5981681Smax.romanov@nginx.com 
5991681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
6001681Smax.romanov@nginx.com         }
6011681Smax.romanov@nginx.com 
6021681Smax.romanov@nginx.com         res = pthread_attr_setstacksize(&attr, c->thread_stack_size);
6031681Smax.romanov@nginx.com         if (nxt_slow_path(res != 0)) {
6041681Smax.romanov@nginx.com             nxt_unit_alert(NULL, "thread attr set stack size failed: %s (%d)",
6051681Smax.romanov@nginx.com                            strerror(res), res);
6061681Smax.romanov@nginx.com 
6071681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
6081681Smax.romanov@nginx.com         }
6091681Smax.romanov@nginx.com 
6101681Smax.romanov@nginx.com         nxt_py_thread_attr = &attr;
6111681Smax.romanov@nginx.com     }
6121681Smax.romanov@nginx.com 
6131681Smax.romanov@nginx.com     nxt_py_threads = nxt_unit_malloc(NULL, sizeof(nxt_py_thread_info_t)
6141681Smax.romanov@nginx.com                                            * (c->threads - 1));
6151681Smax.romanov@nginx.com     if (nxt_slow_path(nxt_py_threads == NULL)) {
6161681Smax.romanov@nginx.com         nxt_unit_alert(NULL, "Failed to allocate thread info array");
6171681Smax.romanov@nginx.com 
6181681Smax.romanov@nginx.com         return NXT_UNIT_ERROR;
6191681Smax.romanov@nginx.com     }
6201681Smax.romanov@nginx.com 
6211681Smax.romanov@nginx.com     memset(nxt_py_threads, 0, sizeof(nxt_py_thread_info_t) * (c->threads - 1));
6221681Smax.romanov@nginx.com 
6231681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
6241681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
6251681Smax.romanov@nginx.com 
6261918Smax.romanov@nginx.com         res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data, 0);
6271681Smax.romanov@nginx.com         if (nxt_slow_path(res != NXT_UNIT_OK)) {
6281681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
6291681Smax.romanov@nginx.com         }
6301681Smax.romanov@nginx.com     }
6311681Smax.romanov@nginx.com 
6321681Smax.romanov@nginx.com     return NXT_UNIT_OK;
6331681Smax.romanov@nginx.com }
6341681Smax.romanov@nginx.com 
6351681Smax.romanov@nginx.com 
6361681Smax.romanov@nginx.com static int
nxt_python_ready_handler(nxt_unit_ctx_t * ctx)6371681Smax.romanov@nginx.com nxt_python_ready_handler(nxt_unit_ctx_t *ctx)
6381681Smax.romanov@nginx.com {
6391681Smax.romanov@nginx.com     int                    res;
6401681Smax.romanov@nginx.com     uint32_t               i;
6411681Smax.romanov@nginx.com     nxt_py_thread_info_t   *ti;
6421681Smax.romanov@nginx.com     nxt_python_app_conf_t  *c;
6431681Smax.romanov@nginx.com 
6441681Smax.romanov@nginx.com     c = ctx->unit->data;
6451681Smax.romanov@nginx.com 
6461681Smax.romanov@nginx.com     if (c->threads <= 1) {
6471681Smax.romanov@nginx.com         return NXT_UNIT_OK;
6481681Smax.romanov@nginx.com     }
6491681Smax.romanov@nginx.com 
6501681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
6511681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
6521681Smax.romanov@nginx.com 
6531681Smax.romanov@nginx.com         ti->ctx = ctx;
6541681Smax.romanov@nginx.com 
6551681Smax.romanov@nginx.com         res = pthread_create(&ti->thread, nxt_py_thread_attr,
6561681Smax.romanov@nginx.com                              nxt_python_thread_func, ti);
6571681Smax.romanov@nginx.com 
6581681Smax.romanov@nginx.com         if (nxt_fast_path(res == 0)) {
6591681Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d created", (int) (i + 1));
6601681Smax.romanov@nginx.com 
6611681Smax.romanov@nginx.com         } else {
6621681Smax.romanov@nginx.com             nxt_unit_alert(ctx, "thread #%d create failed: %s (%d)",
6631681Smax.romanov@nginx.com                            (int) (i + 1), strerror(res), res);
6641681Smax.romanov@nginx.com         }
6651681Smax.romanov@nginx.com     }
6661681Smax.romanov@nginx.com 
6671681Smax.romanov@nginx.com     return NXT_UNIT_OK;
6681681Smax.romanov@nginx.com }
6691681Smax.romanov@nginx.com 
6701681Smax.romanov@nginx.com 
6711681Smax.romanov@nginx.com static void *
nxt_python_thread_func(void * data)6721681Smax.romanov@nginx.com nxt_python_thread_func(void *data)
6731681Smax.romanov@nginx.com {
6741681Smax.romanov@nginx.com     nxt_unit_ctx_t        *ctx;
6751681Smax.romanov@nginx.com     PyGILState_STATE      gstate;
6761681Smax.romanov@nginx.com     nxt_py_thread_info_t  *ti;
6771681Smax.romanov@nginx.com 
6781681Smax.romanov@nginx.com     ti = data;
6791681Smax.romanov@nginx.com 
6801681Smax.romanov@nginx.com     nxt_unit_debug(ti->ctx, "worker thread #%d start",
6811681Smax.romanov@nginx.com                    (int) (ti - nxt_py_threads + 1));
6821681Smax.romanov@nginx.com 
6831681Smax.romanov@nginx.com     gstate = PyGILState_Ensure();
6841681Smax.romanov@nginx.com 
6851681Smax.romanov@nginx.com     if (nxt_py_proto.startup != NULL) {
6861681Smax.romanov@nginx.com         if (nxt_py_proto.startup(ti->ctx_data) != NXT_UNIT_OK) {
6871681Smax.romanov@nginx.com             goto fail;
6881681Smax.romanov@nginx.com         }
6891681Smax.romanov@nginx.com     }
6901681Smax.romanov@nginx.com 
6911681Smax.romanov@nginx.com     ctx = nxt_unit_ctx_alloc(ti->ctx, ti->ctx_data);
6921681Smax.romanov@nginx.com     if (nxt_slow_path(ctx == NULL)) {
6931681Smax.romanov@nginx.com         goto fail;
6941681Smax.romanov@nginx.com     }
6951681Smax.romanov@nginx.com 
6961681Smax.romanov@nginx.com     (void) nxt_py_proto.run(ctx);
6971681Smax.romanov@nginx.com 
6981681Smax.romanov@nginx.com     nxt_unit_done(ctx);
6991681Smax.romanov@nginx.com 
7001681Smax.romanov@nginx.com fail:
7011681Smax.romanov@nginx.com 
7021681Smax.romanov@nginx.com     PyGILState_Release(gstate);
7031681Smax.romanov@nginx.com 
7041681Smax.romanov@nginx.com     nxt_unit_debug(NULL, "worker thread #%d end",
7051681Smax.romanov@nginx.com                    (int) (ti - nxt_py_threads + 1));
7061681Smax.romanov@nginx.com 
7071681Smax.romanov@nginx.com     return NULL;
7081681Smax.romanov@nginx.com }
7091681Smax.romanov@nginx.com 
7101681Smax.romanov@nginx.com 
7111681Smax.romanov@nginx.com static void
nxt_python_join_threads(nxt_unit_ctx_t * ctx,nxt_python_app_conf_t * c)7121681Smax.romanov@nginx.com nxt_python_join_threads(nxt_unit_ctx_t *ctx, nxt_python_app_conf_t *c)
7131681Smax.romanov@nginx.com {
7141681Smax.romanov@nginx.com     int                   res;
7151681Smax.romanov@nginx.com     uint32_t              i;
7161681Smax.romanov@nginx.com     PyThreadState         *thread_state;
7171681Smax.romanov@nginx.com     nxt_py_thread_info_t  *ti;
7181681Smax.romanov@nginx.com 
7191681Smax.romanov@nginx.com     if (nxt_py_threads == NULL) {
7201681Smax.romanov@nginx.com         return;
7211681Smax.romanov@nginx.com     }
7221681Smax.romanov@nginx.com 
7231681Smax.romanov@nginx.com     thread_state = PyEval_SaveThread();
7241681Smax.romanov@nginx.com 
7251681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
7261681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
7271681Smax.romanov@nginx.com 
7281681Smax.romanov@nginx.com         if ((uintptr_t) ti->thread == 0) {
7291681Smax.romanov@nginx.com             continue;
7301681Smax.romanov@nginx.com         }
7311681Smax.romanov@nginx.com 
7321681Smax.romanov@nginx.com         res = pthread_join(ti->thread, NULL);
7331681Smax.romanov@nginx.com 
7341681Smax.romanov@nginx.com         if (nxt_fast_path(res == 0)) {
7351681Smax.romanov@nginx.com             nxt_unit_debug(ctx, "thread #%d joined", (int) (i + 1));
7361681Smax.romanov@nginx.com 
7371681Smax.romanov@nginx.com         } else {
7381681Smax.romanov@nginx.com             nxt_unit_alert(ctx, "thread #%d join failed: %s (%d)",
7391681Smax.romanov@nginx.com                            (int) (i + 1), strerror(res), res);
7401681Smax.romanov@nginx.com         }
7411681Smax.romanov@nginx.com     }
7421681Smax.romanov@nginx.com 
7431681Smax.romanov@nginx.com     PyEval_RestoreThread(thread_state);
7441681Smax.romanov@nginx.com 
7451681Smax.romanov@nginx.com     for (i = 0; i < c->threads - 1; i++) {
7461681Smax.romanov@nginx.com         ti = &nxt_py_threads[i];
7471681Smax.romanov@nginx.com 
7481681Smax.romanov@nginx.com         if (ti->ctx_data != NULL) {
7491681Smax.romanov@nginx.com             nxt_py_proto.ctx_data_free(ti->ctx_data);
7501681Smax.romanov@nginx.com         }
7511681Smax.romanov@nginx.com     }
7521681Smax.romanov@nginx.com 
7531681Smax.romanov@nginx.com     nxt_unit_free(NULL, nxt_py_threads);
7541681Smax.romanov@nginx.com }
7551681Smax.romanov@nginx.com 
7561681Smax.romanov@nginx.com 
7571681Smax.romanov@nginx.com int
nxt_python_init_strings(nxt_python_string_t * pstr)7581592Smax.romanov@nginx.com nxt_python_init_strings(nxt_python_string_t *pstr)
7591592Smax.romanov@nginx.com {
7601592Smax.romanov@nginx.com     PyObject  *obj;
7611592Smax.romanov@nginx.com 
7621592Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
7631592Smax.romanov@nginx.com         obj = PyString_FromStringAndSize((char *) pstr->string.start,
7641592Smax.romanov@nginx.com                                          pstr->string.length);
7651592Smax.romanov@nginx.com         if (nxt_slow_path(obj == NULL)) {
7661681Smax.romanov@nginx.com             return NXT_UNIT_ERROR;
7671592Smax.romanov@nginx.com         }
7681592Smax.romanov@nginx.com 
7691592Smax.romanov@nginx.com         PyUnicode_InternInPlace(&obj);
7701592Smax.romanov@nginx.com 
7711592Smax.romanov@nginx.com         *pstr->object_p = obj;
7721592Smax.romanov@nginx.com 
7731592Smax.romanov@nginx.com         pstr++;
7741592Smax.romanov@nginx.com     }
7751592Smax.romanov@nginx.com 
7761681Smax.romanov@nginx.com     return NXT_UNIT_OK;
7771592Smax.romanov@nginx.com }
7781592Smax.romanov@nginx.com 
7791592Smax.romanov@nginx.com 
7801592Smax.romanov@nginx.com void
nxt_python_done_strings(nxt_python_string_t * pstr)7811592Smax.romanov@nginx.com nxt_python_done_strings(nxt_python_string_t *pstr)
7821592Smax.romanov@nginx.com {
7831592Smax.romanov@nginx.com     PyObject  *obj;
7841592Smax.romanov@nginx.com 
7851592Smax.romanov@nginx.com     while (pstr->string.start != NULL) {
7861592Smax.romanov@nginx.com         obj = *pstr->object_p;
7871592Smax.romanov@nginx.com 
7881592Smax.romanov@nginx.com         Py_XDECREF(obj);
7891592Smax.romanov@nginx.com         *pstr->object_p = NULL;
7901592Smax.romanov@nginx.com 
7911592Smax.romanov@nginx.com         pstr++;
7921592Smax.romanov@nginx.com     }
7931592Smax.romanov@nginx.com }
7941592Smax.romanov@nginx.com 
7951592Smax.romanov@nginx.com 
7961592Smax.romanov@nginx.com static void
nxt_python_atexit(void)7971592Smax.romanov@nginx.com nxt_python_atexit(void)
7981592Smax.romanov@nginx.com {
799*2273Sjeff.iadarola@gmail.com     nxt_int_t            i;
800*2273Sjeff.iadarola@gmail.com     nxt_python_target_t  *target;
8011872So.canty@f5.com 
8021681Smax.romanov@nginx.com     if (nxt_py_proto.done != NULL) {
8031681Smax.romanov@nginx.com         nxt_py_proto.done();
8041681Smax.romanov@nginx.com     }
8051592Smax.romanov@nginx.com 
8061592Smax.romanov@nginx.com     Py_XDECREF(nxt_py_stderr_flush);
8071872So.canty@f5.com 
8081872So.canty@f5.com     if (nxt_py_targets != NULL) {
8091872So.canty@f5.com         for (i = 0; i < nxt_py_targets->count; i++) {
810*2273Sjeff.iadarola@gmail.com             target = &nxt_py_targets->target[i];
811*2273Sjeff.iadarola@gmail.com 
812*2273Sjeff.iadarola@gmail.com             Py_XDECREF(target->application);
813*2273Sjeff.iadarola@gmail.com             Py_XDECREF(target->py_prefix);
814*2273Sjeff.iadarola@gmail.com 
815*2273Sjeff.iadarola@gmail.com             nxt_free(target->prefix.start);
8161872So.canty@f5.com         }
8171872So.canty@f5.com 
8181872So.canty@f5.com         nxt_unit_free(NULL, nxt_py_targets);
8191872So.canty@f5.com     }
8201592Smax.romanov@nginx.com 
8211592Smax.romanov@nginx.com     Py_Finalize();
8221592Smax.romanov@nginx.com 
8231592Smax.romanov@nginx.com     if (nxt_py_home != NULL) {
8241592Smax.romanov@nginx.com         nxt_free(nxt_py_home);
8251592Smax.romanov@nginx.com     }
8261592Smax.romanov@nginx.com }
8271592Smax.romanov@nginx.com 
8281592Smax.romanov@nginx.com 
8291592Smax.romanov@nginx.com void
nxt_python_print_exception(void)8301592Smax.romanov@nginx.com nxt_python_print_exception(void)
8311592Smax.romanov@nginx.com {
8321592Smax.romanov@nginx.com     PyErr_Print();
8331592Smax.romanov@nginx.com 
8341592Smax.romanov@nginx.com #if PY_MAJOR_VERSION == 3
8351592Smax.romanov@nginx.com     /* The backtrace may be buffered in sys.stderr file object. */
8361592Smax.romanov@nginx.com     {
8371592Smax.romanov@nginx.com         PyObject  *result;
8381592Smax.romanov@nginx.com 
8391592Smax.romanov@nginx.com         result = PyObject_CallFunction(nxt_py_stderr_flush, NULL);
8401592Smax.romanov@nginx.com         if (nxt_slow_path(result == NULL)) {
8411592Smax.romanov@nginx.com             PyErr_Clear();
8421592Smax.romanov@nginx.com             return;
8431592Smax.romanov@nginx.com         }
8441592Smax.romanov@nginx.com 
8451592Smax.romanov@nginx.com         Py_DECREF(result);
8461592Smax.romanov@nginx.com     }
8471592Smax.romanov@nginx.com #endif
8481592Smax.romanov@nginx.com }
849