mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-01 12:03:41 +00:00
f7ff77d865
- Invent iso8601us() for faster timestamps - Improve --strace descriptions of sigset_t - Rebuild the Landlock Make bootstrap binary - Introduce MODE=sysv for non-Windows builds - Permit OFD fcntl() locks under pledge(flock) - redbean can now protect your kernel from ddos - Have vfork() fallback to sys_fork() not fork() - Change kmalloc() to not die when out of memory - Improve documentation for some termios functions - Rewrite putenv() and friends to conform to POSIX - Fix linenoise + strace verbosity issue on Windows - Fix regressions in our ability to show backtraces - Change redbean SetHeader() to no-op if value is nil - Improve fcntl() so SQLite locks work in non-WAL mode - Remove some unnecessary work during fork() on Windows - Create redbean-based SSL reverse proxy for IPv4 TurfWar - Fix ape/apeinstall.sh warning when using non-bash shells - Add ProgramTrustedIp(), and IsTrustedIp() APIs to redbean - Support $PWD, $UID, $GID, and $EUID in command interpreter - Introduce experimental JTqFpD APE prefix for non-Windows builds - Invent blackhole daemon for firewalling IP addresses via UNIX named socket - Add ProgramTokenBucket(), AcquireToken(), and CountTokens() APIs to redbean
186 lines
5.6 KiB
C
186 lines
5.6 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/struct/flock.h"
|
|
#include "libc/dce.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/intrin/kprintf.h"
|
|
#include "libc/macros.internal.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/sysv/consts/f.h"
|
|
#include "libc/sysv/consts/o.h"
|
|
#include "libc/testlib/testlib.h"
|
|
#include "libc/thread/thread.h"
|
|
|
|
/**
|
|
* @fileoverview POSIX Advisory Locks Test
|
|
*/
|
|
|
|
#define PROCESSES 8
|
|
#define THREADS 0 // <-- consider F_OFD_* locks
|
|
#define RATIO 3
|
|
#define ITERATIONS 10
|
|
|
|
char testlib_enable_tmp_setup_teardown;
|
|
|
|
_Thread_local const char *kind;
|
|
|
|
void Log(const char *fmt, ...) {
|
|
#if 0
|
|
va_list va;
|
|
char b[512];
|
|
int i, n = sizeof(b);
|
|
va_start(va, fmt);
|
|
i = ksnprintf(b, n, "%s pid=%d tid=%d ", kind, getpid(), gettid());
|
|
i += kvsnprintf(b + i, MAX(0, n - i), fmt, va);
|
|
i += ksnprintf(b + i, MAX(0, n - i), "\n");
|
|
write(2, b, MIN(i, n));
|
|
va_end(va);
|
|
#endif
|
|
}
|
|
|
|
void Lock(int fd, int type, long start, long len) {
|
|
int e;
|
|
struct flock lock = {
|
|
.l_type = type,
|
|
.l_whence = SEEK_SET,
|
|
.l_start = start,
|
|
.l_len = len,
|
|
};
|
|
e = errno;
|
|
while (fcntl(fd, F_SETLK, &lock)) {
|
|
ASSERT_TRUE(errno == EAGAIN || errno == EACCES);
|
|
errno = e;
|
|
Log("flock spinning on %d", fd);
|
|
}
|
|
}
|
|
|
|
void WriteLock(int fd, long start, long len) {
|
|
Lock(fd, F_WRLCK, start, len);
|
|
Log("acquired write lock on %d", fd);
|
|
}
|
|
|
|
void ReadLock(int fd, long start, long len) {
|
|
Lock(fd, F_RDLCK, start, len);
|
|
Log("acquired read lock on %d", fd);
|
|
}
|
|
|
|
void Unlock(int fd, long start, long len) {
|
|
Lock(fd, F_UNLCK, start, len);
|
|
Log("released lock on %d", fd);
|
|
}
|
|
|
|
void *Reader(void *arg) {
|
|
int i, j, fd;
|
|
char buf[10];
|
|
kind = arg;
|
|
ASSERT_NE(-1, (fd = open("db", O_RDONLY)));
|
|
for (j = 0; j < ITERATIONS; ++j) {
|
|
ReadLock(fd, 10, 10);
|
|
for (i = 0; i < 10; ++i) {
|
|
ASSERT_SYS(0, 1, pread(fd, buf + i, 1, 10 + i));
|
|
ASSERT_EQ(buf[0], buf[i]);
|
|
}
|
|
Unlock(fd, 10, 10);
|
|
sched_yield();
|
|
}
|
|
ASSERT_SYS(0, 0, close(fd));
|
|
return 0;
|
|
}
|
|
|
|
void *Writer(void *arg) {
|
|
int i, j, fd;
|
|
char buf[10];
|
|
kind = arg;
|
|
ASSERT_NE(-1, (fd = open("db", O_RDWR)));
|
|
for (j = 0; j < ITERATIONS; ++j) {
|
|
WriteLock(fd, 10, 10);
|
|
for (i = 0; i < 10; ++i) {
|
|
ASSERT_SYS(0, 1, pread(fd, buf + i, 1, 10 + i));
|
|
ASSERT_EQ(buf[0], buf[i]);
|
|
}
|
|
for (i = 0; i < 10; ++i) {
|
|
buf[i]++;
|
|
}
|
|
for (i = 0; i < 10; ++i) {
|
|
ASSERT_SYS(0, 1, pwrite(fd, buf + i, 1, 10 + i));
|
|
}
|
|
Unlock(fd, 10, 10);
|
|
sched_yield();
|
|
}
|
|
ASSERT_SYS(0, 0, close(fd));
|
|
return 0;
|
|
}
|
|
|
|
TEST(posixAdvisoryLocks, threadsAndProcessesFightingForLock) {
|
|
int i, rc, pid, fd, ws;
|
|
pthread_t th[THREADS + 1];
|
|
|
|
ASSERT_SYS(0, 3, creat("db", 0644));
|
|
ASSERT_SYS(0, 0, ftruncate(3, 30));
|
|
ASSERT_SYS(0, 0, close(3));
|
|
|
|
for (i = 0; i < THREADS; ++i) {
|
|
if (i % RATIO == 0) {
|
|
ASSERT_EQ(0, pthread_create(th + i, 0, Reader, "reader thread"));
|
|
} else {
|
|
ASSERT_EQ(0, pthread_create(th + i, 0, Writer, "writer thread"));
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < PROCESSES; ++i) {
|
|
ASSERT_NE(-1, (rc = fork()));
|
|
if (!rc) {
|
|
if (i % RATIO == 0) {
|
|
Writer("writer process");
|
|
} else {
|
|
Reader("reader process");
|
|
}
|
|
_Exit(0);
|
|
}
|
|
}
|
|
|
|
ASSERT_NE(-1, (fd = open("db", O_RDWR)));
|
|
Lock(fd, F_WRLCK, 0, 10);
|
|
Lock(fd, F_WRLCK, 20, 10);
|
|
|
|
for (i = 0; i < THREADS; ++i) {
|
|
ASSERT_EQ(0, pthread_join(th[i], 0));
|
|
}
|
|
|
|
kind = "main process";
|
|
for (;;) {
|
|
int e = errno;
|
|
if ((pid = waitpid(0, &ws, 0)) != -1) {
|
|
if (WIFSIGNALED(ws)) {
|
|
Log("process %d terminated with %G", pid, WTERMSIG(ws));
|
|
testlib_incrementfailed();
|
|
} else if (WEXITSTATUS(ws)) {
|
|
Log("process %d exited with %d", pid, WEXITSTATUS(ws));
|
|
testlib_incrementfailed();
|
|
}
|
|
} else {
|
|
ASSERT_EQ(ECHILD, errno);
|
|
errno = e;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT_SYS(0, 0, close(fd));
|
|
}
|