source: vendor/current/lib/tdb/test/run-marklock-deadlock.c

Last change on this file was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

File size: 7.2 KB
Line 
1#include "../common/tdb_private.h"
2#include "../common/io.c"
3#include "../common/tdb.c"
4#include "../common/lock.c"
5#include "../common/freelist.c"
6#include "../common/traverse.c"
7#include "../common/transaction.c"
8#include "../common/error.c"
9#include "../common/open.c"
10#include "../common/check.c"
11#include "../common/hash.c"
12#include "../common/mutex.c"
13#include "tap-interface.h"
14#include <stdlib.h>
15#include <sys/types.h>
16#include <sys/wait.h>
17#include <stdarg.h>
18#include "logging.h"
19
20static TDB_DATA key, data;
21
22static void do_chainlock(const char *name, int tdb_flags, int up, int down)
23{
24 struct tdb_context *tdb;
25 int ret;
26 ssize_t nread, nwritten;
27 char c = 0;
28
29 tdb = tdb_open_ex(name, 3, tdb_flags,
30 O_RDWR|O_CREAT, 0755, &taplogctx, NULL);
31 ok(tdb, "tdb_open_ex should succeed");
32
33 ret = tdb_chainlock(tdb, key);
34 ok(ret == 0, "tdb_chainlock should succeed");
35
36 nwritten = write(up, &c, sizeof(c));
37 ok(nwritten == sizeof(c), "write should succeed");
38
39 nread = read(down, &c, sizeof(c));
40 ok(nread == sizeof(c), "read should succeed");
41
42 exit(0);
43}
44
45static void do_allrecord_lock(const char *name, int tdb_flags, int up, int down)
46{
47 struct tdb_context *tdb;
48 int ret;
49 ssize_t nread, nwritten;
50 char c = 0;
51
52 tdb = tdb_open_ex(name, 3, tdb_flags,
53 O_RDWR|O_CREAT, 0755, &taplogctx, NULL);
54 ok(tdb, "tdb_open_ex should succeed");
55
56 ret = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false);
57 ok(ret == 0, "tdb_allrecord_lock should succeed");
58
59 nwritten = write(up, &c, sizeof(c));
60 ok(nwritten == sizeof(c), "write should succeed");
61
62 nread = read(down, &c, sizeof(c));
63 ok(nread == sizeof(c), "read should succeed");
64
65 exit(0);
66}
67
68/* The code should barf on TDBs created with rwlocks. */
69static int do_tests(const char *name, int tdb_flags)
70{
71 struct tdb_context *tdb;
72 int ret;
73 pid_t chainlock_child, allrecord_child;
74 int chainlock_down[2];
75 int chainlock_up[2];
76 int allrecord_down[2];
77 int allrecord_up[2];
78 char c;
79 ssize_t nread, nwritten;
80
81 key.dsize = strlen("hi");
82 key.dptr = discard_const_p(uint8_t, "hi");
83 data.dsize = strlen("world");
84 data.dptr = discard_const_p(uint8_t, "world");
85
86 ret = pipe(chainlock_down);
87 ok(ret == 0, "pipe should succeed");
88
89 ret = pipe(chainlock_up);
90 ok(ret == 0, "pipe should succeed");
91
92 ret = pipe(allrecord_down);
93 ok(ret == 0, "pipe should succeed");
94
95 ret = pipe(allrecord_up);
96 ok(ret == 0, "pipe should succeed");
97
98 chainlock_child = fork();
99 ok(chainlock_child != -1, "fork should succeed");
100
101 if (chainlock_child == 0) {
102 close(chainlock_up[0]);
103 close(chainlock_down[1]);
104 close(allrecord_up[0]);
105 close(allrecord_up[1]);
106 close(allrecord_down[0]);
107 close(allrecord_down[1]);
108 do_chainlock(name, tdb_flags,
109 chainlock_up[1], chainlock_down[0]);
110 exit(0);
111 }
112 close(chainlock_up[1]);
113 close(chainlock_down[0]);
114
115 nread = read(chainlock_up[0], &c, sizeof(c));
116 ok(nread == sizeof(c), "read should succeed");
117
118 /*
119 * Now we have a process holding a chainlock. Start another process
120 * trying the allrecord lock. This will block.
121 */
122
123 allrecord_child = fork();
124 ok(allrecord_child != -1, "fork should succeed");
125
126 if (allrecord_child == 0) {
127 close(chainlock_up[0]);
128 close(chainlock_up[1]);
129 close(chainlock_down[0]);
130 close(chainlock_down[1]);
131 close(allrecord_up[0]);
132 close(allrecord_down[1]);
133 do_allrecord_lock(name, tdb_flags,
134 allrecord_up[1], allrecord_down[0]);
135 exit(0);
136 }
137 close(allrecord_up[1]);
138 close(allrecord_down[0]);
139
140 poll(NULL, 0, 500);
141
142 tdb = tdb_open_ex(name, 3, tdb_flags,
143 O_RDWR|O_CREAT, 0755, &taplogctx, NULL);
144 ok(tdb, "tdb_open_ex should succeed");
145
146 /*
147 * Someone already holds a chainlock, but we're able to get the
148 * freelist lock.
149 *
150 * The freelist lock/mutex is independent from the allrecord lock/mutex.
151 */
152
153 ret = tdb_chainlock_nonblock(tdb, key);
154 ok(ret == -1, "tdb_chainlock_nonblock should not succeed");
155
156 ret = tdb_lock_nonblock(tdb, -1, F_WRLCK);
157 ok(ret == 0, "tdb_lock_nonblock should succeed");
158
159 ret = tdb_unlock(tdb, -1, F_WRLCK);
160 ok(ret == 0, "tdb_unlock should succeed");
161
162 /*
163 * We have someone else having done the lock for us. Just mark it.
164 */
165
166 ret = tdb_chainlock_mark(tdb, key);
167 ok(ret == 0, "tdb_chainlock_mark should succeed");
168
169 /*
170 * The tdb_store below will block the freelist. In one version of the
171 * mutex patches, the freelist was already blocked here by the
172 * allrecord child, which was waiting for the chainlock child to give
173 * up its chainlock. Make sure that we don't run into this
174 * deadlock. To excercise the deadlock, just comment out the "ok"
175 * line.
176 *
177 * The freelist lock/mutex is independent from the allrecord lock/mutex.
178 */
179
180 ret = tdb_lock_nonblock(tdb, -1, F_WRLCK);
181 ok(ret == 0, "tdb_lock_nonblock should succeed");
182
183 ret = tdb_unlock(tdb, -1, F_WRLCK);
184 ok(ret == 0, "tdb_unlock should succeed");
185
186 ret = tdb_store(tdb, key, data, TDB_INSERT);
187 ok(ret == 0, "tdb_store should succeed");
188
189 ret = tdb_chainlock_unmark(tdb, key);
190 ok(ret == 0, "tdb_chainlock_unmark should succeed");
191
192 nwritten = write(chainlock_down[1], &c, sizeof(c));
193 ok(nwritten == sizeof(c), "write should succeed");
194
195 nread = read(chainlock_up[0], &c, sizeof(c));
196 ok(nread == 0, "read should succeed");
197
198 nread = read(allrecord_up[0], &c, sizeof(c));
199 ok(nread == sizeof(c), "read should succeed");
200
201 /*
202 * Someone already holds the allrecord lock, but we're able to get the
203 * freelist lock.
204 *
205 * The freelist lock/mutex is independent from the allrecord lock/mutex.
206 */
207
208 ret = tdb_chainlock_nonblock(tdb, key);
209 ok(ret == -1, "tdb_chainlock_nonblock should not succeed");
210
211 ret = tdb_lockall_nonblock(tdb);
212 ok(ret == -1, "tdb_lockall_nonblock should not succeed");
213
214 ret = tdb_lock_nonblock(tdb, -1, F_WRLCK);
215 ok(ret == 0, "tdb_lock_nonblock should succeed");
216
217 ret = tdb_unlock(tdb, -1, F_WRLCK);
218 ok(ret == 0, "tdb_unlock should succeed");
219
220 /*
221 * We have someone else having done the lock for us. Just mark it.
222 */
223
224 ret = tdb_lockall_mark(tdb);
225 ok(ret == 0, "tdb_lockall_mark should succeed");
226
227 ret = tdb_lock_nonblock(tdb, -1, F_WRLCK);
228 ok(ret == 0, "tdb_lock_nonblock should succeed");
229
230 ret = tdb_unlock(tdb, -1, F_WRLCK);
231 ok(ret == 0, "tdb_unlock should succeed");
232
233 ret = tdb_store(tdb, key, data, TDB_REPLACE);
234 ok(ret == 0, "tdb_store should succeed");
235
236 ret = tdb_lockall_unmark(tdb);
237 ok(ret == 0, "tdb_lockall_unmark should succeed");
238
239 nwritten = write(allrecord_down[1], &c, sizeof(c));
240 ok(nwritten == sizeof(c), "write should succeed");
241
242 nread = read(allrecord_up[0], &c, sizeof(c));
243 ok(nread == 0, "read should succeed");
244
245 close(chainlock_up[0]);
246 close(chainlock_down[1]);
247 close(allrecord_up[0]);
248 close(allrecord_down[1]);
249 diag("%s tests done", name);
250 return exit_status();
251}
252
253int main(int argc, char *argv[])
254{
255 int ret;
256 bool mutex_support;
257
258 mutex_support = tdb_runtime_check_for_robust_mutexes();
259
260 ret = do_tests("marklock-deadlock-fcntl.tdb",
261 TDB_CLEAR_IF_FIRST |
262 TDB_INCOMPATIBLE_HASH);
263 ok(ret == 0, "marklock-deadlock-fcntl.tdb tests should succeed");
264
265 if (!mutex_support) {
266 skip(1, "No robust mutex support, "
267 "skipping marklock-deadlock-mutex.tdb tests");
268 return exit_status();
269 }
270
271 ret = do_tests("marklock-deadlock-mutex.tdb",
272 TDB_CLEAR_IF_FIRST |
273 TDB_MUTEX_LOCKING |
274 TDB_INCOMPATIBLE_HASH);
275 ok(ret == 0, "marklock-deadlock-mutex.tdb tests should succeed");
276
277 return exit_status();
278}
Note: See TracBrowser for help on using the repository browser.