1 #include <ccan/tdb2/tdb2.h>
2 #include <ccan/tap/tap.h>
10 #include "external-agent.h"
13 #define alarm fast_alarm
15 /* Speed things up by doing things in milliseconds. */
16 static unsigned int fast_alarm(unsigned int milli_seconds
)
20 it
.it_interval
.tv_sec
= it
.it_interval
.tv_usec
= 0;
21 it
.it_value
.tv_sec
= milli_seconds
/ 1000;
22 it
.it_value
.tv_usec
= milli_seconds
* 1000;
23 setitimer(ITIMER_REAL
, &it
, NULL
);
27 #define CatchSignal(sig, handler) signal((sig), (handler))
29 static void do_nothing(int signum
)
33 /* This example code is taken from SAMBA, so try not to change it. */
34 static struct flock flock_struct
;
36 /* Return a value which is none of v1, v2 or v3. */
37 static inline short int invalid_value(short int v1
, short int v2
, short int v3
)
39 short int try = (v1
+v2
+v3
)^((v1
+v2
+v3
) << 16);
40 while (try == v1
|| try == v2
|| try == v3
)
45 /* We invalidate in as many ways as we can, so the OS rejects it */
46 static void invalidate_flock_struct(int signum
)
48 flock_struct
.l_type
= invalid_value(F_RDLCK
, F_WRLCK
, F_UNLCK
);
49 flock_struct
.l_whence
= invalid_value(SEEK_SET
, SEEK_CUR
, SEEK_END
);
50 flock_struct
.l_start
= -1;
51 /* A large negative. */
52 flock_struct
.l_len
= (((off_t
)1 << (sizeof(off_t
)*CHAR_BIT
- 1)) + 1);
55 static int timeout_lock(int fd
, int rw
, off_t off
, off_t len
, bool waitflag
,
58 int ret
, saved_errno
= errno
;
59 unsigned int timeout
= *(unsigned int *)_timeout
;
61 flock_struct
.l_type
= rw
;
62 flock_struct
.l_whence
= SEEK_SET
;
63 flock_struct
.l_start
= off
;
64 flock_struct
.l_len
= len
;
66 CatchSignal(SIGALRM
, invalidate_flock_struct
);
71 ret
= fcntl(fd
, F_SETLKW
, &flock_struct
);
73 ret
= fcntl(fd
, F_SETLK
, &flock_struct
);
78 /* Not signalled? Something else went wrong. */
79 if (flock_struct
.l_len
== len
) {
80 if (errno
== EAGAIN
|| errno
== EINTR
)
95 static int tdb_chainlock_with_timeout_internal(struct tdb_context
*tdb
,
100 union tdb_attribute locking
;
101 enum TDB_ERROR ecode
;
104 locking
.base
.attr
= TDB_ATTRIBUTE_FLOCK
;
105 ecode
= tdb_get_attribute(tdb
, &locking
);
106 if (ecode
!= TDB_SUCCESS
)
109 /* Replace locking function with our own. */
110 locking
.flock
.data
= &timeout
;
111 locking
.flock
.lock
= timeout_lock
;
113 ecode
= tdb_set_attribute(tdb
, &locking
);
114 if (ecode
!= TDB_SUCCESS
)
117 if (rw_type
== F_RDLCK
)
118 ecode
= tdb_chainlock_read(tdb
, key
);
120 ecode
= tdb_chainlock(tdb
, key
);
123 tdb_unset_attribute(tdb
, TDB_ATTRIBUTE_FLOCK
);
128 int main(int argc
, char *argv
[])
131 struct tdb_context
*tdb
;
132 TDB_DATA key
= tdb_mkdata("hello", 5);
133 int flags
[] = { TDB_DEFAULT
, TDB_NOMMAP
,
134 TDB_CONVERT
, TDB_NOMMAP
|TDB_CONVERT
,
135 TDB_VERSION1
, TDB_NOMMAP
|TDB_VERSION1
,
136 TDB_CONVERT
|TDB_VERSION1
,
137 TDB_NOMMAP
|TDB_CONVERT
|TDB_VERSION1
};
140 plan_tests(sizeof(flags
) / sizeof(flags
[0]) * 15);
142 agent
= prepare_external_agent();
144 for (i
= 0; i
< sizeof(flags
) / sizeof(flags
[0]); i
++) {
145 enum TDB_ERROR ecode
;
146 tdb
= tdb_open("run-locktimeout.tdb", flags
[i
],
147 O_RDWR
|O_CREAT
|O_TRUNC
, 0600, &tap_log_attr
);
151 /* Simple cases: should succeed. */
152 ecode
= tdb_chainlock_with_timeout_internal(tdb
, key
, 20,
154 ok1(ecode
== TDB_SUCCESS
);
155 ok1(tap_log_messages
== 0);
157 tdb_chainunlock_read(tdb
, key
);
158 ok1(tap_log_messages
== 0);
160 ecode
= tdb_chainlock_with_timeout_internal(tdb
, key
, 20,
162 ok1(ecode
== TDB_SUCCESS
);
163 ok1(tap_log_messages
== 0);
165 tdb_chainunlock(tdb
, key
);
166 ok1(tap_log_messages
== 0);
168 /* OK, get agent to start transaction, then we should time out. */
169 ok1(external_agent_operation(agent
, OPEN
, "run-locktimeout.tdb")
171 ok1(external_agent_operation(agent
, TRANSACTION_START
, "")
173 ecode
= tdb_chainlock_with_timeout_internal(tdb
, key
, 20,
175 ok1(ecode
== TDB_ERR_LOCK
);
176 ok1(tap_log_messages
== 0);
178 /* Even if we get a different signal, should be fine. */
179 CatchSignal(SIGUSR1
, do_nothing
);
180 external_agent_operation(agent
, SEND_SIGNAL
, "");
181 ecode
= tdb_chainlock_with_timeout_internal(tdb
, key
, 20,
183 ok1(ecode
== TDB_ERR_LOCK
);
184 ok1(tap_log_messages
== 0);
186 ok1(external_agent_operation(agent
, TRANSACTION_COMMIT
, "")
188 ok1(external_agent_operation(agent
, CLOSE
, "")
192 free_external_agent(agent
);
193 return exit_status();