diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c index 7008a7249..102a4f0d9 100644 --- a/src/mux_fcgi.c +++ b/src/mux_fcgi.c @@ -4173,6 +4173,14 @@ static int fcgi_takeover(struct connection *conn, int orig_tid, int release) * has been migrated. */ if (!release) { + /* If the connection is attached to a buffer_wait (extremely + * rare), it will be woken up at any instant by its own thread + * and we can't undo it anyway, so let's give up on this one. + * It's not interesting anyway since it's not usable right now. + */ + if (LIST_INLIST(&fcgi->buf_wait.list)) + goto fail; + new_task = task_new_here(); new_tasklet = tasklet_new(); if (!new_task || !new_tasklet) @@ -4229,6 +4237,20 @@ static int fcgi_takeover(struct connection *conn, int orig_tid, int release) SUB_RETRY_RECV, &fcgi->wait_event); } + if (release) { + /* we're being called for a server deletion and are running + * under thread isolation. That's the only way we can + * unregister a possible subscription of the original + * connection from its owner thread's queue, as this involves + * manipulating thread-unsafe areas. Note that it is not + * possible to just call b_dequeue() here as it would update + * the current thread's bufq_map and not the original one. + */ + BUG_ON(!thread_isolated()); + if (LIST_INLIST(&fcgi->buf_wait.list)) + _b_dequeue(&fcgi->buf_wait, orig_tid); + } + if (new_task) __task_free(new_task); return 0; diff --git a/src/mux_h1.c b/src/mux_h1.c index d532c1708..a817b8657 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -5264,6 +5264,14 @@ static int h1_takeover(struct connection *conn, int orig_tid, int release) * has been migrated. */ if (!release) { + /* If the connection is attached to a buffer_wait (extremely + * rare), it will be woken up at any instant by its own thread + * and we can't undo it anyway, so let's give up on this one. + * It's not interesting anyway since it's not usable right now. + */ + if (LIST_INLIST(&h1c->buf_wait.list)) + goto fail; + new_task = task_new_here(); new_tasklet = tasklet_new(); if (!new_task || !new_tasklet) @@ -5320,6 +5328,20 @@ static int h1_takeover(struct connection *conn, int orig_tid, int release) SUB_RETRY_RECV, &h1c->wait_event); } + if (release) { + /* we're being called for a server deletion and are running + * under thread isolation. That's the only way we can + * unregister a possible subscription of the original + * connection from its owner thread's queue, as this involves + * manipulating thread-unsafe areas. Note that it is not + * possible to just call b_dequeue() here as it would update + * the current thread's bufq_map and not the original one. + */ + BUG_ON(!thread_isolated()); + if (LIST_INLIST(&h1c->buf_wait.list)) + _b_dequeue(&h1c->buf_wait, orig_tid); + } + if (new_task) __task_free(new_task); return 0; diff --git a/src/mux_h2.c b/src/mux_h2.c index 82ed4cb14..c28c5e1a8 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -7539,6 +7539,14 @@ static int h2_takeover(struct connection *conn, int orig_tid, int release) * has been migrated. */ if (!release) { + /* If the connection is attached to a buffer_wait (extremely + * rare), it will be woken up at any instant by its own thread + * and we can't undo it anyway, so let's give up on this one. + * It's not interesting anyway since it's not usable right now. + */ + if (LIST_INLIST(&h2c->buf_wait.list)) + goto fail; + new_task = task_new_here(); new_tasklet = tasklet_new(); if (!new_task || !new_tasklet) @@ -7595,6 +7603,20 @@ static int h2_takeover(struct connection *conn, int orig_tid, int release) SUB_RETRY_RECV, &h2c->wait_event); } + if (release) { + /* we're being called for a server deletion and are running + * under thread isolation. That's the only way we can + * unregister a possible subscription of the original + * connection from its owner thread's queue, as this involves + * manipulating thread-unsafe areas. Note that it is not + * possible to just call b_dequeue() here as it would update + * the current thread's bufq_map and not the original one. + */ + BUG_ON(!thread_isolated()); + if (LIST_INLIST(&h2c->buf_wait.list)) + _b_dequeue(&h2c->buf_wait, orig_tid); + } + if (new_task) __task_free(new_task); return 0;