xref: /xnu-8020.140.41/tests/counter/counter.c (revision 27b03b360a988dfd3dfdf34262bb0042026747cc)
1 #include <stdatomic.h>
2 #include <sys/kern_sysctl.h>
3 
4 #include <darwintest_utils.h>
5 #include <darwintest.h>
6 
7 #include "counter/common.h"
8 #include "test_utils.h"
9 
10 static unsigned int ncpu(void);
11 
12 static uint64_t
sysctl_read(const char * name)13 sysctl_read(const char *name)
14 {
15 	int result;
16 	uint64_t value;
17 	size_t size = sizeof(value);
18 	result = sysctlbyname(name, &value, &size, NULL, 0);
19 	T_QUIET; T_ASSERT_POSIX_SUCCESS(result, "Read from %s", name);
20 	return value;
21 }
22 
23 static void
sysctl_write(const char * name,int64_t amount)24 sysctl_write(const char* name, int64_t amount)
25 {
26 	kern_return_t result;
27 	result = sysctlbyname(name, NULL, NULL, &amount, sizeof(int64_t));
28 	T_QUIET; T_ASSERT_POSIX_SUCCESS(result, "Write to %s", name);
29 }
30 
31 static void
scalable_counter_add(int64_t amount)32 scalable_counter_add(int64_t amount)
33 {
34 	sysctl_write("kern.scalable_counter_test_add", amount);
35 }
36 
37 static void
static_scalable_counter_add(int64_t amount)38 static_scalable_counter_add(int64_t amount)
39 {
40 	sysctl_write("kern.static_scalable_counter_test_add", amount);
41 }
42 
43 static int64_t
scalable_counter_load(void)44 scalable_counter_load(void)
45 {
46 	return (int64_t) sysctl_read("kern.scalable_counter_test_load");
47 }
48 
49 static int64_t
static_scalable_counter_load(void)50 static_scalable_counter_load(void)
51 {
52 	return (int64_t) sysctl_read("kern.static_scalable_counter_test_load");
53 }
54 
55 /*
56  * A background thread that bangs on the percpu counter and then exits.
57  * @param num_iterations How many times to bang on the counter. Each iteration makes the counter
58  * bigger by 100.
59  */
60 static void*
background_scalable_counter_thread(void * num_iterations_ptr)61 background_scalable_counter_thread(void* num_iterations_ptr)
62 {
63 	int64_t i, num_iterations;
64 	num_iterations = (int64_t)(num_iterations_ptr);
65 	for (i = 0; i < num_iterations; i++) {
66 		scalable_counter_add(-25);
67 		scalable_counter_add(75);
68 		scalable_counter_add(-100);
69 		scalable_counter_add(150);
70 	}
71 	atomic_thread_fence(memory_order_release);
72 	return 0;
73 }
74 
75 static
76 void
darwin_test_fini_scalable_counter_test()77 darwin_test_fini_scalable_counter_test()
78 {
79 	int ret = fini_scalable_counter_test();
80 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "fini_scalable_counter_test");
81 }
82 
83 static
84 void
darwin_test_setup(void)85 darwin_test_setup(void)
86 {
87 	T_SETUPBEGIN;
88 	int dev_kernel = is_development_kernel();
89 	T_QUIET; T_ASSERT_POSIX_SUCCESS(dev_kernel, "sysctlbyname kern.development");
90 	if (is_development_kernel() != 1) {
91 		T_SKIP("Skipping test on non development kernel.");
92 	}
93 	init_scalable_counter_test();
94 	T_SETUPEND;
95 	T_ATEND(darwin_test_fini_scalable_counter_test);
96 }
97 
98 T_DECL(test_scalable_counters_single_threaded, "Test single threaded operations on scalable_counters", T_META_ASROOT(true))
99 {
100 	static int64_t kNumIterations = 100, i, expected_value = 0;
101 	darwin_test_setup();
102 	T_QUIET; T_EXPECT_EQ(scalable_counter_load(), 0LL, "Counter starts at zero");
103 
104 	/* Simple add, subtract, and read */
105 	scalable_counter_add(1);
106 	T_QUIET; T_EXPECT_EQ(scalable_counter_load(), 1LL, "0 + 1 == 1");
107 	scalable_counter_add(-1);
108 	T_QUIET; T_EXPECT_EQ(scalable_counter_load(), 0LL, "1 - 1 == 0");
109 	for (i = 0; i < kNumIterations; i++) {
110 		scalable_counter_add(i);
111 		expected_value += i;
112 	}
113 	for (i = 0; i < kNumIterations / 2; i++) {
114 		scalable_counter_add(-i);
115 		expected_value -= i;
116 	}
117 	T_QUIET; T_EXPECT_EQ(scalable_counter_load(), expected_value, "Counter value is correct.");
118 	T_END;
119 }
120 
121 T_DECL(test_static_counter, "Test staticly declared counter", T_META_ASROOT(true))
122 {
123 	static size_t kNumIterations = 100;
124 	int64_t start_value;
125 	darwin_test_setup();
126 	start_value = static_scalable_counter_load();
127 	for (size_t i = 0; i < kNumIterations; i++) {
128 		static_scalable_counter_add(1);
129 	}
130 	T_QUIET; T_EXPECT_EQ(static_scalable_counter_load(), (long long) kNumIterations + start_value, "Counter value is correct");
131 	T_END;
132 }
133 
134 T_DECL(test_scalable_counters_multithreaded, "Test multi-threaded operations on scalable_counters", T_META_ASROOT(true))
135 {
136 	unsigned int kNumThreads = ncpu() * 5;
137 	int ret;
138 	int64_t i;
139 	pthread_attr_t pthread_attr;
140 	pthread_t *threads;
141 
142 	darwin_test_setup();
143 
144 	threads = malloc(sizeof(pthread_t) * kNumThreads);
145 	T_QUIET; T_ASSERT_NOTNULL(threads, "Out of memory");
146 
147 	ret = pthread_attr_init(&pthread_attr);
148 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_attr_init");
149 
150 	int64_t expected_value = 0;
151 	for (i = 0; i < kNumThreads; i++) {
152 		ret = pthread_create(&threads[i], &pthread_attr, background_scalable_counter_thread, (void*)(i));
153 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_create");
154 		expected_value += 100 * i;
155 	}
156 
157 	for (i = 0; i < kNumThreads; i++) {
158 		void *exit_code;
159 		ret = pthread_join(threads[i], &exit_code);
160 		T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_join");
161 		T_QUIET; T_ASSERT_EQ((ptrdiff_t) exit_code, (ptrdiff_t) 0, "Background thread exited sucessfully.");
162 	}
163 	atomic_thread_fence(memory_order_acquire);
164 
165 	T_QUIET; T_EXPECT_EQ(scalable_counter_load(), expected_value, "Counter value is correct.");
166 
167 	ret = pthread_attr_destroy(&pthread_attr);
168 	T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_attr_destroy");
169 	free(threads);
170 }
171 
172 static unsigned int
ncpu()173 ncpu()
174 {
175 	kern_return_t result;
176 	int ncpu;
177 	size_t size = sizeof(ncpu);
178 	result = sysctlbyname("hw.ncpu", &ncpu, &size, NULL, 0);
179 	T_QUIET; T_ASSERT_MACH_SUCCESS(result, "hw.npu");
180 	return (unsigned int) ncpu;
181 }
182