diff --git a/src/mux_h1.c b/src/mux_h1.c index 8980fa11c..9f16e56ba 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -4998,9 +4998,19 @@ static int h1_takeover(struct connection *conn, int orig_tid) { struct h1c *h1c = conn->ctx; struct task *task; + struct task *new_task; + struct tasklet *new_tasklet; + + /* Pre-allocate tasks so that we don't have to roll back after the xprt + * has been migrated. + */ + new_task = task_new_here(); + new_tasklet = tasklet_new(); + if (!new_task || !new_tasklet) + goto fail; if (fd_takeover(conn->handle.fd, conn) != 0) - return -1; + goto fail; if (conn->xprt->takeover && conn->xprt->takeover(conn, conn->xprt_ctx, orig_tid) != 0) { /* We failed to takeover the xprt, even if the connection may @@ -5010,44 +5020,49 @@ static int h1_takeover(struct connection *conn, int orig_tid) */ conn->flags |= CO_FL_ERROR; tasklet_wakeup_on(h1c->wait_event.tasklet, orig_tid); - return -1; + goto fail; } if (h1c->wait_event.events) h1c->conn->xprt->unsubscribe(h1c->conn, h1c->conn->xprt_ctx, h1c->wait_event.events, &h1c->wait_event); + + task = h1c->task; + if (task) { + /* only assign a task if there was already one, otherwise + * the preallocated new task will be released. + */ + task->context = NULL; + h1c->task = NULL; + __ha_barrier_store(); + task_kill(task); + + h1c->task = new_task; + new_task = NULL; + h1c->task->process = h1_timeout_task; + h1c->task->context = h1c; + } + /* To let the tasklet know it should free itself, and do nothing else, * set its context to NULL. */ h1c->wait_event.tasklet->context = NULL; tasklet_wakeup_on(h1c->wait_event.tasklet, orig_tid); - task = h1c->task; - if (task) { - task->context = NULL; - h1c->task = NULL; - __ha_barrier_store(); - task_kill(task); - - h1c->task = task_new_here(); - if (!h1c->task) { - h1_release(h1c); - return -1; - } - h1c->task->process = h1_timeout_task; - h1c->task->context = h1c; - } - h1c->wait_event.tasklet = tasklet_new(); - if (!h1c->wait_event.tasklet) { - h1_release(h1c); - return -1; - } + h1c->wait_event.tasklet = new_tasklet; h1c->wait_event.tasklet->process = h1_io_cb; h1c->wait_event.tasklet->context = h1c; h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event); + if (new_task) + __task_free(new_task); return 0; + fail: + if (new_task) + __task_free(new_task); + tasklet_free(new_tasklet); + return -1; }