tdb: version 1.3.5
[Samba.git] / lib / tdb / test / run-marklock-deadlock.c
blobff03a11d6fb60f63797ed5334b83d363b796e41d
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"
20 static TDB_DATA key, data;
22 static void do_chainlock(const char *name, int tdb_flags, int up, int down)
24 struct tdb_context *tdb;
25 int ret;
26 ssize_t nread, nwritten;
27 char c = 0;
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");
33 ret = tdb_chainlock(tdb, key);
34 ok(ret == 0, "tdb_chainlock should succeed");
36 nwritten = write(up, &c, sizeof(c));
37 ok(nwritten == sizeof(c), "write should succeed");
39 nread = read(down, &c, sizeof(c));
40 ok(nread == sizeof(c), "read should succeed");
42 exit(0);
45 static void do_allrecord_lock(const char *name, int tdb_flags, int up, int down)
47 struct tdb_context *tdb;
48 int ret;
49 ssize_t nread, nwritten;
50 char c = 0;
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");
56 ret = tdb_allrecord_lock(tdb, F_WRLCK, TDB_LOCK_WAIT, false);
57 ok(ret == 0, "tdb_allrecord_lock should succeed");
59 nwritten = write(up, &c, sizeof(c));
60 ok(nwritten == sizeof(c), "write should succeed");
62 nread = read(down, &c, sizeof(c));
63 ok(nread == sizeof(c), "read should succeed");
65 exit(0);
68 /* The code should barf on TDBs created with rwlocks. */
69 static int do_tests(const char *name, int tdb_flags)
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;
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");
86 ret = pipe(chainlock_down);
87 ok(ret == 0, "pipe should succeed");
89 ret = pipe(chainlock_up);
90 ok(ret == 0, "pipe should succeed");
92 ret = pipe(allrecord_down);
93 ok(ret == 0, "pipe should succeed");
95 ret = pipe(allrecord_up);
96 ok(ret == 0, "pipe should succeed");
98 chainlock_child = fork();
99 ok(chainlock_child != -1, "fork should succeed");
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);
112 close(chainlock_up[1]);
113 close(chainlock_down[0]);
115 nread = read(chainlock_up[0], &c, sizeof(c));
116 ok(nread == sizeof(c), "read should succeed");
119 * Now we have a process holding a chainlock. Start another process
120 * trying the allrecord lock. This will block.
123 allrecord_child = fork();
124 ok(allrecord_child != -1, "fork should succeed");
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);
137 close(allrecord_up[1]);
138 close(allrecord_down[0]);
140 poll(NULL, 0, 500);
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");
147 * Someone already holds a chainlock, but we're able to get the
148 * freelist lock.
150 * The freelist lock/mutex is independent from the allrecord lock/mutex.
153 ret = tdb_chainlock_nonblock(tdb, key);
154 ok(ret == -1, "tdb_chainlock_nonblock should not succeed");
156 ret = tdb_lock_nonblock(tdb, -1, F_WRLCK);
157 ok(ret == 0, "tdb_lock_nonblock should succeed");
159 ret = tdb_unlock(tdb, -1, F_WRLCK);
160 ok(ret == 0, "tdb_unlock should succeed");
163 * We have someone else having done the lock for us. Just mark it.
166 ret = tdb_chainlock_mark(tdb, key);
167 ok(ret == 0, "tdb_chainlock_mark should succeed");
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.
177 * The freelist lock/mutex is independent from the allrecord lock/mutex.
180 ret = tdb_lock_nonblock(tdb, -1, F_WRLCK);
181 ok(ret == 0, "tdb_lock_nonblock should succeed");
183 ret = tdb_unlock(tdb, -1, F_WRLCK);
184 ok(ret == 0, "tdb_unlock should succeed");
186 ret = tdb_store(tdb, key, data, TDB_INSERT);
187 ok(ret == 0, "tdb_store should succeed");
189 ret = tdb_chainlock_unmark(tdb, key);
190 ok(ret == 0, "tdb_chainlock_unmark should succeed");
192 nwritten = write(chainlock_down[1], &c, sizeof(c));
193 ok(nwritten == sizeof(c), "write should succeed");
195 nread = read(chainlock_up[0], &c, sizeof(c));
196 ok(nread == 0, "read should succeed");
198 nread = read(allrecord_up[0], &c, sizeof(c));
199 ok(nread == sizeof(c), "read should succeed");
202 * Someone already holds the allrecord lock, but we're able to get the
203 * freelist lock.
205 * The freelist lock/mutex is independent from the allrecord lock/mutex.
208 ret = tdb_chainlock_nonblock(tdb, key);
209 ok(ret == -1, "tdb_chainlock_nonblock should not succeed");
211 ret = tdb_lockall_nonblock(tdb);
212 ok(ret == -1, "tdb_lockall_nonblock should not succeed");
214 ret = tdb_lock_nonblock(tdb, -1, F_WRLCK);
215 ok(ret == 0, "tdb_lock_nonblock should succeed");
217 ret = tdb_unlock(tdb, -1, F_WRLCK);
218 ok(ret == 0, "tdb_unlock should succeed");
221 * We have someone else having done the lock for us. Just mark it.
224 ret = tdb_lockall_mark(tdb);
225 ok(ret == 0, "tdb_lockall_mark should succeed");
227 ret = tdb_lock_nonblock(tdb, -1, F_WRLCK);
228 ok(ret == 0, "tdb_lock_nonblock should succeed");
230 ret = tdb_unlock(tdb, -1, F_WRLCK);
231 ok(ret == 0, "tdb_unlock should succeed");
233 ret = tdb_store(tdb, key, data, TDB_REPLACE);
234 ok(ret == 0, "tdb_store should succeed");
236 ret = tdb_lockall_unmark(tdb);
237 ok(ret == 0, "tdb_lockall_unmark should succeed");
239 nwritten = write(allrecord_down[1], &c, sizeof(c));
240 ok(nwritten == sizeof(c), "write should succeed");
242 nread = read(allrecord_up[0], &c, sizeof(c));
243 ok(nread == 0, "read should succeed");
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();
253 int main(int argc, char *argv[])
255 int ret;
256 bool mutex_support;
258 mutex_support = tdb_runtime_check_for_robust_mutexes();
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");
265 if (!mutex_support) {
266 skip(1, "No robust mutex support, "
267 "skipping marklock-deadlock-mutex.tdb tests");
268 return exit_status();
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");
277 return exit_status();