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