void hap_register_post_check(int (*fct)());
void hap_register_post_deinit(void (*fct)());
+void hap_register_per_thread_alloc(int (*fct)());
void hap_register_per_thread_init(int (*fct)());
void hap_register_per_thread_deinit(void (*fct)());
+void hap_register_per_thread_free(int (*fct)());
void mworker_accept_wrapper(int fd);
void mworker_reload();
#define REGISTER_POST_DEINIT(fct) \
INITCALL1(STG_REGISTER, hap_register_post_deinit, (fct))
+/* simplified way to declare a per-thread allocation callback in a file */
+#define REGISTER_PER_THREAD_ALLOC(fct) \
+ INITCALL1(STG_REGISTER, hap_register_per_thread_alloc, (fct))
+
/* simplified way to declare a per-thread init callback in a file */
#define REGISTER_PER_THREAD_INIT(fct) \
INITCALL1(STG_REGISTER, hap_register_per_thread_init, (fct))
#define REGISTER_PER_THREAD_DEINIT(fct) \
INITCALL1(STG_REGISTER, hap_register_per_thread_deinit, (fct))
+/* simplified way to declare a per-thread free callback in a file */
+#define REGISTER_PER_THREAD_FREE(fct) \
+ INITCALL1(STG_REGISTER, hap_register_per_thread_free, (fct))
+
#endif /* _TYPES_GLOBAL_H */
/*
fd_cant_recv(fd);
}
-/* Initialize the pollers per thread */
+/* allocate the per-thread fd_updt thus needs to be called early after
+ * thread creation.
+ */
+static int alloc_pollers_per_thread()
+{
+ fd_updt = calloc(global.maxsock, sizeof(*fd_updt));
+ return fd_updt != NULL;
+}
+
+/* Initialize the pollers per thread.*/
static int init_pollers_per_thread()
{
int mypipe[2];
- if ((fd_updt = calloc(global.maxsock, sizeof(*fd_updt))) == NULL)
- return 0;
- if (pipe(mypipe) < 0) {
- free(fd_updt);
- fd_updt = NULL;
+
+ if (pipe(mypipe) < 0)
return 0;
- }
+
poller_rd_pipe = mypipe[0];
poller_wr_pipe[tid] = mypipe[1];
fcntl(poller_rd_pipe, F_SETFL, O_NONBLOCK);
/* Deinitialize the pollers per thread */
static void deinit_pollers_per_thread()
{
- free(fd_updt);
- fd_updt = NULL;
-
/* rd and wr are init at the same place, but only rd is init to -1, so
we rely to rd to close. */
if (poller_rd_pipe > -1) {
}
}
+/* Release the pollers per thread, to be called late */
+static void free_pollers_per_thread()
+{
+ free(fd_updt);
+ fd_updt = NULL;
+}
+
/*
* Initialize the pollers till the best one is found.
* If none works, returns 0, otherwise 1.
return 1;
}
+REGISTER_PER_THREAD_ALLOC(alloc_pollers_per_thread);
REGISTER_PER_THREAD_INIT(init_pollers_per_thread);
REGISTER_PER_THREAD_DEINIT(deinit_pollers_per_thread);
+REGISTER_PER_THREAD_FREE(free_pollers_per_thread);
/*
* Local variables:
int (*fct)();
};
-/* These functions are called when freeing the global sections at the end
- * of deinit, after everything is stopped. They don't return anything, and
- * they work in best effort mode as their sole goal is to make valgrind
- * mostly happy.
- */
-struct list post_deinit_list = LIST_HEAD_INIT(post_deinit_list);
-struct post_deinit_fct {
+/* These functions are called for each thread just after the thread creation
+ * and before running the init functions. They should be used to do per-thread
+ * (re-)allocations that are needed by subsequent functoins. They must return 0
+ * if an error occurred. */
+struct list per_thread_alloc_list = LIST_HEAD_INIT(per_thread_alloc_list);
+struct per_thread_alloc_fct {
struct list list;
- void (*fct)();
+ int (*fct)();
};
/* These functions are called for each thread just after the thread creation
int (*fct)();
};
+/* These functions are called when freeing the global sections at the end of
+ * deinit, after everything is stopped. They don't return anything. They should
+ * not release shared resources that are possibly used by other deinit
+ * functions, only close/release what is private. Use the per_thread_free_list
+ * to release shared resources.
+ */
+struct list post_deinit_list = LIST_HEAD_INIT(post_deinit_list);
+struct post_deinit_fct {
+ struct list list;
+ void (*fct)();
+};
+
+/* These functions are called when freeing the global sections at the end of
+ * deinit, after the thread deinit functions, to release unneeded memory
+ * allocations. They don't return anything, and they work in best effort mode
+ * as their sole goal is to make valgrind mostly happy.
+ */
+struct list per_thread_free_list = LIST_HEAD_INIT(per_thread_free_list);
+struct per_thread_free_fct {
+ struct list list;
+ int (*fct)();
+};
+
/* These functions are called for each thread just after the scheduler loop and
* before exiting the thread. They don't return anything and, as for post-deinit
* functions, they work in best effort mode as their sole goal is to make
LIST_ADDQ(&post_deinit_list, &b->list);
}
+/* used to register some allocation functions to call for each thread. */
+void hap_register_per_thread_alloc(int (*fct)())
+{
+ struct per_thread_alloc_fct *b;
+
+ b = calloc(1, sizeof(*b));
+ if (!b) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ b->fct = fct;
+ LIST_ADDQ(&per_thread_alloc_list, &b->list);
+}
+
/* used to register some initialization functions to call for each thread. */
void hap_register_per_thread_init(int (*fct)())
{
LIST_ADDQ(&per_thread_deinit_list, &b->list);
}
+/* used to register some free functions to call for each thread. */
+void hap_register_per_thread_free(int (*fct)())
+{
+ struct per_thread_free_fct *b;
+
+ b = calloc(1, sizeof(*b));
+ if (!b) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ b->fct = fct;
+ LIST_ADDQ(&per_thread_free_list, &b->list);
+}
+
static void display_version()
{
printf("HA-Proxy version %s %s - https://haproxy.org/\n", haproxy_version, haproxy_date);
static void *run_thread_poll_loop(void *data)
{
+ struct per_thread_alloc_fct *ptaf;
struct per_thread_init_fct *ptif;
struct per_thread_deinit_fct *ptdf;
+ struct per_thread_free_fct *ptff;
ha_set_tid((unsigned long)data);
tv_update_date(-1,-1);
+ /* per-thread alloc calls performed here are not allowed to snoop on
+ * other threads, so they are free to initialize at their own rhythm
+ * as long as they act as if they were alone. None of them may rely
+ * on resources initialized by the other ones.
+ */
+ list_for_each_entry(ptaf, &per_thread_alloc_list, list) {
+ if (!ptaf->fct()) {
+ ha_alert("failed to allocate resources for thread %u.\n", tid);
+ exit(1);
+ }
+ }
+
/* per-thread init calls performed here are not allowed to snoop on
* other threads, so they are free to initialize at their own rhythm
* as long as they act as if they were alone.
list_for_each_entry(ptdf, &per_thread_deinit_list, list)
ptdf->fct();
+ list_for_each_entry(ptff, &per_thread_free_list, list)
+ ptff->fct();
+
#ifdef USE_THREAD
_HA_ATOMIC_AND(&all_threads_mask, ~tid_bit);
if (tid > 0)