Python: using default event_loop for main thread for ASGI.
Unit's ASGI implementation creates a new event loop to run an application for each thread since 542b5b8c0647. This may cause unexpected exceptions or strange bugs if asyncio synchronisation primitives are initialised before the application starts (e.g. globally). Although the approach with a new event loop for the main thread is consistent and helps to prepare the application to run in multiple threads, it can be a source of pain for people who just want to run single-threaded ASGI applications in Unit. This is related to #560 issue on GitHub.
This commit is contained in:
@@ -264,7 +264,7 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data);
|
rc = nxt_py_proto.ctx_data_alloc(&python_init.ctx_data, 1);
|
||||||
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
|
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -504,7 +504,7 @@ nxt_python_init_threads(nxt_python_app_conf_t *c)
|
|||||||
for (i = 0; i < c->threads - 1; i++) {
|
for (i = 0; i < c->threads - 1; i++) {
|
||||||
ti = &nxt_py_threads[i];
|
ti = &nxt_py_threads[i];
|
||||||
|
|
||||||
res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data);
|
res = nxt_py_proto.ctx_data_alloc(&ti->ctx_data, 0);
|
||||||
if (nxt_slow_path(res != NXT_UNIT_OK)) {
|
if (nxt_slow_path(res != NXT_UNIT_OK)) {
|
||||||
return NXT_UNIT_ERROR;
|
return NXT_UNIT_ERROR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ typedef struct {
|
|||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int (*ctx_data_alloc)(void **pdata);
|
int (*ctx_data_alloc)(void **pdata, int main);
|
||||||
void (*ctx_data_free)(void *data);
|
void (*ctx_data_free)(void *data);
|
||||||
int (*startup)(void *data);
|
int (*startup)(void *data);
|
||||||
int (*run)(nxt_unit_ctx_t *ctx);
|
int (*run)(nxt_unit_ctx_t *ctx);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
|
|
||||||
static PyObject *nxt_python_asgi_get_func(PyObject *obj);
|
static PyObject *nxt_python_asgi_get_func(PyObject *obj);
|
||||||
static int nxt_python_asgi_ctx_data_alloc(void **pdata);
|
static int nxt_python_asgi_ctx_data_alloc(void **pdata, int main);
|
||||||
static void nxt_python_asgi_ctx_data_free(void *data);
|
static void nxt_python_asgi_ctx_data_free(void *data);
|
||||||
static int nxt_python_asgi_startup(void *data);
|
static int nxt_python_asgi_startup(void *data);
|
||||||
static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
|
static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
|
||||||
@@ -194,10 +194,11 @@ nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto)
|
|||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nxt_python_asgi_ctx_data_alloc(void **pdata)
|
nxt_python_asgi_ctx_data_alloc(void **pdata, int main)
|
||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
PyObject *asyncio, *loop, *new_event_loop, *obj;
|
PyObject *asyncio, *loop, *event_loop, *obj;
|
||||||
|
const char *event_loop_func;
|
||||||
nxt_py_asgi_ctx_data_t *ctx_data;
|
nxt_py_asgi_ctx_data_t *ctx_data;
|
||||||
|
|
||||||
ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t));
|
ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t));
|
||||||
@@ -232,23 +233,28 @@ nxt_python_asgi_ctx_data_alloc(void **pdata)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio),
|
event_loop_func = main ? "get_event_loop" : "new_event_loop";
|
||||||
"new_event_loop");
|
|
||||||
if (nxt_slow_path(new_event_loop == NULL)) {
|
event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio),
|
||||||
|
event_loop_func);
|
||||||
|
if (nxt_slow_path(event_loop == NULL)) {
|
||||||
nxt_unit_alert(NULL,
|
nxt_unit_alert(NULL,
|
||||||
"Python failed to get 'new_event_loop' from module 'asyncio'");
|
"Python failed to get '%s' from module 'asyncio'",
|
||||||
|
event_loop_func);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nxt_slow_path(PyCallable_Check(new_event_loop) == 0)) {
|
if (nxt_slow_path(PyCallable_Check(event_loop) == 0)) {
|
||||||
nxt_unit_alert(NULL,
|
nxt_unit_alert(NULL,
|
||||||
"'asyncio.new_event_loop' is not a callable object");
|
"'asyncio.%s' is not a callable object",
|
||||||
|
event_loop_func);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
loop = PyObject_CallObject(new_event_loop, NULL);
|
loop = PyObject_CallObject(event_loop, NULL);
|
||||||
if (nxt_slow_path(loop == NULL)) {
|
if (nxt_slow_path(loop == NULL)) {
|
||||||
nxt_unit_alert(NULL, "Python failed to call 'asyncio.new_event_loop'");
|
nxt_unit_alert(NULL, "Python failed to call 'asyncio.%s'",
|
||||||
|
event_loop_func);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ typedef struct {
|
|||||||
} nxt_python_ctx_t;
|
} nxt_python_ctx_t;
|
||||||
|
|
||||||
|
|
||||||
static int nxt_python_wsgi_ctx_data_alloc(void **pdata);
|
static int nxt_python_wsgi_ctx_data_alloc(void **pdata, int main);
|
||||||
static void nxt_python_wsgi_ctx_data_free(void *data);
|
static void nxt_python_wsgi_ctx_data_free(void *data);
|
||||||
static int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx);
|
static int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx);
|
||||||
static void nxt_python_wsgi_done(void);
|
static void nxt_python_wsgi_done(void);
|
||||||
@@ -210,7 +210,7 @@ fail:
|
|||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nxt_python_wsgi_ctx_data_alloc(void **pdata)
|
nxt_python_wsgi_ctx_data_alloc(void **pdata, int main)
|
||||||
{
|
{
|
||||||
nxt_python_ctx_t *pctx;
|
nxt_python_ctx_t *pctx;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user