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