{
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
*/
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);
- /* 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) {
+ /* 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 = task_new_here();
- if (!h1c->task) {
- h1_release(h1c);
- return -1;
- }
+ h1c->task = new_task;
+ new_task = NULL;
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;
- }
+
+ /* 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);
+
+ 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;
}