MEDIUM: threads: automatically assign threads to groups
This takes care of unassigned threads groups and places unassigned
threads there, in a more or less balanced way. Too sparse allocations
may still fail though. For now with a maximum group number fixed to 1
nothing can really fail.
diff --git a/include/haproxy/thread.h b/include/haproxy/thread.h
index 14c88f0..c70108b 100644
--- a/include/haproxy/thread.h
+++ b/include/haproxy/thread.h
@@ -43,6 +43,7 @@
void ha_tkill(unsigned int thr, int sig);
void ha_tkillall(int sig);
void ha_thread_relax(void);
+int thread_map_to_groups();
extern int thread_cpus_enabled_at_boot;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index fe49811..f8e777e 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2439,6 +2439,11 @@
if (!global.nbtgroups)
global.nbtgroups = 1;
+ if (thread_map_to_groups() < 0) {
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
pool_head_requri = create_pool("requri", global.tune.requri_len , MEM_F_SHARED);
pool_head_capture = create_pool("capture", global.tune.cookie_len, MEM_F_SHARED);
diff --git a/src/thread.c b/src/thread.c
index 7374a8e..2a7d3af 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -1001,6 +1001,82 @@
#endif // USE_THREAD
+/* scans the configured thread mapping and establishes the final one. Returns <0
+ * on failure, >=0 on success.
+ */
+int thread_map_to_groups()
+{
+ int t, g, ut, ug;
+ int q, r;
+
+ ut = ug = 0; // unassigned threads & groups
+
+ for (t = 0; t < global.nbthread; t++) {
+ if (!ha_thread_info[t].tg)
+ ut++;
+ }
+
+ for (g = 0; g < global.nbtgroups; g++) {
+ if (!ha_tgroup_info[g].count)
+ ug++;
+ }
+
+ if (ug > ut) {
+ ha_alert("More unassigned thread-groups (%d) than threads (%d). Please reduce thread-groups\n", ug, ut);
+ return -1;
+ }
+
+ /* look for first unassigned thread */
+ for (t = 0; t < global.nbthread && ha_thread_info[t].tg; t++)
+ ;
+
+ /* assign threads to empty groups */
+ for (g = 0; ug && ut; ) {
+ /* due to sparse thread assignment we can end up with more threads
+ * per group on last assigned groups than former ones, so we must
+ * always try to pack the maximum remaining ones together first.
+ */
+ q = ut / ug;
+ r = ut % ug;
+ if ((q + !!r) > MAX_THREADS_PER_GROUP) {
+ ha_alert("Too many remaining unassigned threads (%d) for thread groups (%d). Please increase thread-groups or make sure to keep thread numbers contiguous\n", ug, ut);
+ return -1;
+ }
+
+ /* thread <t> is the next unassigned one. Let's look for next
+ * unassigned group, we know there are some left
+ */
+ while (ut >= ug && ha_tgroup_info[g].count)
+ g++;
+
+ /* group g is unassigned, try to fill it with consecutive threads */
+ while (ut && ut >= ug && ha_tgroup_info[g].count < q + !!r &&
+ (!ha_tgroup_info[g].count || t == ha_tgroup_info[g].base + ha_tgroup_info[g].count)) {
+
+ if (!ha_tgroup_info[g].count) {
+ /* assign new group */
+ ha_tgroup_info[g].base = t;
+ ug--;
+ }
+
+ ha_tgroup_info[g].count++;
+ ha_thread_info[t].tg = &ha_tgroup_info[g];
+
+ ut--;
+ /* switch to next unassigned thread */
+ while (++t < global.nbthread && ha_thread_info[t].tg)
+ ;
+ }
+ }
+
+ if (ut) {
+ ha_alert("Remaining unassigned threads found (%d) because all groups are in use. Please increase 'thread-groups', reduce 'nbthreads' or remove or extend 'thread-group' enumerations.\n", ut);
+ return -1;
+ }
+
+ return 0;
+}
+
/* Parse the "nbthread" global directive, which takes an integer argument that
* contains the desired number of threads.
*/