xref: /freebsd/cddl/lib/libtpool/tests/libtpool_test.c (revision 24e4dcf4ba5e9dedcf89efd358ea3e1fe5867020)
1 #include <sys/stdtypes.h>
2 #include <sys/sysctl.h>
3 #include <errno.h>
4 #include <pthread.h>
5 
6 #include <thread_pool.h>
7 
8 #include <atf-c.h>
9 
10 static void
11 tp_delay(void *arg)
12 {
13 	pthread_barrier_t *barrier = arg;
14 	int r;
15 
16 	/* Block this task until all thread pool workers have been created. */
17 	r = pthread_barrier_wait(barrier);
18 	ATF_REQUIRE_MSG(r == 0 || r == PTHREAD_BARRIER_SERIAL_THREAD,
19 	    "pthread_barrier_wait failed: %s", strerror(r));
20 }
21 
22 /*
23  * NB: we could reduce the test's resource cost by using rctl(4).  But that
24  * isn't enabled by default.  And even with a thread limit of 1500, it takes <
25  * 0.1s to run on my machine.  So I don't think it's worth optimizing for the
26  * case where rctl is available.
27  */
28 ATF_TC(complete_exhaustion);
29 ATF_TC_HEAD(complete_exhaustion, tc)
30 {
31 	atf_tc_set_md_var(tc, "descr",
32 	    "A thread pool should fail to schedule tasks if it is completely impossible to spawn any threads.");
33 }
34 
35 ATF_TC_BODY(complete_exhaustion, tc)
36 {
37 	pthread_barrier_t barrier;
38 	tpool_t *tp0, *tp1;
39 	size_t len;
40 	int max_threads_per_proc = 0;
41 	int nworkers;
42 	int r, i;
43 
44 	len = sizeof(max_threads_per_proc);
45 	r = sysctlbyname("kern.threads.max_threads_per_proc",
46 	    &max_threads_per_proc, &len, NULL, 0);
47 	ATF_REQUIRE_EQ_MSG(r, 0, "sysctlbyname: %s", strerror(errno));
48 	nworkers = max_threads_per_proc - 1;
49 	pthread_barrier_init(&barrier, NULL, max_threads_per_proc);
50 
51 	/*
52 	 * Create the first thread pool and spawn the maximum allowed number of
53 	 * processes.
54 	 */
55 	tp0 = tpool_create(nworkers, nworkers, 1, NULL);
56 	ATF_REQUIRE(tp0 != NULL);
57 	for (i = 0; i < nworkers; i++) {
58 		ATF_REQUIRE_EQ(tpool_dispatch(tp0, tp_delay, &barrier), 0);
59 	}
60 
61 	/*
62 	 * Now create a second thread pool.  Unable to create new threads, the
63 	 * dispatch function should return an error.
64 	 */
65 	tp1 = tpool_create(nworkers, 2 * nworkers, 1, NULL);
66 	ATF_REQUIRE(tp1 != NULL);
67 	ATF_REQUIRE_EQ(tpool_dispatch(tp1, tp_delay, NULL), -1);
68 
69 	/* Cleanup */
70 	r = pthread_barrier_wait(&barrier);
71 	ATF_REQUIRE_MSG(r == 0 || r == PTHREAD_BARRIER_SERIAL_THREAD,
72 	    "pthread_barrier_wait failed: %s", strerror(r));
73 	tpool_wait(tp1);
74 	tpool_wait(tp0);
75 }
76 
77 ATF_TP_ADD_TCS(tp)
78 {
79 	ATF_TP_ADD_TC(tp, complete_exhaustion);
80 
81 	return (atf_no_error());
82 }
83