diff --git a/src/mux_h2.c b/src/mux_h2.c index 3883e15f6..886a26982 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -7261,9 +7261,19 @@ static int h2_takeover(struct connection *conn, int orig_tid) { struct h2c *h2c = 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 @@ -7273,44 +7283,49 @@ static int h2_takeover(struct connection *conn, int orig_tid) */ conn->flags |= CO_FL_ERROR; tasklet_wakeup_on(h2c->wait_event.tasklet, orig_tid); - return -1; + goto fail; } if (h2c->wait_event.events) h2c->conn->xprt->unsubscribe(h2c->conn, h2c->conn->xprt_ctx, h2c->wait_event.events, &h2c->wait_event); + + task = h2c->task; + if (task) { + /* only assign a task if there was already one, otherwise + * the preallocated new task will be released. + */ + task->context = NULL; + h2c->task = NULL; + __ha_barrier_store(); + task_kill(task); + + h2c->task = new_task; + new_task = NULL; + h2c->task->process = h2_timeout_task; + h2c->task->context = h2c; + } + /* To let the tasklet know it should free itself, and do nothing else, * set its context to NULL. */ h2c->wait_event.tasklet->context = NULL; tasklet_wakeup_on(h2c->wait_event.tasklet, orig_tid); - task = h2c->task; - if (task) { - task->context = NULL; - h2c->task = NULL; - __ha_barrier_store(); - task_kill(task); - - h2c->task = task_new_here(); - if (!h2c->task) { - h2_release(h2c); - return -1; - } - h2c->task->process = h2_timeout_task; - h2c->task->context = h2c; - } - h2c->wait_event.tasklet = tasklet_new(); - if (!h2c->wait_event.tasklet) { - h2_release(h2c); - return -1; - } + h2c->wait_event.tasklet = new_tasklet; h2c->wait_event.tasklet->process = h2_io_cb; h2c->wait_event.tasklet->context = h2c; h2c->conn->xprt->subscribe(h2c->conn, h2c->conn->xprt_ctx, SUB_RETRY_RECV, &h2c->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; } /*******************************************************/