Improve cancellations, randomness, and time

- Exhaustively document cancellation points
- Rename SIGCANCEL to SIGTHR just like BSDs
- Further improve POSIX thread cancellations
- Ensure asynchronous cancellations work correctly
- Elevate the quality of getrandom() and getentropy()
- Make futexes cancel correctly on OpenBSD 6.x and 7.x
- Add reboot.com and shutdown.com to examples directory
- Remove underscore prefix from awesome timespec_*() APIs
- Create assertions that help verify our cancellation points
- Remove bad timespec APIs (cmp generalizes eq/ne/gt/gte/lt/lte)
This commit is contained in:
Justine Tunney 2022-11-05 19:49:41 -07:00
parent 0d7c265392
commit 3f0bcdc3ef
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
173 changed files with 1599 additions and 782 deletions

View file

@ -20,22 +20,22 @@ int main(int argc, char *argv[]) {
volatile unsigned long x;
struct timespec now, start, next, interval;
printf("hammering the cpu...\n");
next = start = _timespec_mono();
interval = _timespec_frommillis(500);
next = _timespec_add(next, interval);
next = start = timespec_mono();
interval = timespec_frommillis(500);
next = timespec_add(next, interval);
for (;;) {
for (i = 0;; ++i) {
x *= 7;
if (!(i % 256)) {
now = _timespec_mono();
if (_timespec_gte(now, next)) {
now = timespec_mono();
if (timespec_cmp(now, next) >= 0) {
break;
}
}
}
next = _timespec_add(next, interval);
next = timespec_add(next, interval);
printf("consumed %10g seconds monotonic time and %10g seconds cpu time\n",
_timespec_tonanos(_timespec_sub(now, start)) / 1000000000.,
timespec_tonanos(timespec_sub(now, start)) / 1000000000.,
(double)clock() / CLOCKS_PER_SEC);
}
}

View file

@ -28,7 +28,7 @@ void show(int clock) {
}
shown[n++] = clock;
if (clock_getres(clock, &ts) != -1) {
kprintf("%s %'ld ns\n", DescribeClockName(clock), _timespec_tonanos(ts));
kprintf("%s %'ld ns\n", DescribeClockName(clock), timespec_tonanos(ts));
} else {
kprintf("%s %s\n", DescribeClockName(clock), _strerrno(errno));
}

39
examples/reboot.c Normal file
View file

@ -0,0 +1,39 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/reboot.h"
int main(int argc, char *argv[]) {
char line[8] = {0};
if (argc > 1 && !strcmp(argv[1], "-y")) {
line[0] = 'y';
} else {
printf("reboot your computer? yes or no [no] ");
fflush(stdout);
fgets(line, sizeof(line), stdin);
}
if (line[0] == 'y' || line[0] == 'Y') {
if (reboot(RB_AUTOBOOT)) {
printf("system is rebooting...\n");
exit(0);
} else {
perror("reboot");
exit(1);
}
} else if (line[0] == 'n' || line[0] == 'N') {
exit(0);
} else {
printf("error: unrecognized response\n");
exit(2);
}
}

39
examples/shutdown.c Normal file
View file

@ -0,0 +1,39 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/reboot.h"
int main(int argc, char *argv[]) {
char line[8] = {0};
if (argc > 1 && !strcmp(argv[1], "-y")) {
line[0] = 'y';
} else {
printf("shutdown your computer? yes or no [no] ");
fflush(stdout);
fgets(line, sizeof(line), stdin);
}
if (line[0] == 'y' || line[0] == 'Y') {
if (reboot(RB_POWER_OFF)) {
printf("system is shutting down...\n");
exit(0);
} else {
perror("reboot");
exit(1);
}
} else if (line[0] == 'n' || line[0] == 'N') {
exit(0);
} else {
printf("error: unrecognized response\n");
exit(2);
}
}

View file

@ -1,26 +0,0 @@
/*-*- 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/struct/timespec.h"
/**
* Checks if 𝑥 = 𝑦.
*/
bool _timespec_eq(struct timespec x, struct timespec y) {
return x.tv_sec == y.tv_sec && x.tv_nsec == y.tv_nsec;
}

View file

@ -1,34 +0,0 @@
/*-*- 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/struct/timespec.h"
/**
* Returns true if timespec `x` is greater than `y`.
*/
bool _timespec_gt(struct timespec x, struct timespec y) {
if (x.tv_sec > y.tv_sec) {
return true;
}
if (x.tv_sec == y.tv_sec) {
if (x.tv_nsec > y.tv_nsec) {
return true;
}
}
return false;
}

View file

@ -1,28 +0,0 @@
/*-*- 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/struct/timespec.h"
/**
* Checks if 𝑥 𝑦.
*/
bool _timespec_gte(struct timespec x, struct timespec y) {
if (x.tv_sec > y.tv_sec) return true;
if (x.tv_sec < y.tv_sec) return false;
return x.tv_nsec >= y.tv_nsec;
}

View file

@ -1,26 +0,0 @@
/*-*- 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/struct/timeval.h"
/**
* Checks if 𝑥 = 𝑦.
*/
bool _timeval_eq(struct timeval x, struct timeval y) {
return x.tv_sec == y.tv_sec && x.tv_usec == y.tv_usec;
}

View file

@ -1,28 +0,0 @@
/*-*- 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/struct/timeval.h"
/**
* Checks if 𝑥 𝑦.
*/
bool _timeval_gte(struct timeval x, struct timeval y) {
if (x.tv_sec > y.tv_sec) return true;
if (x.tv_sec < y.tv_sec) return false;
return x.tv_usec >= y.tv_usec;
}

View file

@ -7,13 +7,16 @@ COSMOPOLITAN_C_START_
#define BLOCK_CANCELLATIONS \
do { \
int _CancelState; \
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &_CancelState)
_CancelState = _pthread_block_cancellations()
#define ALLOW_CANCELLATIONS \
pthread_setcancelstate(_CancelState, 0); \
_pthread_allow_cancellations(_CancelState); \
} \
while (0)
int _pthread_block_cancellations(void);
void _pthread_allow_cancellations(int);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_BLOCKCANCEL_H_ */

View file

@ -179,12 +179,12 @@ o//libc/calls/statfs2cosmo.o: private \
# we always want -O2 because:
# division is expensive if not optimized
o/$(MODE)/libc/calls/clock.o \
o/$(MODE)/libc/calls/_timespec_tomillis.o \
o/$(MODE)/libc/calls/_timespec_tomicros.o \
o/$(MODE)/libc/calls/_timespec_totimeval.o \
o/$(MODE)/libc/calls/_timespec_fromnanos.o \
o/$(MODE)/libc/calls/_timespec_frommillis.o \
o/$(MODE)/libc/calls/_timespec_frommicros.o: private \
o/$(MODE)/libc/calls/timespec_tomillis.o \
o/$(MODE)/libc/calls/timespec_tomicros.o \
o/$(MODE)/libc/calls/timespec_totimeval.o \
o/$(MODE)/libc/calls/timespec_fromnanos.o \
o/$(MODE)/libc/calls/timespec_frommillis.o \
o/$(MODE)/libc/calls/timespec_frommicros.o: private \
OVERRIDE_CFLAGS += \
-O2

View file

@ -55,7 +55,7 @@ int64_t clock(void) {
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) == -1) {
errno = e;
if (getrusage(RUSAGE_SELF, &ru) != -1) {
ts = _timeval_totimespec(_timeval_add(ru.ru_utime, ru.ru_stime));
ts = timeval_totimespec(timeval_add(ru.ru_utime, ru.ru_stime));
} else {
return -1;
}

View file

@ -17,11 +17,11 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/timespec.h"
#include "libc/thread/thread.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/sysv/consts/clock.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
static struct {
pthread_once_t once;
@ -30,7 +30,7 @@ static struct {
} g_mono;
static void sys_clock_gettime_mono_init(void) {
g_mono.base_wall = _timespec_real();
g_mono.base_wall = timespec_real();
g_mono.base_tick = rdtsc();
}
@ -42,7 +42,7 @@ int sys_clock_gettime_mono(struct timespec *time) {
pthread_once(&g_mono.once, sys_clock_gettime_mono_init);
cycles = rdtsc() - g_mono.base_tick;
nanos = cycles / 3;
*time = _timespec_add(g_mono.base_wall, _timespec_fromnanos(nanos));
*time = timespec_add(g_mono.base_wall, timespec_fromnanos(nanos));
return 0;
} else {
return einval();

View file

@ -33,24 +33,24 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
abs = *req;
for (;;) {
if (sys_clock_gettime_nt(clock, &now)) return -1;
if (_timespec_gte(now, abs)) return 0;
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(false, g_fds.p)) return -1;
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
_timespec_tomillis(_timespec_sub(abs, now))),
timespec_tomillis(timespec_sub(abs, now))),
false);
}
} else {
if (sys_clock_gettime_nt(clock, &now)) return -1;
abs = _timespec_add(now, *req);
abs = timespec_add(now, *req);
for (;;) {
sys_clock_gettime_nt(clock, &now);
if (_timespec_gte(now, abs)) return 0;
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(false, g_fds.p)) {
if (rem) *rem = _timespec_sub(abs, now);
if (rem) *rem = timespec_sub(abs, now);
return -1;
}
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
_timespec_tomillis(_timespec_sub(abs, now))),
timespec_tomillis(timespec_sub(abs, now))),
false);
}
}

