11 int fd, kind, probe, probe_error, result, error;
12 volatile int entered, done;
13 int64_t started, finished;
15static volatile sig_atomic_t signal_count;
16static void caught(
int signal) {
18 __atomic_add_fetch(&signal_count, 1, __ATOMIC_RELAXED);
20static void finished(
void* argument) {
21 __atomic_store_n((
volatile int*)argument, 1, __ATOMIC_RELEASE);
25 pthread_cleanup_push(finished, (
void*)&wait->done);
26 wait->started = fl_now();
27 wait->probe = fl_lock(wait->fd, wait->kind, F_WRLCK, 0);
28 wait->probe_error = errno;
29 if (wait->probe == -1 && wait->probe_error == EAGAIN) {
30 __atomic_store_n(&wait->entered, 1, __ATOMIC_RELEASE);
31 wait->result = fl_lock(wait->fd, wait->kind, F_WRLCK, 1);
34 wait->finished = fl_now();
35 pthread_cleanup_pop(1);
38static void diagnostic(
const struct lock_wait* wait,
int action, int64_t acted) {
39 const int entered = __atomic_load_n(&wait->entered, __ATOMIC_ACQUIRE);
40 const int done = __atomic_load_n(&wait->done, __ATOMIC_ACQUIRE);
42 "FILE-LOCK-CONTRACT: wait kind=%d action=%d entered=%d done=%d "
43 "probe=%d/%d result=%d/%d start=%lld action_time=%lld finish=%lld now=%lld\n",
44 wait->kind, action, entered, done, entered || done ? wait->probe : -2,
45 entered || done ? wait->probe_error : -2, done ? wait->result : -2,
46 done ? wait->error : -2, entered || done ? (long long)wait->started : -1,
47 (long long)acted, done ? (long long)wait->finished : -1, (long long)fl_now());
49static pid_t holder(
const char* path,
int inherited,
int kind,
int* control) {
51 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair))
58 int fd = open(path, O_RDWR);
59 if (fd < 0 || fl_lock(fd, kind, F_WRLCK, 0) || write(pair[1],
"h", 1) != 1 ||
60 fl_byte(pair[1],
'x'))
62 _exit(fl_lock(fd, kind, F_UNLCK, 0) ? 11 : 0);
73enum { RELEASE, INTERRUPT, CANCEL, REPLACE };
74static int blocking_case(
int kind,
int action) {
75 int failed = 0, fd = -1, other = -1, probe = -1, control = -1, running = 0;
78 struct lock_wait wait = {.kind = kind, .result = -2, .error = -2};
79 char path[128] = {0}, other_path[128] = {0};
81 CHECK((fd = fl_file(path,
"/tmp")) >= 0 && (other = fl_file(other_path,
"/tmp")) >= 0);
82 CHECK((child = holder(path, fd, kind, &control)) > 0 && fl_byte(control,
'h') == 0);
83 CHECK(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == 0);
87 CHECK(fl_wait(&wait.entered, 1000) == 0);
88 CHECK(__atomic_load_n(&wait.done, __ATOMIC_ACQUIRE) == 0);
89 if (action == REPLACE) {
93 CHECK(__atomic_load_n(&wait.done, __ATOMIC_ACQUIRE) == 0);
95 CHECK(dup2(other, fd) == fd);
96 if (kind == FL_CLASSIC) {
97 CHECK(fl_wait(&wait.done, 2000) == 0 && wait.result == -1 && wait.error == EBADF);
99 CHECK(__atomic_load_n(&wait.done, __ATOMIC_ACQUIRE) == 0);
101 }
else if (action == INTERRUPT) {
103 const int64_t until = acted + 2000000000;
104 while (!__atomic_load_n(&wait.done, __ATOMIC_ACQUIRE) && fl_now() < until) {
105 CHECK(pthread_kill(
worker, SIGUSR1) == 0);
108 CHECK(fl_wait(&wait.done, 100) == 0 && wait.result == -1 && wait.error == EINTR);
109 CHECK(fl_lock(fd, kind, F_WRLCK, 0) == -1 && errno == EAGAIN);
110 }
else if (action == CANCEL) {
112 CHECK(pthread_cancel(
worker) == 0 && fl_wait(&wait.done, 2000) == 0);
114 CHECK(pthread_join(
worker, &result) == 0 && result == PTHREAD_CANCELED);
119 CHECK(write(control,
"x", 1) == 1 && fl_reap(child, 2000) == 0);
122 CHECK(fl_wait(&wait.done, 2000) == 0);
123 if (action == RELEASE || (action == REPLACE && kind != FL_CLASSIC))
124 CHECK(wait.result == 0);
125 CHECK(pthread_join(
worker, NULL) == 0);
128 if (action == REPLACE) {
129 CHECK((probe = open(path, O_RDWR)) >= 0);
130 CHECK(fl_lock(probe, kind == FL_CLASSIC ? FL_OFD : kind, F_WRLCK, 0) == 0);
133 CHECK((probe = open(other_path, O_RDWR)) >= 0);
134 CHECK(fl_lock(probe, kind == FL_CLASSIC ? FL_OFD : kind, F_WRLCK, 0) == 0);
136 CHECK(fl_lock(fd, kind, F_WRLCK, 0) == 0);
140 diagnostic(&wait, action, acted);
142 kill(child, SIGKILL);
143 fl_reap(child, 1000);
146 if (fl_wait(&wait.done, 2000))
148 pthread_join(
worker, NULL);
165static int deadlock(
void) {
166 int failed = 0, a = -1, b = -1, pair[2] = {-1, -1}, running = 0;
168 char path[128] = {0}, second[128] = {0};
170 struct lock_wait wait = {.kind = FL_CLASSIC, .result = -2, .error = -2};
171 CHECK((a = fl_file(path,
"/tmp")) >= 0 && (b = fl_file(second,
"/tmp")) >= 0);
172 CHECK(fl_lock(a, FL_CLASSIC, F_WRLCK, 0) == 0);
173 CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0 && (child = fork()) >= 0);
177 if (fl_lock(b, FL_CLASSIC, F_WRLCK, 0) || fl_lock(a, FL_CLASSIC, F_WRLCK, 0) != -1 ||
178 errno != EAGAIN || write(pair[1],
"c", 1) != 1)
180 int result = fl_lock(a, FL_CLASSIC, F_WRLCK, 1), error = errno;
181 char report = result == 0 ?
'a' : error == EDEADLK ?
'd' :
'e';
182 if (fl_lock(b, FL_CLASSIC, F_UNLCK, 0) || write(pair[1], &report, 1) != 1)
184 _exit(report ==
'e' ? 12 : 0);
188 CHECK(fl_byte(pair[0],
'c') == 0);
192 CHECK(fl_wait(&wait.entered, 1000) == 0);
193 const int64_t until = fl_now() + 4000000000;
194 while (!__atomic_load_n(&wait.done, __ATOMIC_ACQUIRE)) {
195 struct pollfd ready = {.fd = pair[0], .events = POLLIN};
196 if (poll(&ready, 1, 0) > 0)
198 CHECK(fl_now() < until);
201 const int parent_deadlock =
202 __atomic_load_n(&wait.done, __ATOMIC_ACQUIRE) && wait.result == -1 && wait.error == EDEADLK;
204 CHECK(fl_lock(a, FL_CLASSIC, F_UNLCK, 0) == 0);
205 CHECK(fl_byte(pair[0], parent_deadlock ?
'a' :
'd') == 0);
206 CHECK(fl_wait(&wait.done, 2000) == 0 &&
207 (parent_deadlock ? wait.result == -1 && wait.error == EDEADLK : wait.result == 0));
208 CHECK(pthread_join(
worker, NULL) == 0);
210 CHECK(fl_reap(child, 2000) == 0);
214 diagnostic(&wait, 4, -1);
216 fl_lock(a, FL_CLASSIC, F_UNLCK, 0);
218 fl_lock(b, FL_CLASSIC, F_UNLCK, 0);
220 kill(child, SIGKILL);
221 fl_reap(child, 1000);
224 if (fl_wait(&wait.done, 2000))
226 pthread_join(
worker, NULL);
228 for (
int n = 0; n < 2; ++n)
241int file_lock_blocking(
void) {
242 int failed = 0, mask_changed = 0;
243 struct sigaction action = {.sa_handler = caught}, original;
244 sigset_t unblocked, previous;
245 sigemptyset(&action.sa_mask);
246 CHECK(sigaction(SIGUSR1, &action, &original) == 0);
247 sigemptyset(&unblocked);
248 sigaddset(&unblocked, SIGUSR1);
249 if (pthread_sigmask(SIG_UNBLOCK, &unblocked, &previous)) {
254 for (
int kind = 0; kind < 3; ++kind) {
255 if (blocking_case(kind, RELEASE) || blocking_case(kind, INTERRUPT) ||
256 blocking_case(kind, REPLACE)) {
261 if (!__atomic_load_n(&signal_count, __ATOMIC_RELAXED) || blocking_case(FL_CLASSIC, CANCEL) ||
265 if (sigaction(SIGUSR1, &original, NULL))
268 if (mask_changed && pthread_sigmask(SIG_SETMASK, &previous, NULL))