Commit 83cb4e0bc3 for openssl.org

commit 83cb4e0bc3989a5d4252ddb8ebf0d5736d3a44de
Author: Bob Beck <beck@openssl.org>
Date:   Sun Aug 16 10:40:59 2026 -0700

    Size the multithreaded lhash test to the coverage it needs

    The iteration and worker counts were not derived from what the test has
    to cover.  What matters is each ordered pair of behaviours being
    exercised concurrently on the same key, and the chance of missing one
    falls off exponentially with the iteration count, so 150000 iterations
    covers that with a wide margin.  The derivation is documented at the
    constant.

    Cap the workers at four rather than taking a quarter of HARNESS_JOBS.
    Four is enough to interleave more than a pair of threads on a key, which
    two cannot do, without tying up a machine.

    02-test_lhash was the longest recipe in the suite and set the floor for
    the whole run.  On a MacBook Pro with HARNESS_JOBS=14 it goes from 111
    seconds to 5.4 seconds.

    Reviewed-by: Andrew Dinh <andrewd@openssl.org>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    Merge-date: Tue Sep  1 14:17:17 2026
    Merged-from: https://github.com/openssl/openssl/pull/32404

diff --git a/test/lhash_test.c b/test/lhash_test.c
index 394321151f..bf45226d90 100644
--- a/test/lhash_test.c
+++ b/test/lhash_test.c
@@ -576,24 +576,61 @@ typedef struct test_mt_entry {

 static HT *m_ht = NULL;
 #define TEST_MT_POOL_SZ 256
-#define TEST_THREAD_ITERATIONS 1000000
-#define NUM_WORKERS 16
+/*-
+ * TEST_THREAD_ITERATIONS is chosen so that collisions between workers
+ * on the same key are exercised, not to make the test run for any
+ * particular length of time.
+ *
+ * Choosing too large a number consumes a multithreaded machine with the
+ * kernel madly exercising lock contention, for no real gain in coverage
+ * for our code, making the tests run excessively long.  A million
+ * iterations is really painful.
+ *
+ * A race needs two workers on one key at once, so what must be covered is
+ * each ordered pair of behaviours -- NUM_BEHAVIORS squared, or 16 --
+ * arriving together on a key.  With W workers an operation collides with
+ * probability P = 1 - ((TEST_MT_POOL_SZ - 1) / TEST_MT_POOL_SZ) ^ (W - 1),
+ * and a collision is equally likely to be any of the 16, so each is seen
+ * an expected E = W * TEST_THREAD_ITERATIONS * P / 16 times per
+ * configuration.  The chance of never seeing one is about 16 * exp(-E):
+ *
+ *     workers        E    P(some combination never exercised)
+ *           2       73    1e-31
+ *           4      438    1e-189
+ *           8     2027    1e-879
+ *          16     8553    1e-3713
+ *
+ * Coverage rises with the square of the worker count but run time only
+ * linearly, so the value is sized for the fewest workers worth supporting.
+ * At two workers 10000 iterations would miss a combination one run in
+ * nine, and 20000 one run in a thousand; the value below is far enough
+ * past that to leave each combination met at many different points in the
+ * interleaving.  More buys only further chances of landing in a narrow
+ * timing window, not more of the state space.
+ */
+#define TEST_THREAD_ITERATIONS 150000
+/*-
+ * Four workers is enough to interleave more than a pair of threads on a
+ * key, which two cannot do, without tying up a whole machine.
+ */
+#define MAX_NUM_WORKERS 4

 static struct test_mt_entry test_mt_entries[TEST_MT_POOL_SZ];
 static char **worker_exits;
 static thread_t *workers;
-static int num_workers = NUM_WORKERS;
+static int num_workers = MAX_NUM_WORKERS;

 static int setup_num_workers(void)
 {
     char *harness_jobs = getenv("HARNESS_JOBS");
     char *lhash_workers = getenv("LHASH_WORKERS");
-    /* If we have HARNESS_JOBS set, don't eat more than a quarter */
+    /* If we have HARNESS_JOBS set, don't use more workers than that */
     if (harness_jobs != NULL) {
         int jobs;

-        if (test_strtoint(harness_jobs, &jobs) && jobs > 0)
-            num_workers = jobs / 4;
+        if (test_strtoint(harness_jobs, &jobs) && jobs > 0
+            && jobs < num_workers)
+            num_workers = jobs;
     }
     /* But if we have explicitly set LHASH_WORKERS use that */
     if (lhash_workers != NULL) {