]>
Commit | Line | Data |
---|---|---|
0a7de745 A |
1 | #include <stdlib.h> |
2 | #include <unistd.h> | |
3 | #include <sys/mman.h> | |
4 | #include <fcntl.h> | |
5 | #include <sys/types.h> | |
6 | #include <sys/sysctl.h> | |
7 | #include <stdatomic.h> | |
8 | #include <TargetConditionals.h> | |
9 | ||
10 | #include <darwintest.h> | |
11 | ||
cb323159 A |
12 | T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true)); |
13 | ||
0a7de745 A |
14 | static int nthreads = 0; |
15 | static int fd; | |
16 | static _Atomic int phase = 0; | |
17 | static _Atomic int pass_count = 0; | |
18 | static _Atomic int fail_count = 0; | |
19 | ||
20 | static void * | |
21 | worker_thread_func(__unused void *arg) | |
22 | { | |
23 | int myfd; | |
24 | int error; | |
25 | ||
26 | /* test racing shm_open */ | |
27 | while (atomic_load(&phase) == 0) { | |
28 | ; | |
29 | } | |
30 | myfd = shm_open("abcd", O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); | |
31 | if (myfd == -1) { | |
32 | T_QUIET; T_EXPECT_EQ(errno, EEXIST, "Expected EEXIST"); | |
33 | atomic_fetch_add(&fail_count, 1); | |
34 | } else { | |
35 | fd = myfd; | |
36 | atomic_fetch_add(&pass_count, 1); | |
37 | } | |
38 | ||
39 | /* test racing ftruncate */ | |
40 | while (atomic_load(&phase) == 1) { | |
41 | ; | |
42 | } | |
43 | error = ftruncate(fd, 8 * 1024); | |
44 | if (error == -1) { | |
45 | T_QUIET; T_EXPECT_EQ(errno, EINVAL, "Expected EINVAL"); | |
46 | atomic_fetch_add(&fail_count, 1); | |
47 | } else { | |
48 | atomic_fetch_add(&pass_count, 1); | |
49 | } | |
50 | ||
51 | /* test racing close */ | |
52 | while (atomic_load(&phase) == 2) { | |
53 | ; | |
54 | } | |
55 | error = close(fd); | |
56 | if (error == -1) { | |
57 | T_QUIET; T_EXPECT_EQ(errno, EBADF, "Expected EBADF"); | |
58 | atomic_fetch_add(&fail_count, 1); | |
59 | } else { | |
60 | atomic_fetch_add(&pass_count, 1); | |
61 | } | |
62 | ||
63 | /* test racing shm_unlink() */ | |
64 | while (atomic_load(&phase) == 3) { | |
65 | ; | |
66 | } | |
67 | error = shm_unlink("abcd"); | |
68 | if (error == -1) { | |
69 | T_QUIET; T_EXPECT_EQ(errno, ENOENT, "Expected ENOENT"); | |
70 | atomic_fetch_add(&fail_count, 1); | |
71 | } else { | |
72 | atomic_fetch_add(&pass_count, 1); | |
73 | } | |
74 | return NULL; | |
75 | } | |
76 | ||
77 | static void | |
78 | create_threads(void) | |
79 | { | |
80 | int ret; | |
81 | int ncpu; | |
82 | size_t ncpu_size = sizeof(ncpu); | |
83 | int i; | |
84 | pthread_attr_t attr; | |
85 | ||
86 | ret = sysctlbyname("hw.ncpu", &ncpu, &ncpu_size, NULL, 0); | |
87 | T_ASSERT_POSIX_SUCCESS(ret, "sysctlbyname(hw.ncpu)"); | |
88 | ||
89 | T_QUIET; T_LOG("%s: Detected %d CPUs\n", __FUNCTION__, ncpu); | |
90 | ||
91 | nthreads = ncpu; | |
92 | T_QUIET; T_LOG("%s: Will create %d threads\n", __FUNCTION__, nthreads); | |
93 | ||
94 | ret = pthread_attr_init(&attr); | |
95 | T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "pthread_attr_init"); | |
96 | ||
97 | for (i = 0; i < nthreads; i++) { | |
98 | pthread_t thread; | |
99 | ret = pthread_create(&thread, &attr, worker_thread_func, NULL); | |
100 | T_QUIET; T_ASSERT_POSIX_ZERO(ret, "pthread_create"); | |
101 | } | |
102 | } | |
103 | ||
104 | ||
105 | T_DECL(testposixshm, "Posix Shared Memory tests") | |
106 | { | |
107 | int fd1; | |
108 | int fd2; | |
109 | int *addr; | |
110 | char *noname = ""; | |
111 | char *toolong = "12345678901234567890123456789012"; | |
112 | char *maxname = "1234567890123456789012345678901"; | |
113 | ||
114 | /* must have O_CREAT */ | |
115 | fd1 = shm_open(maxname, O_RDWR, S_IRUSR | S_IWUSR); | |
116 | T_EXPECT_EQ(fd1, -1, "shm_open() missing O_CREAT"); | |
117 | T_WITH_ERRNO; | |
118 | T_EXPECT_EQ(errno, ENOENT, "Expected ENOENT"); | |
119 | ||
120 | /* name too long */ | |
121 | fd1 = shm_open(toolong, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); | |
122 | T_EXPECT_EQ(fd1, -1, "shm_open() name too long"); | |
123 | T_WITH_ERRNO; | |
124 | T_EXPECT_EQ(errno, ENAMETOOLONG, "Expected ENAMETOOLONG"); | |
125 | ||
126 | /* invalid name */ | |
127 | fd1 = shm_open(noname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); | |
128 | T_EXPECT_EQ(fd1, -1, "shm_open() invalid name"); | |
129 | T_WITH_ERRNO; | |
130 | T_EXPECT_EQ(errno, EINVAL, "Expected EINVAL"); | |
131 | ||
132 | /* valid open */ | |
133 | fd1 = shm_open(maxname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); | |
134 | T_EXPECT_POSIX_SUCCESS(fd1, "valid shm_open() result"); | |
135 | ||
136 | /* O_CREAT, but not O_EXCL should work */ | |
137 | fd2 = shm_open(maxname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); | |
138 | T_EXPECT_POSIX_SUCCESS(fd2, "shm_open() no O_EXCL"); | |
139 | ||
140 | /* close should work */ | |
141 | T_EXPECT_POSIX_ZERO(close(fd2), "close()"); | |
142 | ||
143 | /* O_CREAT | O_EXCL should fail */ | |
144 | fd2 = shm_open(maxname, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); | |
145 | T_WITH_ERRNO; | |
146 | T_EXPECT_EQ(fd2, -1, "shm_open() existing but O_EXCL"); | |
147 | T_EXPECT_EQ(errno, EEXIST, "Expected EEXIST"); | |
148 | ||
149 | /* use ftruncate to create the memory */ | |
150 | T_EXPECT_POSIX_ZERO(ftruncate(fd1, 16 * 1024), NULL); | |
151 | ||
152 | /* a second ftruncate should fail */ | |
153 | T_WITH_ERRNO; | |
154 | T_EXPECT_EQ(ftruncate(fd1, 8 * 1024), -1, "second ftruncate() should fail"); | |
155 | T_EXPECT_EQ(errno, EINVAL, "Expected EINVAL"); | |
156 | ||
157 | /* Map the memory object */ | |
158 | addr = mmap(0, 4 * 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 0); | |
159 | T_WITH_ERRNO; | |
160 | T_EXPECT_NE((void *)addr, MAP_FAILED, "mmap() should work"); | |
161 | ||
162 | /* close should work */ | |
163 | T_EXPECT_POSIX_ZERO(close(fd1), "close()"); | |
164 | ||
165 | /* unlink should work */ | |
166 | T_EXPECT_POSIX_SUCCESS(shm_unlink(maxname), "shm_unlink()"); | |
167 | ||
168 | /* shm_open() after unlink/close should fail */ | |
169 | fd2 = shm_open(maxname, O_RDWR, S_IRUSR | S_IWUSR); | |
170 | T_WITH_ERRNO; | |
171 | T_EXPECT_EQ(fd2, -1, "shm_open() but removed"); | |
172 | T_EXPECT_EQ(errno, ENOENT, "Expected ENOENT"); | |
173 | ||
174 | /* | |
175 | * second phase of tests, try to create race conditions for | |
176 | * shm_open() - multiple threads do shm_open(, ... O_EXCL, ...) | |
177 | * ftruncate() - multiple threads, only 1 should succeed. | |
178 | * fclose() - multiple threads, only 1 should succeed. | |
179 | * shm_unlink() - multiple threads, only 1 should succeed. | |
180 | */ | |
181 | create_threads(); | |
182 | sleep(1); | |
183 | T_LOG("Race testing shm_open"); | |
184 | atomic_fetch_add(&phase, 1); | |
185 | while (pass_count + fail_count < nthreads) { | |
186 | sleep(1); | |
187 | } | |
188 | T_EXPECT_EQ(pass_count, 1, "racing shm_open()"); | |
189 | T_EXPECT_EQ(fail_count, nthreads - 1, "racing shm_open()"); | |
190 | ||
191 | atomic_store(&pass_count, 0); | |
192 | atomic_store(&fail_count, 0); | |
193 | T_LOG("Race testing ftruncate\n"); | |
194 | atomic_fetch_add(&phase, 1); | |
195 | while (pass_count + fail_count < nthreads) { | |
196 | sleep(1); | |
197 | } | |
198 | T_EXPECT_EQ(pass_count, 1, "racing ftruncate()"); | |
199 | T_EXPECT_EQ(fail_count, nthreads - 1, "racing ftruncate()"); | |
200 | ||
201 | atomic_store(&pass_count, 0); | |
202 | atomic_store(&fail_count, 0); | |
203 | T_LOG("Race testing fclose\n"); | |
204 | atomic_fetch_add(&phase, 1); | |
205 | while (pass_count + fail_count < nthreads) { | |
206 | sleep(1); | |
207 | } | |
208 | T_EXPECT_EQ(pass_count, 1, "racing fclose()"); | |
209 | T_EXPECT_EQ(fail_count, nthreads - 1, "racing fclose()"); | |
210 | ||
211 | atomic_store(&pass_count, 0); | |
212 | atomic_store(&fail_count, 0); | |
213 | T_LOG("Race testing shm_unlink\n"); | |
214 | atomic_fetch_add(&phase, 1); | |
215 | while (pass_count + fail_count < nthreads) { | |
216 | sleep(1); | |
217 | } | |
218 | T_EXPECT_EQ(pass_count, 1, "racing shm_unlink()"); | |
219 | T_EXPECT_EQ(fail_count, nthreads - 1, "racing shm_unlink()"); | |
220 | } |