View file

@ -31,8 +31,8 @@ int sys_clock_nanosleep_openbsd(int clock, int flags,
res = sys_nanosleep(req, rem);
} else {
sys_clock_gettime(clock, &now);
if (_timespec_gt(*req, now)) {
rel = _timespec_sub(*req, now);
if (timespec_cmp(*req, now) > 0) {
rel = timespec_sub(*req, now);
res = sys_nanosleep(&rel, 0);
} else {
res = 0;

View file

@ -31,10 +31,10 @@ int sys_clock_nanosleep_xnu(int clock, int flags, const struct timespec *req,
struct timeval now, abs, rel;
if (clock == CLOCK_REALTIME) {
if (flags & TIMER_ABSTIME) {
abs = _timespec_totimeval(*req);
abs = timespec_totimeval(*req);
sys_gettimeofday_xnu(&now, 0, 0);
if (_timeval_gt(abs, now)) {
rel = _timeval_sub(abs, now);
if (timeval_cmp(abs, now) > 0) {
rel = timeval_sub(abs, now);
res = sys_select(0, 0, 0, 0, &rel);
} else {
res = 0;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/asan.internal.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
@ -55,8 +56,8 @@
*
* struct timespec rel, now, abs;
* clock_gettime(CLOCK_REALTIME, &now);
* rel = _timespec_frommillis(100);
* abs = _timespec_add(now, rel);
* rel = timespec_frommillis(100);
* abs = timespec_add(now, rel);
* while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &abs, 0));
*
* will accurately spin on `EINTR` errors. That way you're not impeding
@ -74,6 +75,7 @@
* if flags is `TIMER_ABSTIME` then `rem` is ignored
* @return 0 on success, or errno on error
* @raise EINTR when a signal got delivered while we were waiting
* @raise ECANCELED if thread was cancelled in masked mode
* @raise ENOTSUP if `clock` is known but we can't use it here
* @raise EFAULT if `req` or null or bad memory was passed
* @raise EINVAL if `clock` is unknown to current platform
@ -87,6 +89,7 @@
errno_t clock_nanosleep(int clock, int flags, const struct timespec *req,
struct timespec *rem) {
int rc, e = errno;
BEGIN_CANCELLATION_POINT;
if (!req || (IsAsan() && (!__asan_is_valid_timespec(req) ||
(rem && !__asan_is_valid_timespec(rem))))) {
@ -113,6 +116,8 @@ errno_t clock_nanosleep(int clock, int flags, const struct timespec *req,
errno = e;
}
END_CANCELLATION_POINT;
#if SYSDEBUG
if (__tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
STRACE("clock_nanosleep(%s, %s, %s, [%s]) → %s", DescribeClockName(clock),

View file

@ -37,6 +37,7 @@
* @raise EBADF on OpenBSD if `first` is greater than highest fd
* @raise EINVAL if flags are bad or first is greater than last
* @raise EMFILE if a weird race condition happens on Linux
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR possibly on OpenBSD
* @raise ENOMEM on Linux maybe
*/

View file

@ -16,7 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
@ -39,6 +41,7 @@ static bool HasCopyFileRange(void) {
bool ok;
int e, rc;
e = errno;
BLOCK_CANCELLATIONS;
if (IsLinux()) {
// We modernize our detection by a few years for simplicity.
// This system call is chosen since it's listed by pledge().
@ -49,6 +52,7 @@ static bool HasCopyFileRange(void) {
} else {
ok = false;
}
ALLOW_CANCELLATIONS;
errno = e;
return ok;
}
@ -81,6 +85,7 @@ static void copy_file_range_init(void) {
* @raise EBADF if `infd` or `outfd` aren't open files or append-only
* @raise EPERM if `fdout` refers to an immutable file on Linux
* @raise ENOTSUP if `infd` or `outfd` is a zip file descriptor
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINVAL if ranges overlap or `flags` is non-zero
* @raise EFBIG if `setrlimit(RLIMIT_FSIZE)` is exceeded
* @raise EFAULT if one of the pointers memory is bad
@ -93,12 +98,15 @@ static void copy_file_range_init(void) {
* @raise EIO if a low-level i/o error happens
* @see sendfile() for seekable socket
* @see splice() for fd pipe
* @cancellationpoint
*/
ssize_t copy_file_range(int infd, int64_t *opt_in_out_inoffset, int outfd,
int64_t *opt_in_out_outoffset, size_t uptobytes,
uint32_t flags) {
ssize_t rc;
pthread_once(&g_copy_file_range.once, copy_file_range_init);
BEGIN_CANCELLATION_POINT;
if (!g_copy_file_range.ok) {
rc = enosys();
} else if (IsAsan() && ((opt_in_out_inoffset &&
@ -112,6 +120,8 @@ ssize_t copy_file_range(int infd, int64_t *opt_in_out_inoffset, int outfd,
rc = sys_copy_file_range(infd, opt_in_out_inoffset, outfd,
opt_in_out_outoffset, uptobytes, flags);
}
END_CANCELLATION_POINT;
STRACE("copy_file_range(%d, %s, %d, %s, %'zu, %#x) → %'ld% m", infd,
DescribeInOutInt64(rc, opt_in_out_inoffset), outfd,
DescribeInOutInt64(rc, opt_in_out_outoffset), uptobytes, flags);

View file

@ -16,19 +16,44 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/timeval.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/cp.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/tls.h"
#ifdef MODE_DBG
/**
* Returns true if timeval `x` is greater than `y`.
*/
bool _timeval_gt(struct timeval x, struct timeval y) {
if (x.tv_sec > y.tv_sec) {
return true;
}
if (x.tv_sec == y.tv_sec) {
if (x.tv_usec > y.tv_usec) {
return true;
int begin_cancellation_point(void) {
int state = 0;
struct CosmoTib *tib;
struct PosixThread *pt;
if (__enable_tls) {
tib = __get_tls();
if ((pt = (struct PosixThread *)tib->tib_pthread)) {
state = pt->flags & PT_INCANCEL;
pt->flags |= PT_INCANCEL;
}
}
return false;
return state;
}
void end_cancellation_point(int state) {
struct CosmoTib *tib;
struct PosixThread *pt;
if (__enable_tls) {
tib = __get_tls();
if ((pt = (struct PosixThread *)tib->tib_pthread)) {
pt->flags &= ~PT_INCANCEL;
pt->flags |= state;
}
}
}
void report_cancellation_point(void) {
BLOCK_CANCELLATIONS;
_bt("error: need BEGIN/END_CANCELLATION_POINT\n");
ALLOW_CANCELLATIONS;
}
#endif /* MODE_DBG */

25
libc/calls/cp.internal.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_CP_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_CP_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int begin_cancellation_point(void);
void end_cancellation_point(int);
#ifndef MODE_DBG
#define BEGIN_CANCELLATION_POINT (void)0
#define END_CANCELLATION_POINT (void)0
#else
#define BEGIN_CANCELLATION_POINT \
do { \
int _Cp; \
_Cp = begin_cancellation_point()
#define END_CANCELLATION_POINT \
end_cancellation_point(_Cp); \
} \
while (0)
#endif
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_CP_INTERNAL_H_ */

View file

@ -31,6 +31,7 @@
* @param mode is octal bits, e.g. 0644 usually
* @return file descriptor, or -1 w/ errno
* @see openat() for further documentation
* @cancellationpoint
* @asyncsignalsafe
* @restartable
* @threadsafe

View file

@ -31,13 +31,11 @@
// Implements dup(), dup2(), dup3(), and F_DUPFD for Windows.
textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) {
int64_t rc, proc, handle;
_unassert(oldfd >= 0);
_unassert(newfd >= -1);
_unassert(!(flags & ~O_CLOEXEC));
__fds_lock();
if (oldfd >= g_fds.n ||
if (!__isfdopen(oldfd) || newfd < -1 ||
(g_fds.p[oldfd].kind != kFdFile && g_fds.p[oldfd].kind != kFdSocket &&
g_fds.p[oldfd].kind != kFdConsole)) {
__fds_unlock();

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -40,6 +41,8 @@ static bool IsApeBinary(const char *path) {
int fd;
char buf[8];
bool res = false;
// TODO(jart): Should we block signals too?
BLOCK_CANCELLATIONS;
if ((fd = sys_open(path, O_RDONLY, 0)) != -1) {
if (sys_read(fd, buf, 8) == 8 && //
(READ64LE(buf) == READ64LE("MZqFpD='") ||
@ -48,6 +51,7 @@ static bool IsApeBinary(const char *path) {
}
sys_close(fd);
}
ALLOW_CANCELLATIONS;
return res;
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/flock.h"
#include "libc/calls/struct/flock.internal.h"
@ -94,7 +95,7 @@
* @raise EDEADLK if `cmd` was `F_SETLKW` and waiting would deadlock
* @raise EMFILE if `cmd` is `F_DUPFD` or `F_DUPFD_CLOEXEC` and
* `RLIMIT_NOFILE` would be exceeded
* @cancellationpoint when `cmd` is `F_SETLKW`
* @cancellationpoint when `cmd` is `F_SETLKW` or `F_OFD_SETLKW`
* @asyncsignalsafe
* @restartable
*/
@ -113,7 +114,9 @@ int fcntl(int fd, int cmd, ...) {
rc = _weaken(__zipos_fcntl)(fd, cmd, arg);
} else if (!IsWindows()) {
if (cmd == F_SETLKW || cmd == F_OFD_SETLKW) {
BEGIN_CANCELLATION_POINT;
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl_cp);
END_CANCELLATION_POINT;
} else {
rc = sys_fcntl(fd, cmd, arg, __sys_fcntl);
}

View file

@ -23,5 +23,6 @@
textwindows int sys_fdatasync_nt(int fd) {
// TODO(jart): what should we do with worker pipes?
if (!__isfdkind(fd, kFdFile)) return ebadf();
if (_check_interrupts(false, 0)) return -1;
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : -1;
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -30,6 +31,8 @@
* @return 0 on success, or -1 w/ errno
* @see sync(), fsync(), sync_file_range()
* @see __nosync to secretly disable
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
* @cancellationpoint
* @asyncsignalsafe
*/
@ -37,11 +40,13 @@ int fdatasync(int fd) {
int rc;
struct stat st;
if (__nosync != 0x5453455454534146) {
BEGIN_CANCELLATION_POINT;
if (!IsWindows()) {
rc = sys_fdatasync(fd);
} else {
rc = sys_fdatasync_nt(fd);
}
END_CANCELLATION_POINT;
STRACE("fdatasync(%d) → %d% m", fd, rc);
} else {
rc = fstat(fd, &st);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
@ -35,11 +36,15 @@
*/
int flock(int fd, int op) {
int rc;
BEGIN_CANCELLATION_POINT;
if (!IsWindows()) {
rc = sys_flock(fd, op);
} else {
rc = sys_flock_nt(fd, op);
}
END_CANCELLATION_POINT;
STRACE("flock(%d, %d) → %d% m", fd, op, rc);
return rc;
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/statfs-meta.internal.h"
#include "libc/calls/struct/statfs.internal.h"
@ -33,7 +34,9 @@
int fstatfs(int fd, struct statfs *sf) {
int rc;
union statfs_meta m;
BEGIN_CANCELLATION_POINT;
CheckLargeStackAllocation(&m, sizeof(m));
if (!IsWindows()) {
if ((rc = sys_fstatfs(fd, &m)) != -1) {
statfs2cosmo(sf, &m);
@ -43,6 +46,8 @@ int fstatfs(int fd, struct statfs *sf) {
} else {
rc = ebadf();
}
END_CANCELLATION_POINT;
STRACE("fstatfs(%d, [%s]) → %d% m", fd, DescribeStatfs(rc, sf));
return rc;
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -28,6 +29,8 @@
* Blocks until kernel flushes buffers for fd to disk.
*
* @return 0 on success, or -1 w/ errno
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
* @see fdatasync(), sync_file_range()
* @see __nosync to secretly disable
* @cancellationpoint
@ -37,11 +40,13 @@ int fsync(int fd) {
int rc;
struct stat st;
if (__nosync != 0x5453455454534146) {
BEGIN_CANCELLATION_POINT;
if (!IsWindows()) {
rc = sys_fsync(fd);
} else {
rc = sys_fdatasync_nt(fd);
}
END_CANCELLATION_POINT;
STRACE("fysnc(%d) → %d% m", fd, rc);
} else {
rc = fstat(fd, &st);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -49,6 +50,7 @@
* @return 0 on success, or -1 w/ errno
* @raise EINVAL if `length` is negative
* @raise EINTR if signal was delivered instead
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EIO if a low-level i/o error happened
* @raise EFBIG or EINVAL if `length` is too huge
* @raise ENOTSUP if `fd` is a zip file descriptor
@ -62,6 +64,8 @@
*/
int ftruncate(int fd, int64_t length) {
int rc;
BEGIN_CANCELLATION_POINT;
if (fd < 0) {
rc = ebadf();
} else if (__isfdkind(fd, kFdZip)) {
@ -78,6 +82,8 @@ int ftruncate(int fd, int64_t length) {
} else {
rc = ebadf();
}
END_CANCELLATION_POINT;
STRACE("ftruncate(%d, %'ld) → %d% m", fd, length, rc);
return rc;
}

View file

@ -42,7 +42,7 @@ char *sys_getcwd_xnu(char *res, size_t size) {
int fd;
union metastat st[2];
char buf[XNU_MAXPATHLEN], *ret = NULL;
if ((fd = sys_openat(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY, 0)) != -1) {
if ((fd = __sys_openat_nc(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY, 0)) != -1) {
if (__sys_fstat(fd, &st[0]) != -1) {
if (st[0].xnu.st_dev && st[0].xnu.st_ino) {
if (__sys_fcntl(fd, XNU_F_GETPATH, (uintptr_t)buf) != -1) {

View file

@ -29,7 +29,7 @@ int sys_nanosleep_xnu(const struct timespec *req, struct timespec *rem) {
int rc;
struct timeval wt, t1, t2, td;
if (rem) sys_gettimeofday_xnu(&t1, 0, 0);
wt = _timespec_totimeval(*req); // rounds up
wt = timespec_totimeval(*req); // rounds up
rc = sys_select(0, 0, 0, 0, &wt);
if (rem) {
if (!rc) {
@ -39,12 +39,12 @@ int sys_nanosleep_xnu(const struct timespec *req, struct timespec *rem) {
// xnu select() doesn't modify timeout
// so we need, yet another system call
sys_gettimeofday_xnu(&t2, 0, 0);
td = _timeval_sub(t2, t1);
if (_timeval_gte(td, wt)) {
td = timeval_sub(t2, t1);
if (timeval_cmp(td, wt) >= 0) {
rem->tv_sec = 0;
rem->tv_nsec = 0;
} else {
*rem = _timeval_totimespec(_timeval_sub(wt, td));
*rem = timeval_totimespec(timeval_sub(wt, td));
}
}
}

View file

@ -28,6 +28,7 @@
* time when -1 w/ `EINTR` is returned otherwise `rem` is undefined
* @return 0 on success, or -1 w/ errno
* @raise EINVAL if `req->tv_nsec [0,1000000000)`
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if a signal was delivered and `rem` is updated
* @raise EFAULT if `req` is NULL or `req` / `rem` is a bad pointer
* @raise ENOSYS on bare metal

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall-nt.internal.h"
@ -130,6 +131,7 @@
* @raise ENOTSUP if `file` is on zip file system and process is vfork()'d
* @raise ENOSPC if file system is full when `file` would be `O_CREAT`ed
* @raise EINTR if we needed to block and a signal was delivered instead
* @raise ECANCELED if thread was cancelled in masked mode
* @raise ENOENT if `file` doesn't exist when `O_CREAT` isn't in `flags`
* @raise ENOENT if `file` points to a string that's empty
* @raise ENOMEM if insufficient memory was available
@ -155,6 +157,8 @@ int openat(int dirfd, const char *file, int flags, ...) {
va_start(va, flags);
mode = va_arg(va, unsigned);
va_end(va);
BEGIN_CANCELLATION_POINT;
if (file && (!IsAsan() || __asan_is_valid_str(file))) {
if (!__isfdkind(dirfd, kFdZip)) {
if (_weaken(__zipos_open) &&
@ -177,6 +181,8 @@ int openat(int dirfd, const char *file, int flags, ...) {
} else {
rc = efault();
}
END_CANCELLATION_POINT;
STRACE("openat(%s, %#s, %s, %#o) → %d% m", DescribeDirfd(dirfd), file,
DescribeOpenFlags(flags), (flags & (O_CREAT | O_TMPFILE)) ? mode : 0,
rc);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
@ -44,6 +45,7 @@
int pause(void) {
int rc;
STRACE("pause() → [...]");
BEGIN_CANCELLATION_POINT;
if (!IsWindows()) {
// We'll polyfill pause() using select() with a null timeout, which
@ -64,6 +66,7 @@ int pause(void) {
rc = sys_pause_nt();
}
END_CANCELLATION_POINT;
STRACE("[...] pause → %d% m", rc);
return rc;
}

View file

@ -16,17 +16,19 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/strace.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
/**
* Creates file-less file descriptors for interprocess communication.
*
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached
* @raise ENFILE if system-wide file limit has been reached
* @param pipefd is used to return (reader, writer) file descriptors
* @param flags can have O_CLOEXEC or O_DIRECT or O_NONBLOCK
* @return 0 on success, or -1 w/ errno and pipefd isn't modified

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/cp.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/strace.internal.h"
@ -55,6 +56,8 @@
* @return fds[𝑖].revents is always zero initializaed and then will
* be populated with POLL{IN,OUT,PRI,HUP,ERR,NVAL} if something
* was determined about the file descriptor
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
* @cancellationpoint
* @asyncsignalsafe
* @threadsafe
@ -64,6 +67,7 @@ int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
int rc;
size_t n;
uint64_t millis;
BEGIN_CANCELLATION_POINT;
if (IsAsan() && (__builtin_mul_overflow(nfds, sizeof(struct pollfd), &n) ||
!__asan_is_valid(fds, n))) {
@ -79,6 +83,7 @@ int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
rc = sys_poll_nt(fds, nfds, &millis, 0);
}
END_CANCELLATION_POINT;
STRACE("poll(%s, %'zu, %'d) → %d% lm", DescribePollFds(rc, fds, nfds), nfds,
timeout_ms, rc);
return rc;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/cp.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
@ -50,6 +51,8 @@
*
* @param timeout if null will block indefinitely
* @param sigmask may be null in which case no mask change happens
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
* @cancellationpoint
* @asyncsignalsafe
* @threadsafe
@ -62,6 +65,7 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
uint64_t millis;
sigset_t oldmask;
struct timespec ts, *tsp;
BEGIN_CANCELLATION_POINT;
if (IsAsan() && (__builtin_mul_overflow(nfds, sizeof(struct pollfd), &n) ||
!__asan_is_valid(fds, n) ||
@ -96,6 +100,7 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
rc = sys_poll_nt(fds, nfds, &millis, sigmask);
}
END_CANCELLATION_POINT;
STRACE("ppoll(%s, %'zu, %s, %s) → %d% lm", DescribePollFds(rc, fds, nfds),
nfds, DescribeTimespec(0, timeout), DescribeSigset(0, sigmask), rc);
return rc;

View file

@ -18,6 +18,7 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
@ -47,6 +48,7 @@
* @raise EBADF if `fd` isn't an open file descriptor
* @raise EIO if a complicated i/o error happened
* @raise EINTR if signal was delivered instead
* @raise ECANCELED if thread was cancelled in masked mode
* @see pwrite(), write()
* @cancellationpoint
* @asyncsignalsafe
@ -55,6 +57,8 @@
*/
ssize_t pread(int fd, void *buf, size_t size, int64_t offset) {
ssize_t rc;
BEGIN_CANCELLATION_POINT;
if (offset < 0) {
rc = einval();
} else if (fd < 0) {
@ -73,6 +77,8 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) {
rc = ebadf();
}
_npassert(rc == -1 || (size_t)rc <= size);
END_CANCELLATION_POINT;
DATATRACE("pread(%d, [%#.*hhs%s], %'zu, %'zd) → %'zd% m", fd,
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, offset, rc);
return rc;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
@ -114,7 +115,9 @@ static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
*/
ssize_t preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
ssize_t rc;
BEGIN_CANCELLATION_POINT;
rc = Preadv(fd, iov, iovlen, off);
END_CANCELLATION_POINT;
STRACE("preadv(%d, [%s], %d, %'ld) → %'ld% m", fd,
DescribeIovec(rc, iov, iovlen), iovlen, off, rc);
return rc;

View file

@ -18,6 +18,7 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
@ -50,6 +51,8 @@
ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) {
ssize_t rc;
size_t wrote;
BEGIN_CANCELLATION_POINT;
if (offset < 0) {
rc = einval();
} else if (fd == -1) {
@ -71,6 +74,8 @@ ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) {
_npassert(wrote <= size);
}
}
END_CANCELLATION_POINT;
DATATRACE("pwrite(%d, %#.*hhs%s, %'zu, %'zd) → %'zd% m", fd,
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, offset, rc);
return rc;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
@ -120,7 +121,9 @@ static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen,
*/
ssize_t pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) {
ssize_t rc;
BEGIN_CANCELLATION_POINT;
rc = Pwritev(fd, iov, iovlen, off);
END_CANCELLATION_POINT;
STRACE("pwritev(%d, %s, %d, %'ld) → %'ld% m", fd,
DescribeIovec(rc != -1 ? rc : -2, iov, iovlen), iovlen, off, rc);
return rc;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
@ -49,6 +50,7 @@
* @raise EPERM if pledge() is in play without the stdio promise
* @raise EIO if low-level i/o error happened
* @raise EINTR if signal was delivered instead
* @raise ECANCELED if thread was cancelled in masked mode
* @raise ENOTCONN if `fd` is a socket and it isn't connected
* @raise ECONNRESET if socket peer forcibly closed connection
* @raise ETIMEDOUT if socket transmission timeout occurred
@ -63,6 +65,7 @@
*/
ssize_t read(int fd, void *buf, size_t size) {
ssize_t rc;
BEGIN_CANCELLATION_POINT;
if (fd >= 0) {
if ((!buf && size) || (IsAsan() && !__asan_is_valid(buf, size))) {
rc = efault();
@ -82,6 +85,7 @@ ssize_t read(int fd, void *buf, size_t size) {
} else {
rc = ebadf();
}
END_CANCELLATION_POINT;
DATATRACE("read(%d, [%#.*hhs%s], %'zu) → %'zd% m", fd, MAX(0, MIN(40, rc)),
buf, rc > 40 ? "..." : "", size, rc);
return rc;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
@ -48,6 +49,7 @@
*/
ssize_t readv(int fd, const struct iovec *iov, int iovlen) {
ssize_t rc;
BEGIN_CANCELLATION_POINT;
if (fd >= 0 && iovlen >= 0) {
if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) {
@ -74,6 +76,7 @@ ssize_t readv(int fd, const struct iovec *iov, int iovlen) {
rc = einval();
}
END_CANCELLATION_POINT;
STRACE("readv(%d, [%s], %d) → %'ld% m", fd, DescribeIovec(rc, iov, iovlen),
iovlen, rc);
return rc;

View file

@ -22,9 +22,9 @@
/**
* Accumulates resource statistics in `y` to `x`.
*/
void _addrusage(struct rusage *x, const struct rusage *y) {
x->ru_utime = _timeval_add(x->ru_utime, y->ru_utime);
x->ru_stime = _timeval_add(x->ru_stime, y->ru_stime);
void rusage_add(struct rusage *x, const struct rusage *y) {
x->ru_utime = timeval_add(x->ru_utime, y->ru_utime);
x->ru_stime = timeval_add(x->ru_stime, y->ru_stime);
x->ru_maxrss = MAX(x->ru_maxrss, y->ru_maxrss);
x->ru_ixrss += y->ru_ixrss;
x->ru_idrss += y->ru_idrss;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.h"
@ -49,6 +50,8 @@ int sigsuspend(const sigset_t *ignore) {
long ms, totoms;
sigset_t save, *arg, mask = {0};
STRACE("sigsuspend(%s) → ...", DescribeSigset(0, ignore));
BEGIN_CANCELLATION_POINT;
if (IsAsan() && ignore && !__asan_is_valid(ignore, sizeof(*ignore))) {
rc = efault();
} else if (IsXnu() || IsOpenbsd()) {
@ -94,6 +97,8 @@ int sigsuspend(const sigset_t *ignore) {
// TODO(jart): sigsuspend metal support
rc = enosys();
}
END_CANCELLATION_POINT;
STRACE("...sigsuspend → %d% m", rc);
return rc;
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/asan.internal.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/sigtimedwait.h"
#include "libc/calls/sigtimedwait.internal.h"
#include "libc/calls/struct/siginfo.internal.h"
@ -36,6 +37,7 @@
* @param timeout is relative deadline and null means wait forever
* @return signal number on success, or -1 w/ errno
* @raise EINTR if an asynchronous signal was delivered instead
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINVAL if nanoseconds parameter was out of range
* @raise EAGAIN if deadline expired
* @raise ENOSYS on Windows, XNU, OpenBSD, Metal
@ -48,6 +50,7 @@ int sigtimedwait(const sigset_t *set, siginfo_t *info,
char strsig[15];
struct timespec ts;
union siginfo_meta si = {0};
BEGIN_CANCELLATION_POINT;
if (IsAsan() && (!__asan_is_valid(set, sizeof(*set)) ||
(info && !__asan_is_valid(info, sizeof(*info))) ||
@ -69,6 +72,7 @@ int sigtimedwait(const sigset_t *set, siginfo_t *info,
rc = enosys();
}
END_CANCELLATION_POINT;
STRACE("sigtimedwait(%s, [%s], %s) → %s% m", DescribeSigset(0, set),
DescribeSiginfo(rc, info), DescribeTimespec(0, timeout),
strsignal_r(rc, strsig));

View file

@ -25,6 +25,7 @@
* @param info if not null shall receive info about signal
* @return signal number on success, or -1 w/ errno
* @raise EINTR if an asynchronous signal was delivered instead
* @raise ECANCELED if thread was cancelled in masked mode
* @raise ENOSYS on OpenBSD, XNU, and Windows
* @see sigtimedwait()
* @cancellationpoint

View file

@ -16,7 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/struct/timespec.h"
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/sysv/consts/clock.h"
#include "libc/time/time.h"
@ -27,16 +29,20 @@
* @return 0 if the full time elapsed, otherwise we assume an interrupt
* was delivered, in which case the errno condition is ignored, and
* this function shall return the number of unslept seconds rounded
* using the ceiling function
* using the ceiling function, and finally `-1u` may be returned if
* thread was cancelled with `PTHREAD_CANCEL_MASKED` in play
* @see clock_nanosleep()
* @cancellationpoint
* @asyncsignalsafe
* @norestart
*/
unsigned sleep(unsigned seconds) {
errno_t rc;
unsigned unslept;
struct timespec tv = {seconds};
if (!clock_nanosleep(CLOCK_REALTIME, 0, &tv, &tv)) return 0;
if (!(rc = clock_nanosleep(CLOCK_REALTIME, 0, &tv, &tv))) return 0;
if (rc == ECANCELED) return -1u;
_npassert(rc == EINTR);
unslept = tv.tv_sec;
if (tv.tv_nsec && unslept < UINT_MAX) {
++unslept;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/statfs-meta.internal.h"
#include "libc/calls/struct/statfs.internal.h"
@ -29,11 +30,15 @@
/**
* Returns information about filesystem.
* @return 0 on success, or -1 w/ errno
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
* @cancellationpoint
*/
int statfs(const char *path, struct statfs *sf) {
int rc;
union statfs_meta m;
BEGIN_CANCELLATION_POINT;
CheckLargeStackAllocation(&m, sizeof(m));
if (!IsWindows()) {
if ((rc = sys_statfs(path, &m)) != -1) {
@ -42,6 +47,8 @@ int statfs(const char *path, struct statfs *sf) {
} else {
rc = sys_statfs_nt(path, sf);
}
END_CANCELLATION_POINT;
STRACE("statfs(%#s, [%s]) → %d% m", path, DescribeStatfs(rc, sf));
return rc;
}

View file

@ -26,7 +26,7 @@ struct rusage {
int getrusage(int, struct rusage *);
int wait3(int *, int, struct rusage *);
int wait4(int, int *, int, struct rusage *);
void _addrusage(struct rusage *, const struct rusage *);
void rusage_add(struct rusage *, const struct rusage *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -3,8 +3,8 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define _timespec_zero ((struct timespec){0})
#define _timespec_max ((struct timespec){0x7fffffffffffffff, 999999999})
#define timespec_zero ((struct timespec){0})
#define timespec_max ((struct timespec){0x7fffffffffffffff, 999999999})
struct timespec {
int64_t tv_sec;
@ -21,22 +21,19 @@ int utimensat(int, const char *, const struct timespec[2], int);
int timespec_get(struct timespec *, int);
int timespec_getres(struct timespec *, int);
int _timespec_cmp(struct timespec, struct timespec) pureconst;
bool _timespec_eq(struct timespec, struct timespec) pureconst;
bool _timespec_gt(struct timespec, struct timespec) pureconst;
bool _timespec_gte(struct timespec, struct timespec) pureconst;
int64_t _timespec_tomicros(struct timespec) pureconst;
int64_t _timespec_tomillis(struct timespec) pureconst;
int64_t _timespec_tonanos(struct timespec) pureconst;
struct timespec _timespec_add(struct timespec, struct timespec) pureconst;
struct timespec _timespec_fromnanos(int64_t) pureconst;
struct timespec _timespec_frommicros(int64_t) pureconst;
struct timespec _timespec_frommillis(int64_t) pureconst;
struct timespec _timespec_real(void);
struct timespec _timespec_mono(void);
struct timespec _timespec_sleep(struct timespec);
int _timespec_sleep_until(struct timespec);
struct timespec _timespec_sub(struct timespec, struct timespec) pureconst;
int timespec_cmp(struct timespec, struct timespec) pureconst;
int64_t timespec_tomicros(struct timespec) pureconst;
int64_t timespec_tomillis(struct timespec) pureconst;
int64_t timespec_tonanos(struct timespec) pureconst;
struct timespec timespec_add(struct timespec, struct timespec) pureconst;
struct timespec timespec_fromnanos(int64_t) pureconst;
struct timespec timespec_frommicros(int64_t) pureconst;
struct timespec timespec_frommillis(int64_t) pureconst;
struct timespec timespec_real(void);
struct timespec timespec_mono(void);
struct timespec timespec_sleep(struct timespec);
int timespec_sleep_until(struct timespec);
struct timespec timespec_sub(struct timespec, struct timespec) pureconst;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -16,14 +16,11 @@ int gettimeofday(struct timeval *, struct timezone *);
int lutimes(const char *, const struct timeval[2]);
int utimes(const char *, const struct timeval[2]);
int _timeval_cmp(struct timeval, struct timeval) pureconst;
bool _timeval_eq(struct timeval, struct timeval) pureconst;
bool _timeval_gt(struct timeval, struct timeval) pureconst;
bool _timeval_gte(struct timeval, struct timeval) pureconst;
struct timeval _timeval_add(struct timeval, struct timeval) pureconst;
struct timeval _timeval_sub(struct timeval, struct timeval) pureconst;
struct timeval _timespec_totimeval(struct timespec) pureconst;
struct timespec _timeval_totimespec(struct timeval) pureconst;
int timeval_cmp(struct timeval, struct timeval) pureconst;
struct timeval timeval_add(struct timeval, struct timeval) pureconst;
struct timeval timeval_sub(struct timeval, struct timeval) pureconst;
struct timeval timespec_totimeval(struct timespec) pureconst;
struct timespec timeval_totimespec(struct timeval) pureconst;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -24,6 +24,7 @@ i32 __sys_fstatat(i32, const char *, void *, i32) hidden;
i32 __sys_gettid(i64 *) hidden;
i32 __sys_munmap(void *, u64) hidden;
i32 __sys_openat(i32, const char *, i32, u32) hidden;
i32 __sys_openat_nc(i32, const char *, i32, u32) hidden;
i32 __sys_pipe2(i32[hasatleast 2], u32) hidden;
i32 sys_arch_prctl(i32, i64) hidden;
i32 sys_chdir(const char *) hidden;

View file

@ -10,21 +10,23 @@ bool __is_linux_2_6_23(void) hidden;
bool32 sys_isatty_metal(int);
int __fixupnewfd(int, int) hidden;
int __notziposat(int, const char *);
int __tkill(int, int, void *) hidden;
int _fork(uint32_t) hidden;
int _isptmaster(int) hidden;
int _ptsname(int, char *, size_t) hidden;
int getdomainname_linux(char *, size_t) hidden;
int gethostname_bsd(char *, size_t, int) hidden;
int gethostname_linux(char *, size_t) hidden;
int gethostname_nt(char *, size_t, int) hidden;
int sys_msyscall(void *, size_t);
long sys_bogus(void);
ssize_t __getrandom(void *, size_t, unsigned) hidden;
void *__vdsosym(const char *, const char *) hidden;
void __onfork(void) hidden;
void __restore_rt() hidden;
void __restore_rt_netbsd(void) hidden;
void cosmo2flock(uintptr_t) hidden;
void flock2cosmo(uintptr_t) hidden;
int _ptsname(int, char *, size_t) hidden;
int _isptmaster(int) hidden;
int _fork(uint32_t) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -40,7 +40,7 @@ static int64_t GetUptime(void) {
size_t n = sizeof(x);
int mib[] = {CTL_KERN, KERN_BOOTTIME};
if (sys_sysctl(mib, ARRAYLEN(mib), &x, &n, 0, 0) == -1) return 0;
return _timespec_real().tv_sec - x.tv_sec;
return timespec_real().tv_sec - x.tv_sec;
}
static int64_t GetPhysmem(void) {

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
@ -27,6 +28,7 @@
static textwindows int sys_tcdrain_nt(int fd) {
if (!__isfdopen(fd)) return ebadf();
if (_check_interrupts(false, g_fds.p)) return -1;
if (!FlushFileBuffers(g_fds.p[fd].handle)) return __winerr();
return 0;
}
@ -39,12 +41,15 @@ static textwindows int sys_tcdrain_nt(int fd) {
* @raise ENOTTY if `fd` is open but not a teletypewriter
* @raise EIO if process group of writer is orphoned, calling thread is
* not blocking `SIGTTOU`, and process isn't ignoring `SIGTTOU`
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
* @raise ENOSYS on bare metal
* @cancellationpoint
* @asyncsignalsafe
*/
int tcdrain(int fd) {
int rc;
BEGIN_CANCELLATION_POINT;
if (IsMetal()) {
rc = enosys();
} else if (!IsWindows()) {
@ -52,6 +57,7 @@ int tcdrain(int fd) {
} else {
rc = sys_tcdrain_nt(fd);
}
END_CANCELLATION_POINT;
STRACE("tcdrain(%d) → %d% m", fd, rc);
return rc;
}

View file

@ -21,7 +21,7 @@
/**
* Adds two nanosecond timestamps.
*/
struct timespec _timespec_add(struct timespec x, struct timespec y) {
struct timespec timespec_add(struct timespec x, struct timespec y) {
x.tv_sec += y.tv_sec;
x.tv_nsec += y.tv_nsec;
if (x.tv_nsec >= 1000000000) {

View file

@ -20,8 +20,10 @@
/**
* Compares nanosecond timestamps.
*
* @return 0 if equal, -1 if `a < b`, or +1 if `a > b`
*/
int _timespec_cmp(struct timespec a, struct timespec b) {
int timespec_cmp(struct timespec a, struct timespec b) {
int cmp;
if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec))) {
cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec);

View file

@ -21,7 +21,7 @@
/**
* Converts timespec interval from microseconds.
*/
struct timespec _timespec_frommicros(int64_t x) {
struct timespec timespec_frommicros(int64_t x) {
struct timespec ts;
ts.tv_sec = x / 1000000;
ts.tv_nsec = x % 1000000 * 1000;

View file

@ -21,7 +21,7 @@
/**
* Converts timespec interval from milliseconds.
*/
struct timespec _timespec_frommillis(int64_t x) {
struct timespec timespec_frommillis(int64_t x) {
struct timespec ts;
ts.tv_sec = x / 1000;
ts.tv_nsec = x % 1000 * 1000000;

View file

@ -21,7 +21,7 @@
/**
* Converts timespec interval from nanoseconds.
*/
struct timespec _timespec_fromnanos(int64_t x) {
struct timespec timespec_fromnanos(int64_t x) {
struct timespec ts;
ts.tv_sec = x / 1000000000;
ts.tv_nsec = x % 1000000000;

View file

@ -26,7 +26,7 @@
* @param ts receives `CLOCK_REALTIME` timestamp
* @param base must be `TIME_UTC`
* @return `base` on success, or `0` on failure
* @see _timespec_real()
* @see timespec_real()
*/
int timespec_get(struct timespec *ts, int base) {
if (base == TIME_UTC && !clock_gettime(CLOCK_REALTIME, ts)) {

View file

@ -25,9 +25,9 @@
*
* This function uses a `CLOCK_MONOTONIC` clock and never fails.
*
* @see _timespec_real()
* @see timespec_real()
*/
struct timespec _timespec_mono(void) {
struct timespec timespec_mono(void) {
struct timespec ts;
_npassert(!clock_gettime(CLOCK_MONOTONIC_FAST, &ts));
return ts;

View file

@ -27,9 +27,9 @@
* clock_gettime() or timespec_real() this interface avoids the use of
* pointers which lets time handling code become more elegant.
*
* @see _timespec_mono()
* @see timespec_mono()
*/
struct timespec _timespec_real(void) {
struct timespec timespec_real(void) {
struct timespec ts;
_npassert(!clock_gettime(CLOCK_REALTIME_FAST, &ts));
return ts;

View file

@ -17,8 +17,10 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/errno.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/clock.h"
/**
@ -26,13 +28,14 @@
*
* @return unslept time which may be non-zero if the call was interrupted
*/
struct timespec _timespec_sleep(struct timespec delay) {
int rc;
struct timespec timespec_sleep(struct timespec delay) {
errno_t rc;
struct timespec remain;
if (!(rc = clock_nanosleep(CLOCK_REALTIME, 0, &delay, &remain))) {
return (struct timespec){0};
} else {
_npassert(rc == EINTR || rc == ECANCELED);
return remain;
BLOCK_CANCELLATIONS;
bzero(&remain, sizeof(remain));
if ((rc = clock_nanosleep(CLOCK_REALTIME, 0, &delay, &remain))) {
_npassert(rc == EINTR);
}
ALLOW_CANCELLATIONS;
return remain;
}

View file

@ -26,9 +26,12 @@
* Sleeps until the specified time.
*
* @return 0 on success, or EINTR if interrupted
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
* @cancellationpoint
*/
errno_t _timespec_sleep_until(struct timespec abs_deadline) {
int rc;
errno_t timespec_sleep_until(struct timespec abs_deadline) {
errno_t rc;
rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &abs_deadline, 0);
_npassert(!rc || rc == EINTR || rc == ECANCELED);
return rc;

View file

@ -21,7 +21,7 @@
/**
* Subtracts two nanosecond timestamps.
*/
struct timespec _timespec_sub(struct timespec a, struct timespec b) {
struct timespec timespec_sub(struct timespec a, struct timespec b) {
a.tv_sec -= b.tv_sec;
if (a.tv_nsec < b.tv_nsec) {
a.tv_nsec += 1000000000;

View file

@ -31,9 +31,9 @@
* `INT64_MIN` may be returned. The `errno` variable isn't changed.
*
* @return 64-bit scalar holding microseconds since epoch
* @see _timespec_totimeval()
* @see timespec_totimeval()
*/
int64_t _timespec_tomicros(struct timespec ts) {
int64_t timespec_tomicros(struct timespec ts) {
int64_t us;
// reduce precision from nanos to micros
if (ts.tv_nsec <= 999999000) {

View file

@ -32,7 +32,7 @@
*
* @return 64-bit scalar milliseconds since epoch
*/
int64_t _timespec_tomillis(struct timespec ts) {
int64_t timespec_tomillis(struct timespec ts) {
int64_t ms;
// reduce precision from nanos to millis
if (ts.tv_nsec <= 999000000) {

View file

@ -27,7 +27,7 @@
*
* @return 64-bit integer holding nanoseconds since epoch
*/
int64_t _timespec_tonanos(struct timespec x) {
int64_t timespec_tonanos(struct timespec x) {
int64_t ns;
if (!__builtin_mul_overflow(x.tv_sec, 1000000000ul, &ns) &&
!__builtin_add_overflow(ns, x.tv_nsec, &ns)) {

View file

@ -27,9 +27,9 @@
* timestamp has a special meaning.
*
* @return microseconds since epoch
* @see _timespec_tomicros()
* @see timespec_tomicros()
*/
struct timeval _timespec_totimeval(struct timespec ts) {
struct timeval timespec_totimeval(struct timespec ts) {
if (ts.tv_nsec < 1000000000 - 999) {
return (struct timeval){ts.tv_sec, (ts.tv_nsec + 999) / 1000};
} else {

View file

@ -21,7 +21,7 @@
/**
* Adds two microsecond timestamps.
*/
struct timeval _timeval_add(struct timeval x, struct timeval y) {
struct timeval timeval_add(struct timeval x, struct timeval y) {
x.tv_sec += y.tv_sec;
x.tv_usec += y.tv_usec;
if (x.tv_usec >= 1000000) {

View file

@ -19,9 +19,11 @@
#include "libc/calls/struct/timeval.h"
/**
* Compares microseconds timestamps.
* Compares microsecond timestamps.
*
* @return 0 if equal, -1 if `a < b`, or +1 if `a > b`
*/
int _timeval_cmp(struct timeval a, struct timeval b) {
int timeval_cmp(struct timeval a, struct timeval b) {
int cmp;
if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec))) {
cmp = (a.tv_usec > b.tv_usec) - (a.tv_usec < b.tv_usec);

View file

@ -21,7 +21,7 @@
/**
* Subtracts two nanosecond timestamps.
*/
struct timeval _timeval_sub(struct timeval a, struct timeval b) {
struct timeval timeval_sub(struct timeval a, struct timeval b) {
a.tv_sec -= b.tv_sec;
if (a.tv_usec < b.tv_usec) {
a.tv_usec += 1000000;

View file

@ -21,6 +21,6 @@
/**
* Coerces `tv` from 1e-6 to 1e-9 granularity.
*/
struct timespec _timeval_totimespec(struct timeval tv) {
struct timespec timeval_totimespec(struct timeval tv) {
return (struct timespec){tv.tv_sec, tv.tv_usec * 1000};
}

View file

@ -19,29 +19,35 @@
#include "libc/calls/calls.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/consts/sicode.h"
// OpenBSD has an optional `tib` parameter for extra safety
int __tkill(int tid, int sig, void *tib) {
int rc;
if (!IsWindows() && !IsMetal()) {
rc = sys_tkill(tid, sig, tib);
} else {
rc = __sig_add(tid, sig, SI_TKILL);
}
STRACE("tkill(%d, %G) → %d% m", tid, sig, rc);
return rc;
}
/**
* Kills thread.
*
* @param tid is thread id
* @param sig does nothing on xnu
* @return 0 on success, or -1 w/ errno
* @raise ESRCH if `tid` was valid but no such thread existed
* @raise EAGAIN if `RLIMIT_SIGPENDING` was exceeded
* @raise EINVAL if `tid` or `sig` was invalid
* @raise EINVAL if `tid` or `sig` were invalid
* @raise ESRCH if no such `tid` existed
* @raise EPERM if permission was denied
* @asyncsignalsafe
*/
int tkill(int tid, int sig) {
int rc;
if (!IsWindows() && !IsMetal()) {
rc = sys_tkill(tid, sig, 0);
} else {
rc = __sig_add(tid, sig, SI_TKILL);
}
STRACE("tkill(%d, %G) → %d% m", tid, sig, rc);
return rc;
return __tkill(tid, sig, 0);
}

View file

@ -62,7 +62,10 @@
* other platforms.
*
* @return file descriptor on success, or -1 w/ errno
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if signal was delivered
* @see tmpfile() for stdio version
* @cancellationpoint
* @asyncsignalsafe
* @threadsafe
* @vforksafe

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/timeval.h"
#include "libc/errno.h"
@ -34,7 +35,10 @@ int touch(const char *file, uint32_t mode) {
olderr = errno;
if ((rc = utimes(file, 0)) == -1 && errno == ENOENT) {
errno = olderr;
if ((fd = open(file, O_CREAT | O_WRONLY, mode)) == -1) return -1;
BLOCK_CANCELLATIONS;
fd = open(file, O_CREAT | O_WRONLY, mode);
ALLOW_CANCELLATIONS;
if (fd == -1) return -1;
return close(fd);
}
return rc;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
@ -46,6 +47,7 @@
* @return 0 on success, or -1 w/ errno
* @raise EINVAL if `length` is negative
* @raise EINTR if signal was delivered instead
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EFBIG or EINVAL if `length` is too huge
* @raise EFAULT if `path` points to invalid memory
* @raise ENOTSUP if `path` is a zip filesystem path
@ -65,6 +67,8 @@
int truncate(const char *path, int64_t length) {
int rc;
struct ZiposUri zipname;
BEGIN_CANCELLATION_POINT;
if (IsMetal()) {
rc = enosys();
} else if (!path || (IsAsan() && !__asan_is_valid_str(path))) {
@ -80,6 +84,8 @@ int truncate(const char *path, int64_t length) {
} else {
rc = sys_truncate_nt(path, length);
}
END_CANCELLATION_POINT;
STRACE("truncate(%#s, %'ld) → %d% m", path, length, rc);
return rc;
}

View file

@ -17,9 +17,9 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/landlock.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/bpf.h"
#include "libc/calls/struct/filter.h"
#include "libc/calls/struct/seccomp.h"
@ -29,8 +29,8 @@
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/thread/tls.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
@ -45,6 +45,7 @@
#include "libc/sysv/consts/pr.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/tls.h"
#define OFF(f) offsetof(struct seccomp_data, f)
@ -234,7 +235,9 @@ int sys_unveil_linux(const char *path, const char *permissions) {
}
// now we can open the path
BLOCK_CANCELLATIONS;
rc = sys_open(path, O_PATH | O_NOFOLLOW | O_CLOEXEC, 0);
ALLOW_CANCELLATIONS;
if (rc == -1) return rc;
pb.parent_fd = rc;

View file

@ -26,12 +26,13 @@
*
* @return 0 on success, or -1 w/ errno
* @raise EINTR if a signal was delivered while sleeping
* @raise ECANCELED if thread was cancelled in masked mode
* @see clock_nanosleep()
* @cancellationpoint
* @norestart
*/
int usleep(uint32_t micros) {
struct timespec ts = _timespec_frommicros(micros);
struct timespec ts = timespec_frommicros(micros);
if (clock_nanosleep(CLOCK_REALTIME, 0, &ts, 0)) return eintr();
return 0;
}

View file

@ -39,8 +39,8 @@ int sys_utimensat(int dirfd, const char *path, const struct timespec ts[2],
if (rc == -1 && errno == ENOSYS && path) {
errno = olderr;
if (ts) {
tv[0] = _timespec_totimeval(ts[0]);
tv[1] = _timespec_totimeval(ts[1]);
tv[0] = timespec_totimeval(ts[0]);
tv[1] = timespec_totimeval(ts[1]);
rc = sys_utimes(path, tv);
} else {
rc = sys_utimes(path, NULL);

View file

@ -42,16 +42,16 @@ int sys_utimensat_xnu(int dirfd, const char *path, const struct timespec ts[2],
if (ts[0].tv_nsec == UTIME_NOW) {
tv[0] = now;
} else if (ts[0].tv_nsec == UTIME_OMIT) {
tv[0] = _timespec_totimeval(st.st_atim);
tv[0] = timespec_totimeval(st.st_atim);
} else {
tv[0] = _timespec_totimeval(ts[0]);
tv[0] = timespec_totimeval(ts[0]);
}
if (ts[1].tv_nsec == UTIME_NOW) {
tv[1] = now;
} else if (ts[1].tv_nsec == UTIME_OMIT) {
tv[1] = _timespec_totimeval(st.st_mtim);
tv[1] = timespec_totimeval(st.st_mtim);
} else {
tv[1] = _timespec_totimeval(ts[1]);
tv[1] = timespec_totimeval(ts[1]);
}
} else {
tv[0] = now;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/struct/rusage.internal.h"
#include "libc/calls/wait4.h"
#include "libc/dce.h"
@ -41,6 +42,8 @@
int wait4(int pid, int *opt_out_wstatus, int options,
struct rusage *opt_out_rusage) {
int rc, ws = 0;
BEGIN_CANCELLATION_POINT;
if (IsAsan() &&
((opt_out_wstatus &&
!__asan_is_valid(opt_out_wstatus, sizeof(*opt_out_wstatus))) ||
@ -53,6 +56,8 @@ int wait4(int pid, int *opt_out_wstatus, int options,
rc = sys_wait4_nt(pid, &ws, options, opt_out_rusage);
}
if (rc != -1 && opt_out_wstatus) *opt_out_wstatus = ws;
END_CANCELLATION_POINT;
STRACE("wait4(%d, [%#x], %d, %p) → %d% m", pid, ws, options, opt_out_rusage,
rc);
return rc;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
@ -51,6 +52,7 @@
* @raise ENOSPC if device containing `fd` is full
* @raise EIO if low-level i/o error happened
* @raise EINTR if signal was delivered instead
* @raise ECANCELED if thread was cancelled in masked mode
* @raise EAGAIN if `O_NONBLOCK` is in play and write needs to block
* @raise ENOBUFS if kernel lacked internal resources; which FreeBSD
* and XNU say could happen with sockets, and OpenBSD documents it
@ -64,6 +66,8 @@
*/
ssize_t write(int fd, const void *buf, size_t size) {
ssize_t rc;
BEGIN_CANCELLATION_POINT;
if (fd >= 0) {
if ((!buf && size) || (IsAsan() && !__asan_is_valid(buf, size))) {
rc = efault();
@ -83,6 +87,8 @@ ssize_t write(int fd, const void *buf, size_t size) {
} else {
rc = ebadf();
}
END_CANCELLATION_POINT;
DATATRACE("write(%d, %#.*hhs%s, %'zu) → %'zd% m", fd, MAX(0, MIN(40, rc)),
buf, rc > 40 ? "..." : "", size, rc);
return rc;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/cp.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/iovec.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
@ -52,6 +53,7 @@
*/
ssize_t writev(int fd, const struct iovec *iov, int iovlen) {
ssize_t rc;
BEGIN_CANCELLATION_POINT;
if (fd >= 0 && iovlen >= 0) {
if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) {
@ -78,6 +80,7 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) {
rc = einval();
}
END_CANCELLATION_POINT;
STRACE("writev(%d, %s, %d) → %'ld% m", fd,
DescribeIovec(rc != -1 ? rc : -2, iov, iovlen), iovlen, rc);
return rc;

View file

@ -8,7 +8,8 @@ COSMOPOLITAN_C_START_
* @see libc/sysv/consts.sh for numbers
*/
#if defined(__GNUC__) && defined(__MNO_RED_ZONE__) && !defined(__STRICT_ANSI__)
#if defined(__GNUC__) && defined(__x86_64__) && defined(__MNO_RED_ZONE__) && \
!defined(__STRICT_ANSI__)
#define errno \
(*({ \
errno_t *_ep; \

View file

@ -34,10 +34,10 @@ char *getenv(const char *s) {
if (!(p = environ)) return 0;
e = _getenv(p, s);
#if SYSDEBUG
if (!(s[0] == 'T' && s[1] == 'Z' && !s[2])) {
// if (!(s[0] == 'T' && s[1] == 'Z' && !s[2])) {
// TODO(jart): memoize TZ or something
STRACE("getenv(%#s) → %#s", s, e.s);
}
//}
#endif
return e.s;
}

View file

@ -61,12 +61,12 @@ kSignalNames:
.e SIGWINCH,"SIGWINCH"
.e SIGIO,"SIGIO"
.e SIGSYS,"SIGSYS"
.e SIGINFO,"SIGINFO"
.e SIGCANCEL,"SIGCANCEL"
.e SIGPWR,"SIGPWR"
.e SIGINFO,"SIGINFO" # order matters
.e SIGTHR,"SIGTHR" # order matters
.e SIGRTMAX,"SIGRTMAX"
.e SIGRTMIN,"SIGRTMIN"
.e SIGEMT,"SIGEMT"
.e SIGPWR,"SIGPWR"
.e SIGEMT,"SIGEMT" # order matters
.long MAGNUM_TERMINATOR
.endobj kSignalNames,globl,hidden
.overrun

View file

@ -29,6 +29,8 @@
* key's value is nonzero. The key's value is set to zero before it gets
* called. The ordering for multiple destructor calls is unspecified.
*
* The result should be passed to pthread_key_delete() later.
*
* @param key is set to the allocated key on success
* @param dtor specifies an optional destructor callback
* @return 0 on success, or errno on error

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/errno.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
@ -76,3 +77,13 @@ errno_t pthread_setcancelstate(int state, int *oldstate) {
return 0;
}
}
int _pthread_block_cancellations(void) {
int oldstate;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
return oldstate;
}
void _pthread_allow_cancellations(int oldstate) {
pthread_setcancelstate(oldstate, 0);
}

View file

@ -1,4 +1,4 @@
M(SIGKILL)
M(SIGABRT)
M(SIGSTOP)
M(SIGCANCEL)
M(SIGTHR)

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#define ShouldUseMsabiAttribute() 1
#include "libc/assert.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/errors.h"
@ -34,6 +35,7 @@ privileged int64_t __winerr(void) {
errno_t e;
if (IsWindows()) {
e = __dos2errno(__imp_GetLastError());
_npassert(e > 0);
} else {
e = ENOSYS;
}

View file

@ -54,6 +54,13 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
char *debugbin, *p1, *p2, *p3, *addr2line;
char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames];
// DWARF is a weak standard. Platforms that use LLVM or old GNU
// usually can't be counted upon to print backtraces correctly.
if (!IsLinux() && !IsWindows()) {
ShowHint("won't print addr2line backtrace because probably llvm");
return -1;
}
if (!PLEDGED(STDIO) || !PLEDGED(EXEC) || !PLEDGED(EXEC)) {
return -1;
}
@ -69,13 +76,6 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
return -1;
}
// DWARF is a weak standard. Platforms that use LLVM or old GNU
// usually can't be counted upon to print backtraces correctly.
if (!IsLinux() && !IsWindows()) {
ShowHint("won't print addr2line backtrace because probably llvm");
return -1;
}
// backtrace_test.com failing on windows for some reason via runitd
// don't want to pull in the high-level syscalls here anyway
if (IsWindows()) {

View file

@ -16,9 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigaltstack.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/log/internal.h"
#include "libc/log/log.h"
@ -40,17 +42,16 @@ STATIC_YOINK("PrintBacktraceUsingSymbols"); // for backtracing
STATIC_YOINK("malloc_inspect_all"); // for asan memory origin
STATIC_YOINK("GetSymbolByAddr"); // for asan memory origin
extern const unsigned char __oncrash_thunks[8][11];
static struct sigaltstack g_oldsigaltstack;
static struct sigaction g_oldcrashacts[8];
extern const unsigned char __oncrash_thunks[8][11];
static void InstallCrashHandlers(int extraflags) {
int e;
size_t i;
struct sigaction sa;
bzero(&sa, sizeof(sa));
sa.sa_flags = SA_SIGINFO | SA_NODEFER | extraflags;
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO | SA_NODEFER | extraflags;
for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) {
sigdelset(&sa.sa_mask, kCrashSigs[i]);
}
@ -81,11 +82,6 @@ relegated void RestoreDefaultCrashSignalHandlers(void) {
strace_enabled(+1);
}
static void FreeSigAltStack(void *p) {
sigaltstack(&g_oldsigaltstack, 0);
munmap(p, GetStackSize());
}
/**
* Installs crash signal handlers.
*
@ -102,7 +98,6 @@ static void FreeSigAltStack(void *p) {
* useful, for example, if a program is caught in an infinite loop.
*/
void ShowCrashReports(void) {
char *sp;
struct sigaltstack ss;
_wantcrashreports = true;
/* <SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c */
@ -116,19 +111,12 @@ void ShowCrashReports(void) {
kCrashSigs[7] = SIGURG; /* placeholder */
/* </SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c */
if (!IsWindows()) {
bzero(&ss, sizeof(ss));
ss.ss_flags = 0;
ss.ss_size = GetStackSize();
// FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here
// OpenBSD sigaltstack() auto-applies MAP_STACK to the memory
if ((sp = _mapanon(GetStackSize()))) {
ss.ss_sp = sp;
if (!sigaltstack(&ss, &g_oldsigaltstack)) {
__cxa_atexit(FreeSigAltStack, ss.ss_sp, 0);
} else {
munmap(ss.ss_sp, GetStackSize());
}
}
_npassert((ss.ss_sp = _mapanon(GetStackSize())));
_npassert(!sigaltstack(&ss, 0));
InstallCrashHandlers(SA_ONSTACK);
} else {
InstallCrashHandlers(0);

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/dprintf.h"
#include "libc/calls/struct/stat.h"
@ -96,10 +97,11 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
if (!f) return;
flockfile(f);
strace_enabled(-1);
BLOCK_CANCELLATIONS;
// We display TIMESTAMP.MICROS normally. However, when we log multiple
// times in the same second, we display TIMESTAMP+DELTAMICROS instead.
t2 = _timespec_real();
t2 = timespec_real();
if (t2.tv_sec == vflogf_ts.tv_sec) {
sign = "+";
dots = t2.tv_nsec - vflogf_ts.tv_nsec;
@ -138,6 +140,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
unreachable;
}
ALLOW_CANCELLATIONS;
strace_enabled(+1);
funlockfile(f);
}

View file

@ -309,10 +309,10 @@ static int Usleep(void) {
struct timespec t;
if (n > 1) {
f = 0;
t = _timespec_frommicros(atoi(args[1]));
t = timespec_frommicros(atoi(args[1]));
} else {
f = TIMER_ABSTIME;
t = _timespec_max;
t = timespec_max;
}
return clock_nanosleep(0, f, &t, 0);
}

Some files were not shown because too many files have changed in this diff Show more