]> git.saurik.com Git - apple/xnu.git/blob - tests/testposixshm.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / tests / testposixshm.c
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 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true));
13
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 }