__task_set_state_and_tid() updates t->state and t->tid at once through
an uint64_t cast, which violates strict aliasing. With threads the
__atomic builtins keep the compiler conservative, but without them the
accesses are plain, and gcc -O2 legitimately keeps using a stale
t->state: the CAS always fails and _task_wakeup() spins forever on the
first request until the watchdog kills the process.
Without threads there is no concurrency to protect against, so just
compare and assign the two fields directly.
}
}
- /* When FD tables are not shared between thread groups, the poller
- * pipes of all threads must be created before any group unshares its
- * FD table, so that they are inherited into every group's table and
- * cross-group wake_thread() keeps working. This must be done for all
- * builds, including those without threads, since each thread will pick
- * its own pair from there.
- */
- if (!fd_precreate_poller_pipes()) {
- ha_alert("Failed to create the poller pipes.\n");
- exit(1);
- }
-
/* start threads 2 and above */
setup_extra_threads(&run_thread_poll_loop);
/* the startup thread will be thread 1 */
ha_pthread[0] = pthread_self();
+ /* When FD tables are not shared between thread groups, the poller
+ * pipes of all threads must be created before any group unshares its
+ * FD table, so that they are inherited into every group's table and
+ * cross-group wake_thread() keeps working.
+ */
+ if (!fd_precreate_poller_pipes()) {
+ ha_alert("Failed to create the poller pipes.\n");
+ exit(1);
+ }
+
/* Create one initial thread for each extra thread group. These will
* each be responsible for creating their own extra threads. The first
* group's initial thread is the current thread.