14 return clock_gettime(CLOCK_MONOTONIC, &now) ? -1 : (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
16void fl_pause(
int milliseconds) {
17 struct timespec pause = {milliseconds / 1000, (milliseconds % 1000) * 1000000L};
18 while (nanosleep(&pause, &pause) && errno == EINTR) {
21int fl_wait(
volatile int* flag,
int milliseconds) {
22 const int64_t until = fl_now() + (int64_t)milliseconds * 1000000;
23 while (!__atomic_load_n(flag, __ATOMIC_ACQUIRE)) {
24 if (fl_now() >= until)
30int fl_reap(pid_t child,
int milliseconds) {
31 const int64_t until = fl_now() + (int64_t)milliseconds * 1000000;
32 while (fl_now() < until) {
34 pid_t result = waitpid(child, &status, WNOHANG);
36 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
37 if (result < 0 && errno != EINTR)
41 fprintf(stderr,
"FILE-LOCK-CONTRACT: child=%ld timeout\n", (
long)child);
43 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
47int fl_byte(
int socket,
char expected) {
48 struct pollfd entry = {.fd = socket, .events = POLLIN};
51 result = poll(&entry, 1, 5000);
52 while (result < 0 && errno == EINTR);
54 return result > 0 && read(socket, &
byte, 1) == 1 &&
byte == expected ? 0 : -1;
56int fl_send_fd(
int socket,
int fd) {
58 struct iovec vector = {&byte, 1};
61 char bytes[CMSG_SPACE(
sizeof(
int))];
63 struct msghdr
message = {.msg_iov = &vector,
65 .msg_control = control.bytes,
66 .msg_controllen =
sizeof(control.bytes)};
67 struct cmsghdr* item = CMSG_FIRSTHDR(&
message);
68 item->cmsg_level = SOL_SOCKET;
69 item->cmsg_type = SCM_RIGHTS;
70 item->cmsg_len = CMSG_LEN(
sizeof(
int));
71 memcpy(CMSG_DATA(item), &fd,
sizeof(fd));
72 return sendmsg(socket, &
message, 0) == 1 ? 0 : -1;
74int fl_receive_fd(
int socket) {
76 struct iovec vector = {&byte, 1};
79 char bytes[CMSG_SPACE(
sizeof(
int))];
81 struct msghdr
message = {.msg_iov = &vector,
83 .msg_control = control.bytes,
84 .msg_controllen =
sizeof(control.bytes)};
85 if (recvmsg(socket, &
message, MSG_CMSG_CLOEXEC) != 1 || (
message.msg_flags & MSG_CTRUNC))
87 struct cmsghdr* item = CMSG_FIRSTHDR(&
message);
88 if (!item || item->cmsg_level != SOL_SOCKET || item->cmsg_type != SCM_RIGHTS ||
89 item->cmsg_len != CMSG_LEN(
sizeof(
int)))
92 memcpy(&fd, CMSG_DATA(item),
sizeof(fd));
95int fl_file(
char path[128],
const char* directory) {
96 static unsigned sequence;
97 snprintf(path, 128,
"%s/file-lock-%ld-%u", directory, (
long)getpid(), ++sequence);
98 int fd = open(path, O_CREAT | O_EXCL | O_RDWR, 0600);
99 if (fd >= 0 && ftruncate(fd, 128)) {
106int fl_record(
int fd,
int command,
short type,
short whence, off_t start, off_t length) {
107 struct flock lock = {.l_type = type, .l_whence = whence, .l_start = start, .l_len = length};
108 return fcntl(fd, command, &lock);
110int fl_lock(
int fd,
int kind,
short type,
int blocking) {
111 if (kind == FL_FLOCK) {
112 int operation = type == F_UNLCK ? LOCK_UN : type == F_RDLCK ? LOCK_SH : LOCK_EX;
113 return flock(fd, operation | (blocking ? 0 : LOCK_NB));
116 kind == FL_CLASSIC ? (blocking ? F_SETLKW : F_SETLK)
117 : (blocking ? F_OFD_SETLKW : F_OFD_SETLK),
118 type, SEEK_SET, 0, 0);
120int fl_query(
int fd,
int command, off_t start, off_t length,
short type, off_t expected_start,
121 off_t expected_length, pid_t owner) {
122 struct flock lock = {.l_type = F_WRLCK, .l_whence = SEEK_SET, .l_start = start, .l_len = length};
123 if (fcntl(fd, command, &lock))
125 if (lock.l_type == type &&
126 (type == F_UNLCK || (lock.l_whence == SEEK_SET && lock.l_start == expected_start &&
127 lock.l_len == expected_length && lock.l_pid == owner)))
129 fprintf(stderr,
"FILE-LOCK-CONTRACT: GETLK type=%d whence=%d range=%lld/%lld pid=%ld\n",
130 lock.l_type, lock.l_whence, (
long long)lock.l_start, (
long long)lock.l_len,
134static int run(
const char* name,
int (*test)(
void)) {
135 printf(
"FILE-LOCK-CONTRACT: BEGIN %s\n", name);
137 pid_t child = fork();
145 _exit(result ? 1 : 0);
147 const int status = fl_reap(child, 45000);
148 printf(
"FILE-LOCK-CONTRACT: %s %s status=%d\n", status ?
"FAIL" :
"PASS", name, status);
152int main(
int argc,
char** argv) {
153 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
155 if (argc > 1 && !strcmp(argv[1],
"lock-exec"))
156 return file_lock_exec(argc, argv);
160 } suites[] = {{
"flock", file_lock_flock},
161 {
"records", file_lock_records},
162 {
"lifetime", file_lock_lifetime},
163 {
"blocking", file_lock_blocking},
164 {
"creation", file_lock_creation}};
166 for (
unsigned n = 0; n <
sizeof(suites) /
sizeof(suites[0]); ++n) {
167 if (argc > 1 && strcmp(argv[1], suites[n].name))
170 if (run(suites[n].name, suites[n].test))
175 puts(
"FILE-LOCK-CONTRACT: END PASS");