MINOR: fd: implement fd_migrate_on() to migrate on a non-local thread
fd_migrate_on() can be used to migrate an existing FD to any thread, even
one belonging to a different group from the current one and from the
caller's. All that is needed is to make sure the FD is still valid when
the operation is performed (which is the case when such operations happen).
This is potentially slightly expensive since it locks the tgid during the
delicate operation, but it is normally performed only from an owning
thread to offer the FD to another one (e.g. reassign a better thread upon
accept()).
diff --git a/include/haproxy/fd.h b/include/haproxy/fd.h
index 682aa94..11212ff 100644
--- a/include/haproxy/fd.h
+++ b/include/haproxy/fd.h
@@ -67,6 +67,9 @@
/* makes the fd close-on-exec; returns -1 on failure. */
int fd_set_cloexec(int fd);
+/* Migrate a FD to a new thread <new_tid>. */
+void fd_migrate_on(int fd, uint new_tid);
+
/*
* Take over a FD belonging to another thread.
* Returns 0 on success, and -1 on failure.
diff --git a/src/fd.c b/src/fd.c
index 706aba8..4c1ad57 100644
--- a/src/fd.c
+++ b/src/fd.c
@@ -462,6 +462,45 @@
return DISGUISE(ret);
}
+/* Migrate a FD to a new thread <new_tid>. It is explicitly permitted to
+ * migrate to another thread group, the function takes the necessary locking
+ * for this. It is even permitted to migrate from a foreign group to another,
+ * but the calling thread must be certain that the FD is not about to close
+ * when doing so, reason why it is highly recommended that only one of the
+ * FD's owners performs this operation. The polling is completely disabled.
+ * The operation never fails.
+ */
+void fd_migrate_on(int fd, uint new_tid)
+{
+ struct thread_info *new_ti = &ha_thread_info[new_tid];
+
+ /* we must be alone to work on this idle FD. If not, it means that its
+ * poller is currently waking up and is about to use it, likely to
+ * close it on shut/error, but maybe also to process any unexpectedly
+ * pending data. It's also possible that the FD was closed and
+ * reassigned to another thread group, so let's be careful.
+ */
+ fd_lock_tgid(fd, new_ti->tgid);
+
+ /* now we have exclusive access to it. From now FD belongs to tid_bit
+ * for this tgid.
+ */
+ HA_ATOMIC_STORE(&fdtab[fd].thread_mask, new_ti->ltid_bit);
+
+ /* Make sure the FD doesn't have the active bit. It is possible that
+ * the fd is polled by the thread that used to own it, the new thread
+ * is supposed to call subscribe() later, to activate polling.
+ */
+ fd_stop_both(fd);
+
+ /* we're done with it. As soon as we unlock it, other threads from the
+ * target group can manipulate it. However it may only disappear once
+ * we drop the reference.
+ */
+ fd_unlock_tgid(fd);
+ fd_drop_tgid(fd);
+}
+
/*
* Take over a FD belonging to another thread.
* unexpected_conn is the expected owner of the fd.