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>
19 static TDB_DATA key
, data
;
21 static void log_fn(struct tdb_context
*tdb
, enum tdb_debug_level level
,
26 vfprintf(stderr
, fmt
, ap
);
30 static int do_child(int tdb_flags
, int to
, int from
)
32 struct tdb_context
*tdb
;
33 unsigned int log_count
;
34 struct tdb_logging_context log_ctx
= { log_fn
, &log_count
};
38 tdb
= tdb_open_ex("mutex-transaction1.tdb", 3, tdb_flags
,
39 O_RDWR
|O_CREAT
, 0755, &log_ctx
, NULL
);
40 ok(tdb
, "tdb_open_ex should succeed");
42 ret
= tdb_transaction_start(tdb
);
43 ok(ret
== 0, "tdb_transaction_start should succeed");
45 ret
= tdb_store(tdb
, key
, data
, TDB_INSERT
);
46 ok(ret
== 0, "tdb_store(tdb, key, data, TDB_INSERT) should succeed");
48 write(to
, &c
, sizeof(c
));
49 read(from
, &c
, sizeof(c
));
51 ret
= tdb_transaction_cancel(tdb
);
52 ok(ret
== 0, "tdb_transaction_cancel should succeed");
54 write(to
, &c
, sizeof(c
));
55 read(from
, &c
, sizeof(c
));
57 ret
= tdb_transaction_start(tdb
);
58 ok(ret
== 0, "tdb_transaction_start should succeed");
60 ret
= tdb_store(tdb
, key
, data
, TDB_INSERT
);
61 ok(ret
== 0, "tdb_store(tdb, key, data, TDB_INSERT) should succeed");
63 write(to
, &c
, sizeof(c
));
64 read(from
, &c
, sizeof(c
));
66 ret
= tdb_transaction_commit(tdb
);
67 ok(ret
== 0, "tdb_transaction_commit should succeed");
69 write(to
, &c
, sizeof(c
));
70 read(from
, &c
, sizeof(c
));
72 ret
= tdb_transaction_start(tdb
);
73 ok(ret
== 0, "tdb_transaction_start should succeed");
75 ret
= tdb_store(tdb
, key
, key
, TDB_REPLACE
);
76 ok(ret
== 0, "tdb_store(tdb, key, data, TDB_REPLACE) should succeed");
78 write(to
, &c
, sizeof(c
));
79 read(from
, &c
, sizeof(c
));
81 ret
= tdb_transaction_commit(tdb
);
82 ok(ret
== 0, "tdb_transaction_commit should succeed");
84 write(to
, &c
, sizeof(c
));
85 read(from
, &c
, sizeof(c
));
90 /* The code should barf on TDBs created with rwlocks. */
91 int main(int argc
, char *argv
[])
93 struct tdb_context
*tdb
;
94 unsigned int log_count
;
95 struct tdb_logging_context log_ctx
= { log_fn
, &log_count
};
97 pid_t child
, wait_ret
;
103 bool runtime_support
;
105 runtime_support
= tdb_runtime_check_for_robust_mutexes();
107 if (!runtime_support
) {
108 skip(1, "No robust mutex support");
109 return exit_status();
112 key
.dsize
= strlen("hi");
113 key
.dptr
= discard_const_p(uint8_t, "hi");
114 data
.dsize
= strlen("world");
115 data
.dptr
= discard_const_p(uint8_t, "world");
120 tdb_flags
= TDB_INCOMPATIBLE_HASH
|
128 return do_child(tdb_flags
, fromchild
[1], tochild
[0]);
133 read(fromchild
[0], &c
, sizeof(c
));
135 tdb
= tdb_open_ex("mutex-transaction1.tdb", 0,
136 tdb_flags
, O_RDWR
|O_CREAT
, 0755,
138 ok(tdb
, "tdb_open_ex should succeed");
141 * The child has the transaction running
143 ret
= tdb_transaction_start_nonblock(tdb
);
144 ok(ret
== -1, "tdb_transaction_start_nonblock not succeed");
146 ret
= tdb_chainlock_nonblock(tdb
, key
);
147 ok(ret
== -1, "tdb_chainlock_nonblock should not succeed");
152 ret
= tdb_exists(tdb
, key
);
153 ok(ret
== 0, "tdb_exists(tdb, key) should return 0");
155 val
= tdb_fetch(tdb
, key
);
156 ok(val
.dsize
== 0, "tdb_fetch(tdb, key) should return an empty value");
158 write(tochild
[1], &c
, sizeof(c
));
161 * When the child canceled we can start...
163 ret
= tdb_transaction_start(tdb
);
164 ok(ret
== 0, "tdb_transaction_start should succeed");
166 read(fromchild
[0], &c
, sizeof(c
));
167 write(tochild
[1], &c
, sizeof(c
));
169 ret
= tdb_transaction_cancel(tdb
);
170 ok(ret
== 0, "tdb_transaction_cancel should succeed");
173 * When we canceled the child can start and store...
175 read(fromchild
[0], &c
, sizeof(c
));
178 * We still see the old values before the child commits...
180 ret
= tdb_exists(tdb
, key
);
181 ok(ret
== 0, "tdb_exists(tdb, key) should return 0");
183 val
= tdb_fetch(tdb
, key
);
184 ok(val
.dsize
== 0, "tdb_fetch(tdb, key) should return an empty value");
186 write(tochild
[1], &c
, sizeof(c
));
187 read(fromchild
[0], &c
, sizeof(c
));
190 * We see the new values after the commit...
192 ret
= tdb_exists(tdb
, key
);
193 ok(ret
== 1, "tdb_exists(tdb, key) should return 1");
195 val
= tdb_fetch(tdb
, key
);
196 ok(val
.dsize
!= 0, "tdb_fetch(tdb, key) should return a value");
197 ok(val
.dsize
== data
.dsize
, "tdb_fetch(tdb, key) should return a value");
198 ok(memcmp(val
.dptr
, data
.dptr
, data
.dsize
) == 0, "tdb_fetch(tdb, key) should return a value");
200 write(tochild
[1], &c
, sizeof(c
));
201 read(fromchild
[0], &c
, sizeof(c
));
204 * The child started a new transaction and replaces the value,
205 * but we still see the old values before the child commits...
207 ret
= tdb_exists(tdb
, key
);
208 ok(ret
== 1, "tdb_exists(tdb, key) should return 1");
210 val
= tdb_fetch(tdb
, key
);
211 ok(val
.dsize
!= 0, "tdb_fetch(tdb, key) should return a value");
212 ok(val
.dsize
== data
.dsize
, "tdb_fetch(tdb, key) should return a value");
213 ok(memcmp(val
.dptr
, data
.dptr
, data
.dsize
) == 0, "tdb_fetch(tdb, key) should return a value");
215 write(tochild
[1], &c
, sizeof(c
));
216 read(fromchild
[0], &c
, sizeof(c
));
219 * We see the new values after the commit...
221 ret
= tdb_exists(tdb
, key
);
222 ok(ret
== 1, "tdb_exists(tdb, key) should return 1");
224 val
= tdb_fetch(tdb
, key
);
225 ok(val
.dsize
!= 0, "tdb_fetch(tdb, key) should return a value");
226 ok(val
.dsize
== key
.dsize
, "tdb_fetch(tdb, key) should return a value");
227 ok(memcmp(val
.dptr
, key
.dptr
, key
.dsize
) == 0, "tdb_fetch(tdb, key) should return a value");
229 write(tochild
[1], &c
, sizeof(c
));
231 wait_ret
= wait(&status
);
232 ok(wait_ret
== child
, "child should have exited correctly");
235 return exit_status();