]> git.ozlabs.org Git - ccan/blob - ccan/ntdb/test/api-fork-test.c
ntdb: next-generation trivial key-value database
[ccan] / ccan / ntdb / test / api-fork-test.c
1 /* Test forking while holding lock.
2  *
3  * There are only five ways to do this currently:
4  * (1) grab a ntdb_chainlock, then fork.
5  * (2) grab a ntdb_lockall, then fork.
6  * (3) grab a ntdb_lockall_read, then fork.
7  * (4) start a transaction, then fork.
8  * (5) fork from inside a ntdb_parse() callback.
9  *
10  * Note that we don't hold a lock across ntdb_traverse callbacks, so
11  * that doesn't matter.
12  */
13 #include "config.h"
14 #include "ntdb.h"
15 #include "private.h"
16 #include "tap-interface.h"
17 #include "logging.h"
18
19 static bool am_child = false;
20
21 static enum NTDB_ERROR fork_in_parse(NTDB_DATA key, NTDB_DATA data,
22                                     struct ntdb_context *ntdb)
23 {
24         int status;
25
26         if (fork() == 0) {
27                 am_child = true;
28
29                 /* We expect this to fail. */
30                 if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
31                         exit(1);
32
33                 if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
34                         exit(1);
35
36                 if (tap_log_messages != 2)
37                         exit(2);
38
39                 return NTDB_SUCCESS;
40         }
41         wait(&status);
42         ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
43         return NTDB_SUCCESS;
44 }
45
46 int main(int argc, char *argv[])
47 {
48         unsigned int i;
49         struct ntdb_context *ntdb;
50         int flags[] = { NTDB_DEFAULT, NTDB_NOMMAP,
51                         NTDB_CONVERT, NTDB_NOMMAP|NTDB_CONVERT };
52         NTDB_DATA key = ntdb_mkdata("key", 3);
53         NTDB_DATA data = ntdb_mkdata("data", 4);
54
55         plan_tests(sizeof(flags) / sizeof(flags[0]) * 14);
56         for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
57                 int status;
58
59                 tap_log_messages = 0;
60
61                 ntdb = ntdb_open("run-fork-test.ntdb",
62                                  flags[i]|MAYBE_NOSYNC,
63                                  O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
64                 if (!ok1(ntdb))
65                         continue;
66
67                 /* Put a record in here. */
68                 ok1(ntdb_store(ntdb, key, data, NTDB_REPLACE) == NTDB_SUCCESS);
69
70                 ok1(ntdb_chainlock(ntdb, key) == NTDB_SUCCESS);
71                 if (fork() == 0) {
72                         /* We expect this to fail. */
73                         if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
74                                 return 1;
75
76                         if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
77                                 return 1;
78
79                         if (tap_log_messages != 2)
80                                 return 2;
81
82                         /* Child can do this without any complaints. */
83                         ntdb_chainunlock(ntdb, key);
84                         if (tap_log_messages != 2)
85                                 return 3;
86                         ntdb_close(ntdb);
87                         if (tap_log_messages != 2)
88                                 return 4;
89                         return 0;
90                 }
91                 wait(&status);
92                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
93                 ntdb_chainunlock(ntdb, key);
94
95                 ok1(ntdb_lockall(ntdb) == NTDB_SUCCESS);
96                 if (fork() == 0) {
97                         /* We expect this to fail. */
98                         if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
99                                 return 1;
100
101                         if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
102                                 return 1;
103
104                         if (tap_log_messages != 2)
105                                 return 2;
106
107                         /* Child can do this without any complaints. */
108                         ntdb_unlockall(ntdb);
109                         if (tap_log_messages != 2)
110                                 return 3;
111                         ntdb_close(ntdb);
112                         if (tap_log_messages != 2)
113                                 return 4;
114                         return 0;
115                 }
116                 wait(&status);
117                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
118                 ntdb_unlockall(ntdb);
119
120                 ok1(ntdb_lockall_read(ntdb) == NTDB_SUCCESS);
121                 if (fork() == 0) {
122                         /* We expect this to fail. */
123                         /* This would always fail anyway... */
124                         if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
125                                 return 1;
126
127                         if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
128                                 return 1;
129
130                         if (tap_log_messages != 2)
131                                 return 2;
132
133                         /* Child can do this without any complaints. */
134                         ntdb_unlockall_read(ntdb);
135                         if (tap_log_messages != 2)
136                                 return 3;
137                         ntdb_close(ntdb);
138                         if (tap_log_messages != 2)
139                                 return 4;
140                         return 0;
141                 }
142                 wait(&status);
143                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
144                 ntdb_unlockall_read(ntdb);
145
146                 ok1(ntdb_transaction_start(ntdb) == NTDB_SUCCESS);
147                 /* If transactions is empty, noop "commit" succeeds. */
148                 ok1(ntdb_delete(ntdb, key) == NTDB_SUCCESS);
149                 if (fork() == 0) {
150                         int last_log_messages;
151
152                         /* We expect this to fail. */
153                         if (ntdb_store(ntdb, key, data, NTDB_REPLACE) != NTDB_ERR_LOCK)
154                                 return 1;
155
156                         if (ntdb_fetch(ntdb, key, &data) != NTDB_ERR_LOCK)
157                                 return 1;
158
159                         if (tap_log_messages != 2)
160                                 return 2;
161
162                         if (ntdb_transaction_prepare_commit(ntdb)
163                             != NTDB_ERR_LOCK)
164                                 return 3;
165                         if (tap_log_messages == 2)
166                                 return 4;
167
168                         last_log_messages = tap_log_messages;
169                         /* Child can do this without any complaints. */
170                         ntdb_transaction_cancel(ntdb);
171                         if (tap_log_messages != last_log_messages)
172                                 return 4;
173                         ntdb_close(ntdb);
174                         if (tap_log_messages != last_log_messages)
175                                 return 4;
176                         return 0;
177                 }
178                 wait(&status);
179                 ok1(WIFEXITED(status) && WEXITSTATUS(status) == 0);
180                 ntdb_transaction_cancel(ntdb);
181
182                 ok1(ntdb_parse_record(ntdb, key, fork_in_parse, ntdb)
183                     == NTDB_SUCCESS);
184                 ntdb_close(ntdb);
185                 if (am_child) {
186                         /* Child can return from parse without complaints. */
187                         if (tap_log_messages != 2)
188                                 exit(3);
189                         exit(0);
190                 }
191                 ok1(tap_log_messages == 0);
192         }
193         return exit_status();
194 }