tdb2: test: fix run-57-die-during-transaction.c to be more efficient.
[ccan] / ccan / tdb2 / test / run-locktimeout.c
1 #include "tdb2-source.h"
2 #include <ccan/tap/tap.h>
3 #include "logging.h"
4 #include "external-agent.h"
5
6 #undef alarm
7 #define alarm fast_alarm
8
9 /* Speed things up by doing things in milliseconds. */
10 static unsigned int fast_alarm(unsigned int milli_seconds)
11 {
12         struct itimerval it;
13
14         it.it_interval.tv_sec = it.it_interval.tv_usec = 0;
15         it.it_value.tv_sec = milli_seconds / 1000;
16         it.it_value.tv_usec = milli_seconds * 1000;
17         setitimer(ITIMER_REAL, &it, NULL);
18         return 0;
19 }
20
21 #define CatchSignal(sig, handler) signal((sig), (handler))
22
23 static void do_nothing(int signum)
24 {
25 }
26
27 /* This example code is taken from SAMBA, so try not to change it. */
28 static struct flock flock_struct;
29
30 /* Return a value which is none of v1, v2 or v3. */
31 static inline short int invalid_value(short int v1, short int v2, short int v3)
32 {
33         short int try = (v1+v2+v3)^((v1+v2+v3) << 16);
34         while (try == v1 || try == v2 || try == v3)
35                 try++;
36         return try;
37 }
38
39 /* We invalidate in as many ways as we can, so the OS rejects it */
40 static void invalidate_flock_struct(int signum)
41 {
42         flock_struct.l_type = invalid_value(F_RDLCK, F_WRLCK, F_UNLCK);
43         flock_struct.l_whence = invalid_value(SEEK_SET, SEEK_CUR, SEEK_END);
44         flock_struct.l_start = -1;
45         /* A large negative. */
46         flock_struct.l_len = (((off_t)1 << (sizeof(off_t)*CHAR_BIT - 1)) + 1);
47 }
48
49 static int timeout_lock(int fd, int rw, off_t off, off_t len, bool waitflag,
50                         void *_timeout)
51 {
52         int ret, saved_errno = errno;
53         unsigned int timeout = *(unsigned int *)_timeout;
54
55         flock_struct.l_type = rw;
56         flock_struct.l_whence = SEEK_SET;
57         flock_struct.l_start = off;
58         flock_struct.l_len = len;
59
60         CatchSignal(SIGALRM, invalidate_flock_struct);
61         alarm(timeout);
62
63         for (;;) {
64                 if (waitflag)
65                         ret = fcntl(fd, F_SETLKW, &flock_struct);
66                 else
67                         ret = fcntl(fd, F_SETLK, &flock_struct);
68
69                 if (ret == 0)
70                         break;
71
72                 /* Not signalled?  Something else went wrong. */
73                 if (flock_struct.l_len == len) {
74                         if (errno == EAGAIN || errno == EINTR)
75                                 continue;
76                         saved_errno = errno;
77                         break;
78                 } else {
79                         saved_errno = EINTR;
80                         break;
81                 }
82         }
83
84         alarm(0);
85         errno = saved_errno;
86         return ret;
87 }
88
89 static int tdb_chainlock_with_timeout_internal(struct tdb_context *tdb,
90                                                TDB_DATA key,
91                                                unsigned int timeout,
92                                                int rw_type)
93 {
94         union tdb_attribute locking;
95         enum TDB_ERROR ecode;
96
97         if (timeout) {
98                 locking.base.attr = TDB_ATTRIBUTE_FLOCK;
99                 ecode = tdb_get_attribute(tdb, &locking);
100                 if (ecode != TDB_SUCCESS)
101                         return ecode;
102
103                 /* Replace locking function with our own. */
104                 locking.flock.data = &timeout;
105                 locking.flock.lock = timeout_lock;
106
107                 ecode = tdb_set_attribute(tdb, &locking);
108                 if (ecode != TDB_SUCCESS)
109                         return ecode;
110         }
111         if (rw_type == F_RDLCK)
112                 ecode = tdb_chainlock_read(tdb, key);
113         else
114                 ecode = tdb_chainlock(tdb, key);
115
116         if (timeout) {
117                 tdb_unset_attribute(tdb, TDB_ATTRIBUTE_FLOCK);
118         }
119         return ecode;
120 }
121
122 int main(int argc, char *argv[])
123 {
124         unsigned int i;
125         struct tdb_context *tdb;
126         TDB_DATA key = tdb_mkdata("hello", 5);
127         int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
128                         TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT,
129                         TDB_VERSION1, TDB_NOMMAP|TDB_VERSION1,
130                         TDB_CONVERT|TDB_VERSION1,
131                         TDB_NOMMAP|TDB_CONVERT|TDB_VERSION1 };
132         struct agent *agent;
133
134         plan_tests(sizeof(flags) / sizeof(flags[0]) * 15);
135
136         agent = prepare_external_agent();
137
138         for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
139                 enum TDB_ERROR ecode;
140                 tdb = tdb_open("run-locktimeout.tdb", flags[i],
141                                O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
142                 if (!ok1(tdb))
143                         break;
144
145                 /* Simple cases: should succeed. */ 
146                 ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
147                                                             F_RDLCK);
148                 ok1(ecode == TDB_SUCCESS);
149                 ok1(tap_log_messages == 0);
150
151                 tdb_chainunlock_read(tdb, key);
152                 ok1(tap_log_messages == 0);
153
154                 ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
155                                                             F_WRLCK);
156                 ok1(ecode == TDB_SUCCESS);
157                 ok1(tap_log_messages == 0);
158
159                 tdb_chainunlock(tdb, key);
160                 ok1(tap_log_messages == 0);
161
162                 /* OK, get agent to start transaction, then we should time out. */
163                 ok1(external_agent_operation(agent, OPEN, "run-locktimeout.tdb")
164                     == SUCCESS);
165                 ok1(external_agent_operation(agent, TRANSACTION_START, "") 
166                     == SUCCESS);
167                 ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
168                                                             F_WRLCK);
169                 ok1(ecode == TDB_ERR_LOCK);
170                 ok1(tap_log_messages == 0);
171
172                 /* Even if we get a different signal, should be fine. */
173                 CatchSignal(SIGUSR1, do_nothing);
174                 external_agent_operation(agent, SEND_SIGNAL, "");
175                 ecode = tdb_chainlock_with_timeout_internal(tdb, key, 20,
176                                                             F_WRLCK);
177                 ok1(ecode == TDB_ERR_LOCK);
178                 ok1(tap_log_messages == 0);
179
180                 ok1(external_agent_operation(agent, TRANSACTION_COMMIT, "")
181                     == SUCCESS);
182                 ok1(external_agent_operation(agent, CLOSE, "")
183                     == SUCCESS);
184                 tdb_close(tdb);
185         }
186         free_external_agent(agent);
187         return exit_status();
188 }