]> git.saurik.com Git - apple/xnu.git/blob - tests/counter/counter.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / tests / counter / counter.c
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
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
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
32 scalable_counter_add(int64_t amount)
33 {
34 sysctl_write("kern.scalable_counter_test_add", amount);
35 }
36
37 static void
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
44 scalable_counter_load(void)
45 {
46 return (int64_t) sysctl_read("kern.scalable_counter_test_load");
47 }
48
49 static int64_t
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*
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
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
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
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 }