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"
15 #include <sys/types.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
;
26 ssize_t nread
, nwritten
;
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");
45 static void do_allrecord_lock(const char *name
, int tdb_flags
, int up
, int down
)
47 struct tdb_context
*tdb
;
49 ssize_t nread
, nwritten
;
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");
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
;
73 pid_t chainlock_child
, allrecord_child
;
74 int chainlock_down
[2];
76 int allrecord_down
[2];
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]);
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]);
137 close(allrecord_up
[1]);
138 close(allrecord_down
[0]);
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
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"
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
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
[])
258 mutex_support
= tdb_runtime_check_for_robust_mutexes();
260 ret
= do_tests("marklock-deadlock-fcntl.tdb",
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",
274 TDB_INCOMPATIBLE_HASH
);
275 ok(ret
== 0, "marklock-deadlock-mutex.tdb tests should succeed");
277 return exit_status();