mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-02 23:18:44 +00:00
Use re-entrant locks on stdio
This commit is contained in:
parent
4e9662cbc7
commit
1f229e4efc
78 changed files with 427 additions and 179 deletions
|
@ -28,15 +28,18 @@
|
|||
|
||||
_Alignas(64) static int rlock;
|
||||
|
||||
static privileged inline bool AcquireInterruptPollLock(void) {
|
||||
// return 0 on success, or tid of other owner
|
||||
static privileged inline int AcquireInterruptPollLock(void) {
|
||||
// any thread can poll for interrupts
|
||||
// but it's wasteful to have every single thread doing it
|
||||
int me, owner, tries;
|
||||
if (!__threaded) return true;
|
||||
me = gettid();
|
||||
owner = 0;
|
||||
if (_lockcmpxchgp(&rlock, &owner, me)) return true;
|
||||
return owner == me;
|
||||
int me, owner = 0;
|
||||
if (__threaded) {
|
||||
me = gettid();
|
||||
if (!_lockcmpxchgp(&rlock, &owner, me) && owner == me) {
|
||||
owner = 0;
|
||||
}
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
|
||||
static textwindows inline void ReleaseInterruptPollLock(void) {
|
||||
|
@ -47,7 +50,7 @@ static textwindows inline void ReleaseInterruptPollLock(void) {
|
|||
textwindows bool _check_interrupts(bool restartable, struct Fd *fd) {
|
||||
bool res;
|
||||
if (__time_critical) return false;
|
||||
if (!AcquireInterruptPollLock()) return false;
|
||||
if (AcquireInterruptPollLock()) return false;
|
||||
if (weaken(_check_sigalrm)) weaken(_check_sigalrm)();
|
||||
if (weaken(_check_sigchld)) weaken(_check_sigchld)();
|
||||
if (fd && weaken(_check_sigwinch)) weaken(_check_sigwinch)(fd);
|
||||
|
|
|
@ -27,6 +27,12 @@ __msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId;
|
|||
|
||||
/**
|
||||
* Returns current thread id.
|
||||
*
|
||||
* On Linux, and Linux only, this is guaranteed to be equal to getpid()
|
||||
* if this is the main thread. On NetBSD, gettid() for the main thread
|
||||
* is always 1.
|
||||
*
|
||||
* @return thread id greater than zero or -1 w/ errno
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
privileged int gettid(void) {
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
do { \
|
||||
autotype(lock) __lock = (lock); \
|
||||
typeof(*__lock) __x; \
|
||||
int __tries = 0; \
|
||||
unsigned __tries = 0; \
|
||||
for (;;) { \
|
||||
__atomic_load(__lock, &__x, __ATOMIC_RELAXED); \
|
||||
if (!__x && !_trylock_inline(__lock)) { \
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nexgen32e/stackframe.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
|
@ -89,7 +90,8 @@ extern unsigned __log_level; /* log level for runtime check */
|
|||
--__ftrace; \
|
||||
flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \
|
||||
if (weaken(__die)) weaken(__die)(); \
|
||||
exit(1); \
|
||||
__restorewintty(); \
|
||||
_Exit(1); \
|
||||
unreachable; \
|
||||
} while (0)
|
||||
|
||||
|
|
|
@ -41,24 +41,25 @@
|
|||
#define kNontrivialSize (8 * 1000 * 1000)
|
||||
|
||||
static struct timespec vflogf_ts;
|
||||
_Alignas(64) static int vflogf_lock;
|
||||
|
||||
/**
|
||||
* Takes corrective action if logging is on the fritz.
|
||||
*/
|
||||
void vflogf_onfail(FILE *f) {
|
||||
static void vflogf_onfail(FILE *f) {
|
||||
errno_t err;
|
||||
int64_t size;
|
||||
if (IsTiny()) return;
|
||||
err = ferror(f);
|
||||
if (fileno(f) != -1 && (err == ENOSPC || err == EDQUOT || err == EFBIG) &&
|
||||
((size = getfiledescriptorsize(fileno(f))) == -1 ||
|
||||
err = ferror_unlocked(f);
|
||||
if (fileno_unlocked(f) != -1 &&
|
||||
(err == ENOSPC || err == EDQUOT || err == EFBIG) &&
|
||||
((size = getfiledescriptorsize(fileno_unlocked(f))) == -1 ||
|
||||
size > kNontrivialSize)) {
|
||||
ftruncate(fileno(f), 0);
|
||||
fseek(f, SEEK_SET, 0);
|
||||
ftruncate(fileno_unlocked(f), 0);
|
||||
fseeko_unlocked(f, SEEK_SET, 0);
|
||||
f->beg = f->end = 0;
|
||||
clearerr(f);
|
||||
(fprintf)(f, "performed emergency log truncation: %s\n", strerror(err));
|
||||
clearerr_unlocked(f);
|
||||
(fprintf_unlocked)(f, "performed emergency log truncation: %s\n",
|
||||
strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,8 +89,8 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
int64_t secs, nsec, dots;
|
||||
if (!f) f = __log_file;
|
||||
if (!f) return;
|
||||
_spinlock(&vflogf_lock);
|
||||
__atomic_fetch_sub(&__strace, 1, __ATOMIC_RELAXED);
|
||||
flockfile(f);
|
||||
--__strace;
|
||||
|
||||
t2 = nowl();
|
||||
secs = t2;
|
||||
|
@ -105,16 +106,17 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
bufmode = f->bufmode;
|
||||
if (bufmode == _IOLBF) f->bufmode = _IOFBF;
|
||||
|
||||
if ((fprintf)(f, "%r%c%s%06ld:%s:%d:%.*s:%d] ", "FEWIVDNT"[level & 7], buf32,
|
||||
rem1000000int64(div1000int64(dots)), file, line,
|
||||
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
|
||||
if ((fprintf_unlocked)(f, "%r%c%s%06ld:%s:%d:%.*s:%d] ",
|
||||
"FEWIVDNT"[level & 7], buf32,
|
||||
rem1000000int64(div1000int64(dots)), file, line,
|
||||
strchrnul(prog, '.') - prog, prog, getpid()) <= 0) {
|
||||
vflogf_onfail(f);
|
||||
}
|
||||
(vfprintf)(f, fmt, va);
|
||||
fprintf(f, "\n");
|
||||
(vfprintf_unlocked)(f, fmt, va);
|
||||
fputc_unlocked('\n', f);
|
||||
if (bufmode == _IOLBF) {
|
||||
f->bufmode = _IOLBF;
|
||||
fflush(f);
|
||||
fflush_unlocked(f);
|
||||
}
|
||||
|
||||
if (level == kLogFatal) {
|
||||
|
@ -122,11 +124,10 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
strcpy(buf32, "unknown");
|
||||
gethostname(buf32, sizeof(buf32));
|
||||
(dprintf)(STDERR_FILENO, "fatality %s pid %d\n", buf32, getpid());
|
||||
_spunlock(&vflogf_lock);
|
||||
__die();
|
||||
unreachable;
|
||||
}
|
||||
|
||||
__atomic_fetch_add(&__strace, 1, __ATOMIC_RELAXED);
|
||||
_spunlock(&vflogf_lock);
|
||||
++__strace;
|
||||
funlockfile(f);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
* @return memory address, or NULL w/ errno
|
||||
* @throw EINVAL if !IS2POW(a)
|
||||
* @see pvalloc()
|
||||
* @threadsafe
|
||||
*/
|
||||
void *aligned_alloc(size_t a, size_t n) {
|
||||
if (IS2POW(a)) {
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
* portability, since that's guaranteed to work with all libraries
|
||||
* @return bytes written (excluding NUL) or -1 w/ errno
|
||||
* @see xasprintf() for a better API
|
||||
* @threadsafe
|
||||
*/
|
||||
int(asprintf)(char **strp, const char *fmt, ...) {
|
||||
int res;
|
||||
|
|
|
@ -26,5 +26,6 @@
|
|||
// @return rax is memory address, or NULL w/ errno
|
||||
// @note overreliance on memalign is a sure way to fragment space
|
||||
// @see dlcalloc()
|
||||
// @threadsafe
|
||||
calloc: jmp *hook_calloc(%rip)
|
||||
.endfn calloc,globl
|
||||
|
|
|
@ -28,5 +28,6 @@
|
|||
//
|
||||
// @param rdi is allocation address, which may be NULL
|
||||
// @see dlfree()
|
||||
// @threadsafe
|
||||
free: jmp *hook_free(%rip)
|
||||
.endfn free,globl
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
* that'll be returned.
|
||||
*
|
||||
* @return pointer that must be free()'d, or NULL w/ errno
|
||||
* @threadsafe
|
||||
*/
|
||||
dontdiscard char *get_current_dir_name(void) {
|
||||
const char *p;
|
||||
|
|
|
@ -33,5 +33,6 @@
|
|||
//
|
||||
// @param rdi is number of bytes needed, coerced to 1+
|
||||
// @return new memory, or NULL w/ errno
|
||||
// @threadsafe
|
||||
malloc: jmp *hook_malloc(%rip)
|
||||
.endfn malloc,globl
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
// @param rdi is address of allocation
|
||||
// @return rax is total number of bytes
|
||||
// @see dlmalloc_usable_size()
|
||||
// @threadsafe
|
||||
malloc_usable_size:
|
||||
jmp *hook_malloc_usable_size(%rip)
|
||||
.endfn malloc_usable_size,globl
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
// @param rsi is number of bytes needed, coerced to 1+
|
||||
// @return rax is memory address, or NULL w/ errno
|
||||
// @see valloc(), pvalloc()
|
||||
// @threadsafe
|
||||
memalign:
|
||||
jmp *hook_memalign(%rip)
|
||||
.endfn memalign,globl
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
* @param bytes is number of bytes to allocate
|
||||
* @return return 0 or EINVAL or ENOMEM w/o setting errno
|
||||
* @see memalign()
|
||||
* @threadsafe
|
||||
*/
|
||||
int posix_memalign(void **pp, size_t alignment, size_t bytes) {
|
||||
int e;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
* @param n number of bytes needed
|
||||
* @return memory address, or NULL w/ errno
|
||||
* @see valloc()
|
||||
* @threadsafe
|
||||
*/
|
||||
void *pvalloc(size_t n) {
|
||||
return memalign(PAGESIZE, ROUNDUP(n, PAGESIZE));
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
// @note realloc(p=0, n=0) → malloc(32)
|
||||
// @note realloc(p≠0, n=0) → free(p)
|
||||
// @see dlrealloc()
|
||||
// @threadsafe
|
||||
realloc:
|
||||
jmp *hook_realloc(%rip)
|
||||
.endfn realloc,globl
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
// @param rsi (newsize) is number of bytes needed
|
||||
// @return rax is result, or NULL w/ errno
|
||||
// @see dlrealloc_in_place()
|
||||
// @threadsafe
|
||||
realloc_in_place:
|
||||
jmp *hook_realloc_in_place(%rip)
|
||||
.endfn realloc_in_place,globl
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
* @param ptr may be NULL for malloc() behavior
|
||||
* @param nmemb may be 0 for free() behavior; shrinking is promised too
|
||||
* @return new address or NULL w/ errno and ptr is NOT free()'d
|
||||
* @threadsafe
|
||||
*/
|
||||
void *reallocarray(void *ptr, size_t nmemb, size_t itemsize) {
|
||||
size_t n;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
* @param s is a NUL-terminated byte string
|
||||
* @return new string or NULL w/ errno
|
||||
* @error ENOMEM
|
||||
* @threadsafe
|
||||
*/
|
||||
char *strdup(const char *s) {
|
||||
size_t len = strlen(s);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
* @param n if less than strlen(s) will truncate the string
|
||||
* @return new string or NULL w/ errno
|
||||
* @error ENOMEM
|
||||
* @threadsafe
|
||||
*/
|
||||
char *strndup(const char *s, size_t n) {
|
||||
char *s2;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
* @param n number of bytes needed
|
||||
* @return memory address, or NULL w/ errno
|
||||
* @see pvalloc()
|
||||
* @threadsafe
|
||||
*/
|
||||
void *valloc(size_t n) {
|
||||
return memalign(PAGESIZE, n);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
/**
|
||||
* Formats string w/ dynamic memory allocation.
|
||||
* @see xasprintf() for a better API
|
||||
* @threadsafe
|
||||
*/
|
||||
int(vasprintf)(char **strp, const char *fmt, va_list va) {
|
||||
va_list vb;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
/**
|
||||
* Allocates copy of wide string.
|
||||
* @threadsafe
|
||||
*/
|
||||
wchar_t *wcsdup(const wchar_t *s) {
|
||||
size_t len = wcslen(s);
|
||||
|
|
|
@ -70,7 +70,8 @@ static privileged inline void ReleaseFtraceLock(void) {
|
|||
}
|
||||
|
||||
static privileged inline bool AcquireFtraceLock(void) {
|
||||
int me, owner, tries;
|
||||
int me, owner;
|
||||
unsigned tries;
|
||||
if (!__threaded) {
|
||||
return _cmpxchg(&ftrace_lock, 0, -1);
|
||||
} else {
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
/**
|
||||
* Blocks until data from stream buffer is written out.
|
||||
*
|
||||
* @param f is the stream handle
|
||||
* @param f is the stream handle, or 0 for all streams
|
||||
* @return is 0 on success or -1 on error
|
||||
*/
|
||||
int fflush_unlocked(FILE *f) {
|
||||
|
|
|
@ -16,12 +16,28 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/lockcmpxchgp.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Acquires lock on stdio object, blocking if needed.
|
||||
* Acquires reentrant lock on stdio object, blocking if needed.
|
||||
*/
|
||||
void flockfile(FILE *f) {
|
||||
_spinlock(&f->lock);
|
||||
int me, owner;
|
||||
unsigned tries;
|
||||
if (!__threaded) return;
|
||||
for (tries = 0, me = gettid();;) {
|
||||
owner = 0;
|
||||
if (_lockcmpxchgp(&f->lock, &owner, me) || owner == me) {
|
||||
return;
|
||||
}
|
||||
if (++tries & 7) {
|
||||
__builtin_ia32_pause();
|
||||
} else {
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,11 +31,11 @@ void _flushlbf(void) {
|
|||
_spinlock(&__fflush.lock);
|
||||
for (i = 0; i < __fflush.handles.i; ++i) {
|
||||
if ((f = __fflush.handles.p[i])) {
|
||||
_spinlock(&f->lock);
|
||||
flockfile(f);
|
||||
if (f->bufmode == _IOLBF) {
|
||||
fflush_unlocked(f);
|
||||
}
|
||||
_spunlock(&f->lock);
|
||||
funlockfile(f);
|
||||
}
|
||||
}
|
||||
_spunlock(&__fflush.lock);
|
||||
|
|
|
@ -18,11 +18,17 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Formats and writes text to stream.
|
||||
* @see printf() for further documentation
|
||||
*/
|
||||
int(fprintf)(FILE *f, const char *fmt, ...) {
|
||||
int rc;
|
||||
va_list va;
|
||||
flockfile(f);
|
||||
va_start(va, fmt);
|
||||
rc = (vfprintf)(f, fmt, va);
|
||||
rc = (vfprintf_unlocked)(f, fmt, va);
|
||||
va_end(va);
|
||||
funlockfile(f);
|
||||
return rc;
|
||||
}
|
||||
|
|
32
libc/stdio/fprintf_unlocked.c
Normal file
32
libc/stdio/fprintf_unlocked.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 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/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Formats and writes text to stream.
|
||||
* @see printf() for further documentation
|
||||
*/
|
||||
int(fprintf_unlocked)(FILE *f, const char *fmt, ...) {
|
||||
int rc;
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
rc = (vfprintf_unlocked)(f, fmt, va);
|
||||
va_end(va);
|
||||
return rc;
|
||||
}
|
|
@ -17,7 +17,6 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
|
@ -41,7 +40,7 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
|
|||
FILE *res;
|
||||
unsigned flags;
|
||||
flags = fopenflags(mode);
|
||||
_spinlock(&stream->lock);
|
||||
flockfile(stream);
|
||||
fflush_unlocked(stream);
|
||||
if (pathname) {
|
||||
/* open new stream, overwriting existing alloc */
|
||||
|
@ -60,6 +59,6 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
|
|||
fcntl(stream->fd, F_SETFL, flags & ~O_CLOEXEC);
|
||||
res = stream;
|
||||
}
|
||||
_spunlock(&stream->lock);
|
||||
funlockfile(stream);
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
@ -36,11 +35,10 @@
|
|||
* @param whence can be SEET_SET, SEEK_CUR, or SEEK_END
|
||||
* @returns 0 on success or -1 on error
|
||||
*/
|
||||
int fseeko(FILE *f, int64_t offset, int whence) {
|
||||
int fseeko_unlocked(FILE *f, int64_t offset, int whence) {
|
||||
int res;
|
||||
ssize_t rc;
|
||||
int64_t pos;
|
||||
_spinlock(&f->lock);
|
||||
if (f->fd != -1) {
|
||||
if (__fflush_impl(f) == -1) return -1;
|
||||
if (whence == SEEK_CUR && f->beg < f->end) {
|
||||
|
@ -77,6 +75,5 @@ int fseeko(FILE *f, int64_t offset, int whence) {
|
|||
res = -1;
|
||||
}
|
||||
}
|
||||
_spunlock(&f->lock);
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
@ -49,8 +48,8 @@ static int64_t ftello_unlocked(FILE *f) {
|
|||
*/
|
||||
int64_t ftello(FILE *f) {
|
||||
int64_t rc;
|
||||
_spinlock(&f->lock);
|
||||
flockfile(f);
|
||||
rc = ftello_unlocked(f);
|
||||
_spunlock(&f->lock);
|
||||
funlockfile(f);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,13 +16,23 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/lockcmpxchgp.h"
|
||||
#include "libc/nexgen32e/threaded.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Tries to acquire stdio object lock.
|
||||
* @return 0 for success or non-zero if someone else has the lock
|
||||
* Tries to acquire reentrant stdio object lock.
|
||||
*
|
||||
* @return 0 on success, or non-zero if another thread owns the lock
|
||||
*/
|
||||
int ftrylockfile(FILE *f) {
|
||||
return _trylock(&f->lock);
|
||||
int me, owner = 0;
|
||||
if (__threaded) {
|
||||
me = gettid();
|
||||
if (!_lockcmpxchgp(&f->lock, &owner, me) && owner == me) {
|
||||
owner = 0;
|
||||
}
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -87,8 +86,8 @@ static ssize_t getdelim_unlocked(char **s, size_t *n, int delim, FILE *f) {
|
|||
*/
|
||||
ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
|
||||
ssize_t rc;
|
||||
_spinlock(&f->lock);
|
||||
flockfile(f);
|
||||
rc = getdelim_unlocked(s, n, delim, f);
|
||||
_spunlock(&f->lock);
|
||||
funlockfile(f);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Formats and writes string to stdout.
|
||||
* Formats and writes text to stdout.
|
||||
*
|
||||
* Cosmopolitan supports most of the standard formatting behaviors
|
||||
* described by `man 3 printf`, in addition to the following
|
||||
|
|
|
@ -18,21 +18,32 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Writes string w/ trailing newline to stdout.
|
||||
*/
|
||||
int puts(const char *s) {
|
||||
FILE *f;
|
||||
static int PutsImpl(const char *s, FILE *f) {
|
||||
size_t n, r;
|
||||
f = stdout;
|
||||
if ((n = strlen(s))) {
|
||||
r = fwrite(s, 1, n, f);
|
||||
r = fwrite_unlocked(s, 1, n, f);
|
||||
if (!r) return -1;
|
||||
if (r < n) return r;
|
||||
}
|
||||
if (fputc('\n', f) == -1) {
|
||||
if (feof(f)) return n;
|
||||
if (fputc_unlocked('\n', f) == -1) {
|
||||
if (feof_unlocked(f)) return n;
|
||||
return -1;
|
||||
}
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes string w/ trailing newline to stdout.
|
||||
*
|
||||
* @return non-negative number on success, or `EOF` on error with
|
||||
* `errno` set and the `ferror(stdout)` state is updated
|
||||
*/
|
||||
int puts(const char *s) {
|
||||
FILE *f;
|
||||
int bytes;
|
||||
f = stdout;
|
||||
flockfile(f);
|
||||
bytes = PutsImpl(s, f);
|
||||
funlockfile(f);
|
||||
return bytes;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
* EOF state, without reopening it.
|
||||
*/
|
||||
void rewind(FILE *f) {
|
||||
fseek(f, 0, SEEK_SET);
|
||||
f->state = 0;
|
||||
flockfile(f);
|
||||
if (!fseeko_unlocked(f, 0, SEEK_SET)) {
|
||||
f->state = 0;
|
||||
}
|
||||
funlockfile(f);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
* @return 0 on success or -1 on error
|
||||
*/
|
||||
int setvbuf(FILE *f, char *buf, int mode, size_t size) {
|
||||
flockfile(f);
|
||||
if (buf) {
|
||||
if (!size) size = BUFSIZ;
|
||||
if (!f->nofree && f->buf != buf) free_s(&f->buf);
|
||||
|
@ -38,5 +39,6 @@ int setvbuf(FILE *f, char *buf, int mode, size_t size) {
|
|||
f->nofree = true;
|
||||
}
|
||||
f->bufmode = mode;
|
||||
funlockfile(f);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -116,30 +116,6 @@ int wprintf(const wchar_t *, ...);
|
|||
int wscanf(const wchar_t *, ...);
|
||||
int fwide(FILE *, int);
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § standard i/o » optimizations ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
#define getc(f) fgetc(f)
|
||||
#define getwc(f) fgetwc(f)
|
||||
#define putc(c, f) fputc(c, f)
|
||||
#define putwc(c, f) fputwc(c, f)
|
||||
|
||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
||||
#define printf(FMT, ...) (printf)(PFLINK(FMT), ##__VA_ARGS__)
|
||||
#define vprintf(FMT, VA) (vprintf)(PFLINK(FMT), VA)
|
||||
#define fprintf(F, FMT, ...) (fprintf)(F, PFLINK(FMT), ##__VA_ARGS__)
|
||||
#define vfprintf(F, FMT, VA) (vfprintf)(F, PFLINK(FMT), VA)
|
||||
#define vscanf(FMT, VA) (vscanf)(SFLINK(FMT), VA)
|
||||
#define scanf(FMT, ...) (scanf)(SFLINK(FMT), ##__VA_ARGS__)
|
||||
#define fscanf(F, FMT, ...) (fscanf)(F, SFLINK(FMT), ##__VA_ARGS__)
|
||||
#define vfscanf(F, FMT, VA) (vfscanf)(F, SFLINK(FMT), VA)
|
||||
#endif
|
||||
|
||||
#define stdin SYMBOLIC(stdin)
|
||||
#define stdout SYMBOLIC(stdout)
|
||||
#define stderr SYMBOLIC(stderr)
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § standard i/o » without mutexes ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
@ -172,12 +148,45 @@ wchar_t *fgetws_unlocked(wchar_t *, int, FILE *);
|
|||
int fputws_unlocked(const wchar_t *, FILE *);
|
||||
wint_t ungetwc_unlocked(wint_t, FILE *) paramsnonnull();
|
||||
int ungetc_unlocked(int, FILE *) paramsnonnull();
|
||||
int fseeko_unlocked(FILE *, int64_t, int) paramsnonnull();
|
||||
int fprintf_unlocked(FILE *, const char *, ...) printfesque(2)
|
||||
paramsnonnull((1, 2)) dontthrow nocallback;
|
||||
int vfprintf_unlocked(FILE *, const char *, va_list)
|
||||
paramsnonnull() dontthrow nocallback;
|
||||
|
||||
#define getc_unlocked(f) fgetc_unlocked(f)
|
||||
#define getwc_unlocked(f) fgetwc_unlocked(f)
|
||||
#define putc_unlocked(c, f) fputc_unlocked(c, f)
|
||||
#define putwc_unlocked(c, f) fputwc_unlocked(c, f)
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § standard i/o » optimizations ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
#define getc(f) fgetc(f)
|
||||
#define getwc(f) fgetwc(f)
|
||||
#define putc(c, f) fputc(c, f)
|
||||
#define putwc(c, f) fputwc(c, f)
|
||||
|
||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
||||
/* clang-format off */
|
||||
#define printf(FMT, ...) (printf)(PFLINK(FMT), ##__VA_ARGS__)
|
||||
#define vprintf(FMT, VA) (vprintf)(PFLINK(FMT), VA)
|
||||
#define fprintf(F, FMT, ...) (fprintf)(F, PFLINK(FMT), ##__VA_ARGS__)
|
||||
#define vfprintf(F, FMT, VA) (vfprintf)(F, PFLINK(FMT), VA)
|
||||
#define fprintf_unlocked(F, FMT, ...) (fprintf_unlocked)(F, PFLINK(FMT), ##__VA_ARGS__)
|
||||
#define vfprintf_unlocked(F, FMT, VA) (vfprintf_unlocked)(F, PFLINK(FMT), VA)
|
||||
#define vscanf(FMT, VA) (vscanf)(SFLINK(FMT), VA)
|
||||
#define scanf(FMT, ...) (scanf)(SFLINK(FMT), ##__VA_ARGS__)
|
||||
#define fscanf(F, FMT, ...) (fscanf)(F, SFLINK(FMT), ##__VA_ARGS__)
|
||||
#define vfscanf(F, FMT, VA) (vfscanf)(F, SFLINK(FMT), VA)
|
||||
/* clang-format on */
|
||||
#endif
|
||||
|
||||
#define stdin SYMBOLIC(stdin)
|
||||
#define stdout SYMBOLIC(stdout)
|
||||
#define stderr SYMBOLIC(stderr)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_STDIO_STDIO_H_ */
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
//
|
||||
// @param rdi has stream pointer
|
||||
// @see clearerr_unlocked()
|
||||
// @threadsafe
|
||||
clearerr:
|
||||
mov %rdi,%r11
|
||||
ezlea clearerr_unlocked,ax
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// @param rdi has file stream object pointer
|
||||
// @note EOF doesn't count
|
||||
// @see feof_unlocked()
|
||||
// @threadsafe
|
||||
feof: mov %rdi,%r11
|
||||
ezlea feof_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// @param rdi has file stream object pointer
|
||||
// @note EOF doesn't count
|
||||
// @see ferror_unlocked()
|
||||
// @threadsafe
|
||||
ferror: mov %rdi,%r11
|
||||
ezlea ferror_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -20,10 +20,14 @@
|
|||
|
||||
// Blocks until data from stream buffer is written out.
|
||||
//
|
||||
// @param rdi is the stream handle
|
||||
// @param rdi is the stream handle, or 0 for all streams
|
||||
// @return 0 on success or -1 w/ errno
|
||||
// @see fflush_unlocked()
|
||||
fflush: mov %rdi,%r11
|
||||
ezlea fflush_unlocked,ax
|
||||
// @threadsafe
|
||||
fflush: ezlea fflush_unlocked,ax
|
||||
test %rdi,%rdi
|
||||
jz 1f
|
||||
mov %rdi,%r11
|
||||
jmp stdio_unlock
|
||||
1: jmp *%rax
|
||||
.endfn fflush,globl
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// @param rdi has stream object pointer
|
||||
// @return byte in range 0..255, or -1 w/ errno
|
||||
// @see fgetc_unlocked()
|
||||
// @threadsafe
|
||||
fgetc: mov %rdi,%r11
|
||||
ezlea fgetc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
// @return rax has rdi on success, NULL on error or
|
||||
// NULL if EOF happens with zero chars read
|
||||
// @see fgets_unlocked()
|
||||
// @threadsafe
|
||||
fgets: mov %rdx,%r11
|
||||
ezlea fgets_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// @param rdi has stream object pointer
|
||||
// @return wide character or -1 on EOF or error
|
||||
// @see fgetwc_unlocked()
|
||||
// @threadsafe
|
||||
fgetwc: mov %rdi,%r11
|
||||
ezlea fgetwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
// @param rsi is size of rdi buffer
|
||||
// @param rsi is file stream object pointer
|
||||
// @see fgetws_unlocked()
|
||||
// @threadsafe
|
||||
fgetws: mov %rdx,%r11
|
||||
ezlea fgetws_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
//
|
||||
// @param rdi has file stream object pointer
|
||||
// @see fileno_unlocked()
|
||||
// @threadsafe
|
||||
fileno: mov %rdi,%r11
|
||||
ezlea fileno_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
// @param rsi has stream object pointer
|
||||
// @return c as unsigned char if written or -1 w/ errno
|
||||
// @see fputc_unlocked()
|
||||
// @threadsafe
|
||||
fputc: mov %rsi,%r11
|
||||
ezlea fputc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
// @param rsi is file object stream pointer
|
||||
// @return strlen(rdi) on success or -1 w/ errno
|
||||
// @see fputs_unlocked()
|
||||
// @threadsafe
|
||||
fputs: mov %rsi,%r11
|
||||
ezlea fputs_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
// @param rsi has file object stream pointer
|
||||
// @return rax is wide character if written or -1 w/ errno
|
||||
// @see fputwc_unlocked()
|
||||
// @threadsafe
|
||||
fputwc: mov %rsi,%r11
|
||||
ezlea fputwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
// @param rsi is file object stream pointer
|
||||
// @return strlen(rdi) on success or -1 w/ errno
|
||||
// @see fputws_unlocked()
|
||||
// @threadsafe
|
||||
fputws: mov %rsi,%r11
|
||||
ezlea fputws_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
// @param rcx has file object stream pointer
|
||||
// @return count on success, [0,count) on EOF, 0 on error or count==0
|
||||
// @see fread_unlocked()
|
||||
// @threadsafe
|
||||
fread: mov %rcx,%r11
|
||||
ezlea fread_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
37
libc/stdio/unlocked/fseeko_unlocked.S
Normal file
37
libc/stdio/unlocked/fseeko_unlocked.S
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 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/macros.internal.h"
|
||||
|
||||
// Repositions open file stream.
|
||||
//
|
||||
// This function flushes the buffer (unless it's currently in the EOF
|
||||
// state) and then calls lseek() on the underlying file. If the stream
|
||||
// is in the EOF state, this function can be used to restore it without
|
||||
// needing to reopen the file.
|
||||
//
|
||||
// @param rdi is stream handle
|
||||
// @param rsi is offset is the byte delta
|
||||
// @param rdx is whence and can be SEET_SET, SEEK_CUR, or SEEK_END
|
||||
// @return 0 on success or -1 w/ errno
|
||||
// @see fflush_unlocked()
|
||||
// @threadsafe
|
||||
fseeko: mov %rdi,%r11
|
||||
ezlea fseeko_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fseeko,globl
|
|
@ -26,6 +26,7 @@
|
|||
// @param rcx has file object stream pointer
|
||||
// @return count on success, [0,count) on EOF, 0 on error or count==0
|
||||
// @see fwrite_unlocked()
|
||||
// @threadsafe
|
||||
fwrite: mov %rcx,%r11
|
||||
ezlea fwrite_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// @param rdi has file stream object pointer
|
||||
// @return byte in range 0..255, or -1 w/ errno
|
||||
// @see fgetc_unlocked()
|
||||
// @threadsafe
|
||||
getc: mov %rdi,%r11
|
||||
ezlea fgetwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
//
|
||||
// @return byte in range 0..255, or -1 w/ errno
|
||||
// @see fgetc_unlocked()
|
||||
// @threadsafe
|
||||
getchar:
|
||||
mov stdin(%rip),%rdi
|
||||
mov %rdi,%r11
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// @param rdi has file stream object pointer
|
||||
// @return wide character or -1 on EOF or error
|
||||
// @see fgetwc_unlocked()
|
||||
// @threadsafe
|
||||
getwc: mov %rdi,%r11
|
||||
ezlea fgetwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
//
|
||||
// @return wide character or -1 on EOF or error
|
||||
// @see fgetwc_unlocked()
|
||||
// @threadsafe
|
||||
getwchar:
|
||||
mov stdin(%rip),%rdi
|
||||
mov %rdi,%r11
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
// @param rsi has stream object pointer
|
||||
// @return c as unsigned char if written or -1 w/ errno
|
||||
// @see fputc_unlocked()
|
||||
// @threadsafe
|
||||
putc: mov %rsi,%r11
|
||||
ezlea fputc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// @param rdi has character
|
||||
// @return c (as unsigned char) if written or -1 w/ errno
|
||||
// @see fputc_unlocked()
|
||||
// @threadsafe
|
||||
putchar:
|
||||
mov stdout(%rip),%rsi
|
||||
mov %rsi,%r11
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
// @param rsi has file object
|
||||
// @return wc if written or -1 w/ errno
|
||||
// @see putwc_unlocked()
|
||||
// @threadsafe
|
||||
putwc: mov %rsi,%r11
|
||||
ezlea fputwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// @param rdi has wide character
|
||||
// @return wc if written or -1 w/ errno
|
||||
// @see fputwc_unlocked()
|
||||
// @threadsafe
|
||||
putwchar:
|
||||
mov stdout(%rip),%rsi
|
||||
mov %rsi,%r11
|
||||
|
|
|
@ -18,53 +18,54 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
#define LOCK 0x2c /* see struct file in stdio.h */
|
||||
|
||||
// Wrapper for applying locking to stdio functions.
|
||||
//
|
||||
// This function is intended to be called by thunks.
|
||||
//
|
||||
// @param rax has the delegate function pointer
|
||||
// @param rax is stdio function pointer
|
||||
// @param rdi is passed along as an arg
|
||||
// @param rsi is passed along as an arg
|
||||
// @param rdx is passed along as an arg
|
||||
// @param rcx is passed along as an arg
|
||||
// @param r8 is passed along as an arg
|
||||
// @param r9 is passed along as an arg
|
||||
// @param r10 is passed along as an arg
|
||||
// @param r11 has the FILE* obj pointer
|
||||
// @return rax is passed along as result
|
||||
// @return rdx is passed along as result
|
||||
// @threadsafe
|
||||
stdio_unlock:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable
|
||||
|
||||
// acquires mutex
|
||||
push %rcx
|
||||
push %rax
|
||||
push %rdi
|
||||
push %rsi
|
||||
push %rdx
|
||||
mov $1,%cl
|
||||
0: mov LOCK(%r11),%dl # optimistic
|
||||
test %dl,%dl
|
||||
je 2f
|
||||
1: pause # hyperyield
|
||||
jmp 0b
|
||||
2: mov %ecx,%edx
|
||||
xchg LOCK(%r11),%dl # locks bus!
|
||||
test %dl,%dl
|
||||
jne 1b
|
||||
pop %rdx
|
||||
push %rcx
|
||||
push %r11
|
||||
mov %r11,%rdi
|
||||
call flockfile
|
||||
pop %r11
|
||||
pop %rcx
|
||||
pop %rdx
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
pop %rax
|
||||
|
||||
// calls delegate
|
||||
push %rsi
|
||||
push %r11
|
||||
push %rsi # align stack
|
||||
call *%rax
|
||||
pop %r11
|
||||
pop %rsi
|
||||
pop %r11
|
||||
|
||||
// releases mutex
|
||||
movb $0,LOCK(%r11)
|
||||
push %rax
|
||||
push %rdx
|
||||
mov %r11,%rdi
|
||||
call funlockfile
|
||||
pop %rdx
|
||||
pop %rax
|
||||
|
||||
pop %rbp
|
||||
ret
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
// @param rds has stream object pointer
|
||||
// @return rax has rdi on success or -1 w/ errno
|
||||
// @see ungetc_unlocked()
|
||||
// @threadsafe
|
||||
ungetc: mov %rsi,%r11
|
||||
ezlea ungetc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
// @param rds has stream object pointer
|
||||
// @return rax has rdi on success or -1 w/ errno
|
||||
// @see ungetwc_unlocked()
|
||||
// @threadsafe
|
||||
ungetwc:
|
||||
mov %rsi,%r11
|
||||
ezlea ungetwc_unlocked,ax
|
||||
|
|
|
@ -16,45 +16,16 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
struct state {
|
||||
FILE *f;
|
||||
int n;
|
||||
};
|
||||
|
||||
static int vfprintfputchar(const char *s, struct state *t, size_t n) {
|
||||
int rc;
|
||||
if (n) {
|
||||
_spinlock(&t->f->lock);
|
||||
if (n == 1 && *s != '\n' && t->f->beg < t->f->size &&
|
||||
t->f->bufmode != _IONBF) {
|
||||
t->f->buf[t->f->beg++] = *s;
|
||||
t->n += n;
|
||||
rc = 0;
|
||||
} else if (!fwrite_unlocked(s, 1, n, t->f)) {
|
||||
rc = -1;
|
||||
} else {
|
||||
t->n += n;
|
||||
rc = 0;
|
||||
}
|
||||
_spunlock(&t->f->lock);
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats and writes text to stream.
|
||||
* @see printf() for further documentation
|
||||
*/
|
||||
int(vfprintf)(FILE *f, const char *fmt, va_list va) {
|
||||
struct state st[1] = {{f, 0}};
|
||||
if (__fmt(vfprintfputchar, st, fmt, va) != -1) {
|
||||
return st->n;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
int rc;
|
||||
flockfile(f);
|
||||
rc = (vfprintf_unlocked)(f, fmt, va);
|
||||
funlockfile(f);
|
||||
return rc;
|
||||
}
|
||||
|
|
61
libc/stdio/vfprintf_unlocked.c
Normal file
61
libc/stdio/vfprintf_unlocked.c
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 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/fmt/fmt.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
struct state {
|
||||
FILE *f;
|
||||
int n;
|
||||
};
|
||||
|
||||
static int vfprintfputchar(const char *s, struct state *t, size_t n) {
|
||||
int rc;
|
||||
if (n) {
|
||||
if (n == 1 && *s != '\n' && t->f->beg < t->f->size &&
|
||||
t->f->bufmode != _IONBF) {
|
||||
t->f->buf[t->f->beg++] = *s;
|
||||
t->n += n;
|
||||
rc = 0;
|
||||
} else if (!fwrite_unlocked(s, 1, n, t->f)) {
|
||||
rc = -1;
|
||||
} else {
|
||||
t->n += n;
|
||||
rc = 0;
|
||||
}
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats and writes text to stream.
|
||||
* @see printf() for further documentation
|
||||
*/
|
||||
int(vfprintf_unlocked)(FILE *f, const char *fmt, va_list va) {
|
||||
int rc;
|
||||
struct state st[1] = {{f, 0}};
|
||||
if ((rc = __fmt(vfprintfputchar, st, fmt, va)) != -1) {
|
||||
rc = st->n;
|
||||
}
|
||||
return rc;
|
||||
}
|
|
@ -18,6 +18,10 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Formats and writes text to stdout.
|
||||
* @see printf() for further documentation
|
||||
*/
|
||||
int(vprintf)(const char* fmt, va_list va) {
|
||||
return (vfprintf)(stdout, fmt, va);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
FILE *f;
|
||||
|
@ -60,3 +61,13 @@ TEST(fgetc, testUnbuffered) {
|
|||
EXPECT_TRUE(feof(f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
}
|
||||
|
||||
BENCH(fputc, bench) {
|
||||
FILE *f;
|
||||
ASSERT_NE(NULL, (f = fopen("/dev/null", "w")));
|
||||
EZBENCH2("fputc", donothing, fputc('E', f));
|
||||
flockfile(f);
|
||||
EZBENCH2("fputc_unlocked", donothing, fputc_unlocked('E', f));
|
||||
funlockfile(f);
|
||||
fclose(f);
|
||||
}
|
||||
|
|
14
third_party/mbedtls/ssl.h
vendored
14
third_party/mbedtls/ssl.h
vendored
|
@ -12,20 +12,6 @@
|
|||
COSMOPOLITAN_C_START_
|
||||
/* clang-format off */
|
||||
|
||||
#define MBEDTLS_ERR_NET_SOCKET_FAILED -0x0042 /*< Failed to open a socket. */
|
||||
#define MBEDTLS_ERR_NET_CONNECT_FAILED -0x0044 /*< The connection to the given server / port failed. */
|
||||
#define MBEDTLS_ERR_NET_BIND_FAILED -0x0046 /*< Binding of the socket failed. */
|
||||
#define MBEDTLS_ERR_NET_LISTEN_FAILED -0x0048 /*< Could not listen on the socket. */
|
||||
#define MBEDTLS_ERR_NET_ACCEPT_FAILED -0x004A /*< Could not accept the incoming connection. */
|
||||
#define MBEDTLS_ERR_NET_RECV_FAILED -0x004C /*< Reading information from the socket failed. */
|
||||
#define MBEDTLS_ERR_NET_SEND_FAILED -0x004E /*< Sending information through the socket failed. */
|
||||
#define MBEDTLS_ERR_NET_CONN_RESET -0x0050 /*< Connection was reset by peer. */
|
||||
#define MBEDTLS_ERR_NET_UNKNOWN_HOST -0x0052 /*< Failed to get an IP address for the given hostname. */
|
||||
#define MBEDTLS_ERR_NET_BUFFER_TOO_SMALL -0x0043 /*< Buffer is too small to hold the data. */
|
||||
#define MBEDTLS_ERR_NET_INVALID_CONTEXT -0x0045 /*< The context is invalid, eg because it was free()ed. */
|
||||
#define MBEDTLS_ERR_NET_POLL_FAILED -0x0047 /*< Polling the net context failed. */
|
||||
#define MBEDTLS_ERR_NET_BAD_INPUT_DATA -0x0049 /*< Input invalid. */
|
||||
|
||||
/*
|
||||
* SSL Error codes
|
||||
*/
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "third_party/mbedtls/ctr_drbg.h"
|
||||
#include "third_party/mbedtls/ecp.h"
|
||||
#include "third_party/mbedtls/error.h"
|
||||
#include "third_party/mbedtls/net_sockets.h"
|
||||
#include "third_party/mbedtls/platform.h"
|
||||
#include "third_party/mbedtls/ssl.h"
|
||||
#include "tool/build/lib/eztls.h"
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "net/https/https.h"
|
||||
#include "third_party/mbedtls/net_sockets.h"
|
||||
#include "third_party/mbedtls/ssl.h"
|
||||
#include "third_party/zlib/zlib.h"
|
||||
#include "tool/build/lib/eztls.h"
|
||||
|
@ -112,6 +113,7 @@ static const struct addrinfo kResolvHints = {.ai_family = AF_INET,
|
|||
|
||||
int g_sock;
|
||||
char *g_prog;
|
||||
long g_backoff;
|
||||
char *g_runitd;
|
||||
jmp_buf g_jmpbuf;
|
||||
uint16_t g_sshport;
|
||||
|
@ -308,7 +310,7 @@ TryAgain:
|
|||
freeaddrinfo(ai);
|
||||
}
|
||||
|
||||
static void Send(const void *output, size_t outputsize) {
|
||||
static bool Send(const void *output, size_t outputsize) {
|
||||
int rc, have;
|
||||
static bool once;
|
||||
static z_stream zs;
|
||||
|
@ -326,11 +328,17 @@ static void Send(const void *output, size_t outputsize) {
|
|||
rc = deflate(&zs, Z_SYNC_FLUSH);
|
||||
CHECK_NE(Z_STREAM_ERROR, rc);
|
||||
have = sizeof(zbuf) - zs.avail_out;
|
||||
CHECK_EQ(have, mbedtls_ssl_write(&ezssl, zbuf, have));
|
||||
rc = mbedtls_ssl_write(&ezssl, zbuf, have);
|
||||
if (rc == MBEDTLS_ERR_NET_CONN_RESET) {
|
||||
usleep((g_backoff = (g_backoff + 1000) * 2));
|
||||
return false;
|
||||
}
|
||||
CHECK_EQ(have, rc);
|
||||
} while (!zs.avail_out);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SendRequest(void) {
|
||||
int SendRequest(void) {
|
||||
int fd;
|
||||
char *p;
|
||||
size_t i;
|
||||
|
@ -357,22 +365,30 @@ void SendRequest(void) {
|
|||
q = mempcpy(q, name, namesize);
|
||||
assert(hdrsize == q - hdr);
|
||||
DEBUGF("running %s on %s", g_prog, g_hostname);
|
||||
Send(hdr, hdrsize);
|
||||
Send(p, progsize);
|
||||
CHECK_EQ(0, EzTlsFlush(&ezbio, 0, 0));
|
||||
if (Send(hdr, hdrsize) && Send(p, progsize)) {
|
||||
if (!(rc = EzTlsFlush(&ezbio, 0, 0))) {
|
||||
rc = 0;
|
||||
} else if (rc == MBEDTLS_ERR_NET_CONN_RESET) {
|
||||
rc = -1;
|
||||
} else {
|
||||
CHECK_EQ(0, rc);
|
||||
}
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
CHECK_NE(-1, munmap(p, st.st_size));
|
||||
CHECK_NE(-1, close(fd));
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool Recv(unsigned char *p, size_t n) {
|
||||
size_t i, rc;
|
||||
static long backoff;
|
||||
for (i = 0; i < n; i += rc) {
|
||||
do {
|
||||
rc = mbedtls_ssl_read(&ezssl, p + i, n - i);
|
||||
} while (rc == MBEDTLS_ERR_SSL_WANT_READ);
|
||||
if (!rc || rc == MBEDTLS_ERR_NET_CONN_RESET) {
|
||||
usleep((backoff = (backoff + 1000) * 2));
|
||||
usleep((g_backoff = (g_backoff + 1000) * 2));
|
||||
return false;
|
||||
} else if (rc < 0) {
|
||||
TlsDie("read response failed", rc);
|
||||
|
@ -442,8 +458,7 @@ int RunOnHost(char *spec) {
|
|||
WARNF("warning: got connection reset in handshake");
|
||||
close(g_sock);
|
||||
}
|
||||
SendRequest();
|
||||
} while ((rc = ReadResponse()) == -1);
|
||||
} while ((rc = SendRequest()) == -1 || (rc = ReadResponse()) == -1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/so.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
#include "libc/sysv/consts/sol.h"
|
||||
|
@ -110,7 +111,12 @@ void OnInterrupt(int sig) {
|
|||
|
||||
void OnChildTerminated(int sig) {
|
||||
int ws, pid;
|
||||
sigset_t ss, oldss;
|
||||
sigfillset(&ss);
|
||||
sigdelset(&ss, SIGTERM);
|
||||
sigprocmask(SIG_BLOCK, &ss, &oldss);
|
||||
for (;;) {
|
||||
INFOF("waitpid");
|
||||
if ((pid = waitpid(-1, &ws, WNOHANG)) != -1) {
|
||||
if (pid) {
|
||||
if (WIFEXITED(ws)) {
|
||||
|
@ -127,6 +133,7 @@ void OnChildTerminated(int sig) {
|
|||
FATALF("waitpid failed in sigchld");
|
||||
}
|
||||
}
|
||||
sigprocmask(SIG_SETMASK, &oldss, 0);
|
||||
}
|
||||
|
||||
wontreturn void ShowUsage(FILE *f, int rc) {
|
||||
|
@ -229,6 +236,7 @@ void SendExitMessage(int rc) {
|
|||
msg[0 + 3] = (RUNITD_MAGIC & 0x000000ff) >> 000;
|
||||
msg[4] = kRunitExit;
|
||||
msg[5] = rc;
|
||||
INFOF("mbedtls_ssl_write");
|
||||
CHECK_EQ(sizeof(msg), mbedtls_ssl_write(&ezssl, msg, sizeof(msg)));
|
||||
CHECK_EQ(0, EzTlsFlush(&ezbio, 0, 0));
|
||||
}
|
||||
|
@ -247,6 +255,7 @@ void SendOutputFragmentMessage(enum RunitCommand kind, unsigned char *buf,
|
|||
msg[5 + 1] = (size & 0x00ff0000) >> 020;
|
||||
msg[5 + 2] = (size & 0x0000ff00) >> 010;
|
||||
msg[5 + 3] = (size & 0x000000ff) >> 000;
|
||||
INFOF("mbedtls_ssl_write");
|
||||
CHECK_EQ(sizeof(msg), mbedtls_ssl_write(&ezssl, msg, sizeof(msg)));
|
||||
while (size) {
|
||||
CHECK_NE(-1, (rc = mbedtls_ssl_write(&ezssl, buf, size)));
|
||||
|
@ -294,6 +303,7 @@ void Recv(void *output, size_t outputsize) {
|
|||
// get another fixed-size data packet from network
|
||||
// pass along error conditions to caller
|
||||
// pass along eof condition to zlib
|
||||
INFOF("mbedtls_ssl_read");
|
||||
received = mbedtls_ssl_read(&ezssl, buf, sizeof(buf));
|
||||
if (received < 0) TlsDie("read failed", received);
|
||||
// decompress packet completely
|
||||
|
@ -347,12 +357,14 @@ void HandleClient(void) {
|
|||
|
||||
/* read request to run program */
|
||||
addrsize = sizeof(addr);
|
||||
INFOF("accept");
|
||||
CHECK_NE(-1, (g_clifd = accept4(g_servfd, &addr, &addrsize, SOCK_CLOEXEC)));
|
||||
if (fork()) {
|
||||
close(g_clifd);
|
||||
return;
|
||||
}
|
||||
EzFd(g_clifd);
|
||||
INFOF("EzHandshake");
|
||||
EzHandshake();
|
||||
addrstr = gc(DescribeAddress(&addr));
|
||||
DEBUGF("%s %s %s", gc(DescribeAddress(&g_servaddr)), "accepted", addrstr);
|
||||
|
@ -376,6 +388,7 @@ void HandleClient(void) {
|
|||
}
|
||||
CHECK_NE(-1, (g_exefd = creat(g_exepath, 0700)));
|
||||
LOGIFNEG1(ftruncate(g_exefd, filesize));
|
||||
INFOF("xwrite");
|
||||
CHECK_NE(-1, xwrite(g_exefd, exe, filesize));
|
||||
LOGIFNEG1(close(g_exefd));
|
||||
|
||||
|
@ -425,6 +438,7 @@ void HandleClient(void) {
|
|||
fds[0].events = POLLIN;
|
||||
fds[1].fd = pipefds[0];
|
||||
fds[1].events = POLLIN;
|
||||
INFOF("poll");
|
||||
events = poll(fds, ARRAYLEN(fds), (deadline - now) * 1000);
|
||||
CHECK_NE(-1, events); // EINTR shouldn't be possible
|
||||
if (fds[0].revents) {
|
||||
|
@ -440,6 +454,7 @@ void HandleClient(void) {
|
|||
LOGIFNEG1(unlink(g_exepath));
|
||||
_exit(1);
|
||||
}
|
||||
INFOF("read");
|
||||
got = read(pipefds[0], g_buf, sizeof(g_buf));
|
||||
CHECK_NE(-1, got); // EINTR shouldn't be possible
|
||||
if (!got) {
|
||||
|
@ -449,6 +464,7 @@ void HandleClient(void) {
|
|||
fwrite(g_buf, got, 1, stderr);
|
||||
SendOutputFragmentMessage(kRunitStderr, g_buf, got);
|
||||
}
|
||||
INFOF("waitpid");
|
||||
CHECK_NE(-1, waitpid(child, &wstatus, 0)); // EINTR shouldn't be possible
|
||||
if (WIFEXITED(wstatus)) {
|
||||
if (WEXITSTATUS(wstatus)) {
|
||||
|
@ -463,6 +479,7 @@ void HandleClient(void) {
|
|||
}
|
||||
LOGIFNEG1(unlink(g_exepath));
|
||||
SendExitMessage(exitcode);
|
||||
INFOF("mbedtls_ssl_close_notify");
|
||||
mbedtls_ssl_close_notify(&ezssl);
|
||||
LOGIFNEG1(close(g_clifd));
|
||||
_exit(0);
|
||||
|
@ -524,7 +541,7 @@ void Daemonize(void) {
|
|||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER, GetRunitPsk());
|
||||
/* __log_level = kLogDebug; */
|
||||
__log_level = kLogInfo;
|
||||
GetOpts(argc, argv);
|
||||
for (i = 3; i < 16; ++i) close(i);
|
||||
errno = 0;
|
||||
|
|
|
@ -108,6 +108,7 @@
|
|||
"protip"
|
||||
"nxbitsafe"
|
||||
"vforksafe"
|
||||
"threadsafe"
|
||||
"preinitsafe"
|
||||
"asyncsignalsafe"
|
||||
"notasyncsignalsafe"
|
||||
|
|
|
@ -146,6 +146,7 @@
|
|||
#include "third_party/mbedtls/iana.h"
|
||||
#include "third_party/mbedtls/md.h"
|
||||
#include "third_party/mbedtls/md5.h"
|
||||
#include "third_party/mbedtls/net_sockets.h"
|
||||
#include "third_party/mbedtls/oid.h"
|
||||
#include "third_party/mbedtls/pk.h"
|
||||
#include "third_party/mbedtls/rsa.h"
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "third_party/mbedtls/ctr_drbg.h"
|
||||
#include "third_party/mbedtls/debug.h"
|
||||
#include "third_party/mbedtls/error.h"
|
||||
#include "third_party/mbedtls/net_sockets.h"
|
||||
#include "third_party/mbedtls/ssl.h"
|
||||
|
||||
#define OPTS "BIqksvzX:H:C:m:"
|
||||
|
|
Loading…
Add table
Reference in a new issue