mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-01 00:38:31 +00:00
Eliminate cyclic locks in runtime
This change introduces a new deadlock detector for Cosmo's POSIX threads implementation. Error check mutexes will now track a DAG of nested locks and report EDEADLK when a deadlock is theoretically possible. These will occur rarely, but it's important for production hardening your code. You don't even need to change your mutexes to use the POSIX error check mode because `cosmocc -mdbg` will enable error checking on mutexes by default globally. When cycles are found, an error message showing your demangled symbols describing the strongly connected component are printed and then the SIGTRAP is raised, which means you'll also get a backtrace if you're using ShowCrashReports() too. This new error checker is so low-level and so pure that it's able to verify the relationships of every libc runtime lock, including those locks upon which the mutex implementation depends.
This commit is contained in:
parent
26c051c297
commit
af7bd80430
141 changed files with 2094 additions and 1601 deletions
|
@ -32,12 +32,13 @@ LIBC_STDIO_A_DIRECTDEPS = \
|
|||
LIBC_NEXGEN32E \
|
||||
LIBC_NT_ADVAPI32 \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_PROC \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
THIRD_PARTY_GDTOA
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
THIRD_PARTY_GDTOA \
|
||||
|
||||
LIBC_STDIO_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(LIBC_STDIO_A_DIRECTDEPS),$($(x))))
|
||||
|
|
|
@ -22,20 +22,14 @@
|
|||
|
||||
FILE *__stdio_alloc(void) {
|
||||
FILE *f;
|
||||
__stdio_lock();
|
||||
if ((f = calloc(1, sizeof(FILE)))) {
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&f->lock, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
f->dynamic = 1;
|
||||
f->freethis = 1;
|
||||
f->fd = -1;
|
||||
f->lock = (pthread_mutex_t)PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||
dll_init(&f->elem);
|
||||
dll_make_last(&__stdio.files, &f->elem);
|
||||
}
|
||||
__stdio_unlock();
|
||||
return f;
|
||||
}
|
||||
|
||||
void __stdio_free(FILE *f) {
|
||||
pthread_mutex_destroy(&f->lock);
|
||||
if (f->dynamic) {
|
||||
free(f);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,47 +16,26 @@
|
|||
│ 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/errno.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Closes standard i/o stream and its underlying thing.
|
||||
*
|
||||
* @param f is the file object
|
||||
* @return 0 on success or -1 on error, which can be a trick for
|
||||
* differentiating between EOF and real errors during previous
|
||||
* i/o calls, without needing to call ferror()
|
||||
* @return 0 on success, or EOF w/ errno
|
||||
*/
|
||||
int fclose(FILE *f) {
|
||||
int rc;
|
||||
if (!f)
|
||||
return 0;
|
||||
__fflush_unregister(f);
|
||||
fflush(f);
|
||||
if (_weaken(free)) {
|
||||
_weaken(free)(f->getln);
|
||||
if (!f->nofree && f->buf != f->mem) {
|
||||
_weaken(free)(f->buf);
|
||||
}
|
||||
}
|
||||
f->state = EOF;
|
||||
if (f->noclose) {
|
||||
int rc = 0;
|
||||
if (f) {
|
||||
flockfile(f);
|
||||
rc |= fflush(f);
|
||||
int fd = f->fd;
|
||||
f->fd = -1;
|
||||
} else if (f->fd != -1 && close(f->fd) == -1) {
|
||||
f->state = errno;
|
||||
f->state = EOF;
|
||||
if (fd != -1)
|
||||
rc |= close(fd);
|
||||
funlockfile(f);
|
||||
__stdio_unref(f);
|
||||
}
|
||||
if (f->state == EOF) {
|
||||
rc = 0;
|
||||
} else {
|
||||
errno = f->state;
|
||||
rc = EOF;
|
||||
}
|
||||
__stdio_free(f);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,14 +16,12 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
__static_yoink("fflush");
|
||||
|
||||
/**
|
||||
* Allocates stream object for already-opened file descriptor.
|
||||
|
@ -38,16 +36,16 @@ FILE *fdopen(int fd, const char *mode) {
|
|||
struct stat st;
|
||||
if (fstat(fd, &st))
|
||||
return 0;
|
||||
if ((f = __stdio_alloc())) {
|
||||
f->fd = fd;
|
||||
f->bufmode = S_ISREG(st.st_mode) ? _IOFBF : _IONBF;
|
||||
f->iomode = fopenflags(mode);
|
||||
f->buf = f->mem;
|
||||
f->size = BUFSIZ;
|
||||
if ((f->iomode & O_ACCMODE) != O_RDONLY) {
|
||||
__fflush_register(f);
|
||||
}
|
||||
return f;
|
||||
if (!(f = __stdio_alloc()))
|
||||
return 0;
|
||||
f->bufmode = S_ISCHR(st.st_mode) ? _IONBF : _IOFBF;
|
||||
f->oflags = fopenflags(mode);
|
||||
f->size = BUFSIZ;
|
||||
if (!(f->buf = malloc(f->size))) {
|
||||
__stdio_unref(f);
|
||||
return 0;
|
||||
}
|
||||
return NULL;
|
||||
f->freebuf = 1;
|
||||
f->fd = fd;
|
||||
return f;
|
||||
}
|
||||
|
|
|
@ -16,20 +16,38 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/cxxabi.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
|
||||
/**
|
||||
* Blocks until data from stream buffer is written out.
|
||||
*
|
||||
* @param f is the stream handle, or 0 for all streams
|
||||
* @return is 0 on success or -1 on error
|
||||
* @return is 0 on success or EOF on error
|
||||
*/
|
||||
int fflush(FILE *f) {
|
||||
int rc;
|
||||
if (f)
|
||||
if (f) {
|
||||
flockfile(f);
|
||||
rc = fflush_unlocked(f);
|
||||
if (f)
|
||||
rc = fflush_unlocked(f);
|
||||
funlockfile(f);
|
||||
} else {
|
||||
__stdio_lock();
|
||||
struct Dll *e, *e2;
|
||||
for (rc = 0, e = dll_last(__stdio.files); e; e = e2) {
|
||||
f = FILE_CONTAINER(e);
|
||||
__stdio_ref(f);
|
||||
__stdio_unlock();
|
||||
rc |= fflush(FILE_CONTAINER(e));
|
||||
__stdio_lock();
|
||||
e2 = dll_prev(__stdio.files, e);
|
||||
__stdio_unref_unlocked(f);
|
||||
}
|
||||
__stdio_unlock();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
__attribute__((__constructor__(60))) static textstartup void fflush_init(void) {
|
||||
__cxa_atexit((void *)fflush, 0, 0);
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_STDIO_FFLUSH_H_
|
||||
#define COSMOPOLITAN_LIBC_STDIO_FFLUSH_H_
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct StdioFlushHandles {
|
||||
size_t i, n;
|
||||
FILE **p;
|
||||
};
|
||||
|
||||
struct StdioFlush {
|
||||
struct StdioFlushHandles handles;
|
||||
FILE *handles_initmem[8];
|
||||
};
|
||||
|
||||
extern struct StdioFlush __fflush;
|
||||
extern pthread_mutex_t __fflush_lock_obj;
|
||||
|
||||
void __fflush_lock(void);
|
||||
void __fflush_unlock(void);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_STDIO_FFLUSH_H_ */
|
|
@ -16,75 +16,46 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/cxxabi.h"
|
||||
#include "libc/intrin/pushpop.h"
|
||||
#include "libc/mem/arraylist.internal.h"
|
||||
#include "libc/stdio/fflush.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Blocks until data from stream buffer is written out.
|
||||
*
|
||||
* @param f is the stream handle, or 0 for all streams
|
||||
* @return is 0 on success or -1 on error
|
||||
* @param f is the stream handle, which must not be null
|
||||
* @return is 0 on success or EOF on error
|
||||
*/
|
||||
int fflush_unlocked(FILE *f) {
|
||||
int rc = 0;
|
||||
size_t i;
|
||||
if (!f) {
|
||||
__fflush_lock();
|
||||
for (i = __fflush.handles.i; i; --i) {
|
||||
if ((f = __fflush.handles.p[i - 1])) {
|
||||
if (fflush(f) == -1) {
|
||||
rc = -1;
|
||||
if (f->getln) {
|
||||
if (_weaken(free))
|
||||
_weaken(free)(f->getln);
|
||||
f->getln = 0;
|
||||
}
|
||||
if (f->fd != -1) {
|
||||
if (f->beg && !f->end && (f->oflags & O_ACCMODE) != O_RDONLY) {
|
||||
ssize_t rc;
|
||||
for (i = 0; i < f->beg; i += rc) {
|
||||
if ((rc = write(f->fd, f->buf + i, f->beg - i)) == -1) {
|
||||
f->state = errno;
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
f->beg = 0;
|
||||
}
|
||||
__fflush_unlock();
|
||||
} else if (f->fd != -1) {
|
||||
if (__fflush_impl(f) == -1) {
|
||||
rc = -1;
|
||||
if (f->beg < f->end && (f->oflags & O_ACCMODE) != O_WRONLY) {
|
||||
if (lseek(f->fd, -(int)(f->end - f->beg), SEEK_CUR) == -1) {
|
||||
f->state = errno;
|
||||
return EOF;
|
||||
}
|
||||
f->end = f->beg;
|
||||
}
|
||||
} else if (f->beg && f->beg < f->size) {
|
||||
}
|
||||
if (f->buf && f->beg && f->beg < f->size)
|
||||
f->buf[f->beg] = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
textstartup int __fflush_register(FILE *f) {
|
||||
int rc;
|
||||
size_t i;
|
||||
struct StdioFlush *sf;
|
||||
__fflush_lock();
|
||||
sf = &__fflush;
|
||||
if (!sf->handles.p) {
|
||||
sf->handles.p = sf->handles_initmem;
|
||||
pushmov(&sf->handles.n, ARRAYLEN(sf->handles_initmem));
|
||||
__cxa_atexit((void *)fflush_unlocked, 0, 0);
|
||||
}
|
||||
for (i = sf->handles.i; i; --i) {
|
||||
if (!sf->handles.p[i - 1]) {
|
||||
sf->handles.p[i - 1] = f;
|
||||
__fflush_unlock();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
rc = append(&sf->handles, &f);
|
||||
__fflush_unlock();
|
||||
return rc;
|
||||
}
|
||||
|
||||
void __fflush_unregister(FILE *f) {
|
||||
size_t i;
|
||||
struct StdioFlush *sf;
|
||||
__fflush_lock();
|
||||
sf = &__fflush;
|
||||
sf = pushpop(sf);
|
||||
for (i = sf->handles.i; i; --i) {
|
||||
if (sf->handles.p[i - 1] == f) {
|
||||
pushmov(&sf->handles.p[i - 1], 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
__fflush_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 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/errno.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
int __fflush_impl(FILE *f) {
|
||||
size_t i;
|
||||
ssize_t rc;
|
||||
if (f->getln) {
|
||||
if (_weaken(free)) {
|
||||
_weaken(free)(f->getln);
|
||||
}
|
||||
f->getln = 0;
|
||||
}
|
||||
if (f->fd != -1) {
|
||||
if (f->beg && !f->end && (f->iomode & O_ACCMODE) != O_RDONLY) {
|
||||
for (i = 0; i < f->beg; i += rc) {
|
||||
if ((rc = write(f->fd, f->buf + i, f->beg - i)) == -1) {
|
||||
f->state = errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
f->beg = 0;
|
||||
}
|
||||
if (f->beg < f->end && (f->iomode & O_ACCMODE) != O_WRONLY) {
|
||||
if (lseek(f->fd, -(int)(f->end - f->beg), SEEK_CUR) == -1) {
|
||||
f->state = errno;
|
||||
return -1;
|
||||
}
|
||||
f->end = f->beg;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -17,10 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/stdio/fflush.internal.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
|
@ -30,39 +27,3 @@ void flockfile(FILE *f) {
|
|||
unassert(f != NULL);
|
||||
pthread_mutex_lock(&f->lock);
|
||||
}
|
||||
|
||||
void(__fflush_lock)(void) {
|
||||
pthread_mutex_lock(&__fflush_lock_obj);
|
||||
}
|
||||
|
||||
void(__fflush_unlock)(void) {
|
||||
pthread_mutex_unlock(&__fflush_lock_obj);
|
||||
}
|
||||
|
||||
static void __stdio_fork_prepare(void) {
|
||||
FILE *f;
|
||||
__fflush_lock();
|
||||
for (int i = 0; i < __fflush.handles.i; ++i)
|
||||
if ((f = __fflush.handles.p[i]))
|
||||
pthread_mutex_lock(&f->lock);
|
||||
}
|
||||
|
||||
static void __stdio_fork_parent(void) {
|
||||
FILE *f;
|
||||
for (int i = __fflush.handles.i; i--;)
|
||||
if ((f = __fflush.handles.p[i]))
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
__fflush_unlock();
|
||||
}
|
||||
|
||||
static void __stdio_fork_child(void) {
|
||||
FILE *f;
|
||||
for (int i = __fflush.handles.i; i--;)
|
||||
if ((f = __fflush.handles.p[i]))
|
||||
f->lock = (pthread_mutex_t)PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||
pthread_mutex_init(&__fflush_lock_obj, 0);
|
||||
}
|
||||
|
||||
__attribute__((__constructor__(60))) static textstartup void stdioinit(void) {
|
||||
pthread_atfork(__stdio_fork_prepare, __stdio_fork_parent, __stdio_fork_child);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/stdio/fflush.internal.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/stdio_ext.h"
|
||||
|
@ -26,17 +25,18 @@
|
|||
* Flushes all line-buffered streams.
|
||||
*/
|
||||
void _flushlbf(void) {
|
||||
int i;
|
||||
FILE *f;
|
||||
__fflush_lock();
|
||||
for (i = 0; i < __fflush.handles.i; ++i) {
|
||||
if ((f = __fflush.handles.p[i])) {
|
||||
flockfile(f);
|
||||
if (f->bufmode == _IOLBF) {
|
||||
fflush_unlocked(f);
|
||||
}
|
||||
funlockfile(f);
|
||||
__stdio_lock();
|
||||
struct Dll *e, *e2;
|
||||
for (e = dll_last(__stdio.files); e; e = e2) {
|
||||
FILE *f = FILE_CONTAINER(e);
|
||||
if (f->bufmode == _IOLBF) {
|
||||
__stdio_ref(f);
|
||||
__stdio_unlock();
|
||||
fflush(FILE_CONTAINER(e));
|
||||
__stdio_lock();
|
||||
e2 = dll_prev(__stdio.files, e);
|
||||
__stdio_unref_unlocked(f);
|
||||
}
|
||||
}
|
||||
__fflush_unlock();
|
||||
__stdio_unlock();
|
||||
}
|
||||
|
|
|
@ -37,36 +37,31 @@
|
|||
FILE *fmemopen(void *buf, size_t size, const char *mode) {
|
||||
FILE *f;
|
||||
char *p;
|
||||
int iomode;
|
||||
iomode = fopenflags(mode);
|
||||
int oflags;
|
||||
oflags = fopenflags(mode);
|
||||
if ((size && size > 0x7ffff000) || //
|
||||
(!buf && (iomode & O_ACCMODE) != O_RDWR)) {
|
||||
(!buf && (oflags & O_ACCMODE) != O_RDWR)) {
|
||||
einval();
|
||||
return NULL;
|
||||
}
|
||||
if (!(f = __stdio_alloc())) {
|
||||
if (!(f = __stdio_alloc()))
|
||||
return NULL;
|
||||
}
|
||||
if (buf) {
|
||||
f->nofree = true;
|
||||
} else {
|
||||
if (!buf) {
|
||||
if (!size)
|
||||
size = BUFSIZ;
|
||||
// TODO(jart): Why do we need calloc()?
|
||||
if (!_weaken(calloc) || !(buf = _weaken(calloc)(1, size))) {
|
||||
__stdio_free(f);
|
||||
if (!(buf = malloc(size))) {
|
||||
__stdio_unref(f);
|
||||
enomem();
|
||||
return NULL;
|
||||
}
|
||||
f->freebuf = 1;
|
||||
}
|
||||
f->fd = -1;
|
||||
f->buf = buf;
|
||||
if (!(iomode & O_TRUNC)) {
|
||||
if (!(oflags & O_TRUNC))
|
||||
f->end = size;
|
||||
}
|
||||
f->size = size;
|
||||
f->iomode = iomode;
|
||||
if (iomode & O_APPEND) {
|
||||
f->oflags = oflags;
|
||||
if (oflags & O_APPEND) {
|
||||
if ((p = memchr(buf, '\0', size))) {
|
||||
f->beg = p - (char *)buf;
|
||||
} else {
|
||||
|
|
|
@ -17,36 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static const char *fixpathname(const char *pathname, int flags) {
|
||||
if ((flags & O_ACCMODE) == O_RDONLY && strcmp(pathname, "-") == 0) {
|
||||
return "/dev/stdin";
|
||||
} else if ((flags & O_ACCMODE) == O_WRONLY && strcmp(pathname, "-") == 0) {
|
||||
return "/dev/stdout";
|
||||
} else {
|
||||
return pathname;
|
||||
}
|
||||
}
|
||||
|
||||
static int openpathname(const char *pathname, int flags, bool *out_noclose) {
|
||||
if ((flags & O_ACCMODE) == O_RDONLY && strcmp(pathname, "/dev/stdin") == 0) {
|
||||
*out_noclose = true;
|
||||
return fileno(stdin);
|
||||
} else if ((flags & O_ACCMODE) == O_WRONLY &&
|
||||
strcmp(pathname, "/dev/stdout") == 0) {
|
||||
*out_noclose = true;
|
||||
return fileno(stdout);
|
||||
} else {
|
||||
*out_noclose = false;
|
||||
return open(pathname, flags, 0666);
|
||||
}
|
||||
}
|
||||
__static_yoink("fflush");
|
||||
|
||||
/**
|
||||
* Opens file as stream object.
|
||||
|
@ -57,21 +30,13 @@ static int openpathname(const char *pathname, int flags, bool *out_noclose) {
|
|||
* @note microsoft unilaterally deprecated this function lool
|
||||
*/
|
||||
FILE *fopen(const char *pathname, const char *mode) {
|
||||
FILE *f = 0;
|
||||
bool noclose;
|
||||
int fd, flags;
|
||||
if (!pathname) {
|
||||
efault();
|
||||
int fd;
|
||||
if ((fd = open(pathname, fopenflags(mode), 0666)) == -1)
|
||||
return 0;
|
||||
FILE *f;
|
||||
if (!(f = fdopen(fd, mode))) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
flags = fopenflags(mode);
|
||||
pathname = fixpathname(pathname, flags);
|
||||
if ((fd = openpathname(pathname, flags, &noclose)) != -1) {
|
||||
if ((f = fdopen(fd, mode)) != NULL) {
|
||||
f->noclose = noclose;
|
||||
} else if (!noclose) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ size_t fread_unlocked(void *buf, size_t stride, size_t count, FILE *f) {
|
|||
size_t n, m, got, need;
|
||||
|
||||
// check state and parameters
|
||||
if ((f->iomode & O_ACCMODE) == O_WRONLY) {
|
||||
if ((f->oflags & O_ACCMODE) == O_WRONLY) {
|
||||
f->state = errno = EBADF;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,6 @@
|
|||
* Returns nonzero if stream allows reading.
|
||||
*/
|
||||
int __freadable(FILE *f) {
|
||||
return (f->iomode & O_ACCMODE) == O_RDONLY ||
|
||||
(f->iomode & O_ACCMODE) == O_RDWR;
|
||||
return (f->oflags & O_ACCMODE) == O_RDONLY ||
|
||||
(f->oflags & O_ACCMODE) == O_RDWR;
|
||||
}
|
||||
|
|
|
@ -24,5 +24,5 @@
|
|||
* Returns nonzero if stream is read only.
|
||||
*/
|
||||
int __freading(FILE *f) {
|
||||
return (f->iomode & O_ACCMODE) == O_RDONLY;
|
||||
return (f->oflags & O_ACCMODE) == O_RDONLY;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
|
|||
close(fd);
|
||||
if (fd2 != -1) {
|
||||
stream->fd = fd2;
|
||||
stream->iomode = flags;
|
||||
stream->oflags = flags;
|
||||
stream->beg = 0;
|
||||
stream->end = 0;
|
||||
res = stream;
|
||||
|
|
|
@ -34,13 +34,13 @@
|
|||
* @param f is a non-null stream handle
|
||||
* @param offset is the byte delta
|
||||
* @param whence can be SEET_SET, SEEK_CUR, or SEEK_END
|
||||
* @returns 0 on success or -1 on error
|
||||
* @returns 0 on success or -1 w/ errno
|
||||
*/
|
||||
int fseek_unlocked(FILE *f, int64_t offset, int whence) {
|
||||
int res;
|
||||
int64_t pos;
|
||||
if (f->fd != -1) {
|
||||
if (__fflush_impl(f) == -1)
|
||||
if (fflush_unlocked(f) == EOF)
|
||||
return -1;
|
||||
if (whence == SEEK_CUR && f->beg < f->end) {
|
||||
offset -= f->end - f->beg;
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
static inline int64_t ftell_unlocked(FILE *f) {
|
||||
int64_t pos;
|
||||
if (f->fd != -1) {
|
||||
if (__fflush_impl(f) == -1)
|
||||
if (fflush_unlocked(f) == EOF)
|
||||
return -1;
|
||||
if ((pos = lseek(f->fd, 0, SEEK_CUR)) != -1) {
|
||||
if (f->beg < f->end)
|
||||
|
|
|
@ -24,6 +24,6 @@
|
|||
* Returns nonzero if stream allows reading.
|
||||
*/
|
||||
int __fwritable(FILE *f) {
|
||||
return (f->iomode & O_ACCMODE) == O_WRONLY ||
|
||||
(f->iomode & O_ACCMODE) == O_RDWR;
|
||||
return (f->oflags & O_ACCMODE) == O_WRONLY ||
|
||||
(f->oflags & O_ACCMODE) == O_RDWR;
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ size_t fwrite_unlocked(const void *data, size_t stride, size_t count, FILE *f) {
|
|||
struct iovec iov[2];
|
||||
if (!stride || !count)
|
||||
return 0;
|
||||
if ((f->iomode & O_ACCMODE) == O_RDONLY) {
|
||||
if ((f->oflags & O_ACCMODE) == O_RDONLY) {
|
||||
f->state = errno = EBADF;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -24,5 +24,5 @@
|
|||
* Returns nonzero if stream is write only.
|
||||
*/
|
||||
int __fwriting(FILE *f) {
|
||||
return (f->iomode & O_ACCMODE) == O_WRONLY;
|
||||
return (f->oflags & O_ACCMODE) == O_WRONLY;
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 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"
|
||||
|
||||
uint64_t g_rando = 1;
|
|
@ -32,7 +32,7 @@ ssize_t getdelim_unlocked(char **s, size_t *n, int delim, FILE *f) {
|
|||
ssize_t rc;
|
||||
char *p, *s2;
|
||||
size_t i, m, n2;
|
||||
if ((f->iomode & O_ACCMODE) == O_WRONLY) {
|
||||
if ((f->oflags & O_ACCMODE) == O_WRONLY) {
|
||||
f->state = errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -1,39 +1,49 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_STDIO_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_STDIO_INTERNAL_H_
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
#define PUSHBACK 12
|
||||
|
||||
#define FILE_CONTAINER(e) DLL_CONTAINER(struct FILE, elem, e)
|
||||
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct FILE {
|
||||
uint8_t bufmode; /* _IOFBF, etc. (ignored if fd=-1) */
|
||||
char noclose; /* for fake dup() todo delete! */
|
||||
char dynamic; /* did malloc() create this object? */
|
||||
uint32_t iomode; /* O_RDONLY, etc. (ignored if fd=-1) */
|
||||
int32_t state; /* 0=OK, -1=EOF, >0=errno */
|
||||
int fd; /* ≥0=fd, -1=closed|buffer */
|
||||
uint32_t beg;
|
||||
uint32_t end;
|
||||
char *buf;
|
||||
uint32_t size;
|
||||
uint32_t nofree;
|
||||
char bufmode; /* _IOFBF, _IOLBF, or _IONBF */
|
||||
char freethis; /* fclose() should free(this) */
|
||||
char freebuf; /* fclose() should free(this->buf) */
|
||||
char forking; /* used by fork() implementation */
|
||||
int oflags; /* O_RDONLY, etc. */
|
||||
int state; /* 0=OK, -1=EOF, >0=errno */
|
||||
int fd; /* ≥0=fd, -1=closed|buffer */
|
||||
int pid;
|
||||
char *getln;
|
||||
atomic_int refs;
|
||||
unsigned size;
|
||||
unsigned beg;
|
||||
unsigned end;
|
||||
char *buf;
|
||||
pthread_mutex_t lock;
|
||||
struct FILE *next;
|
||||
char mem[BUFSIZ];
|
||||
struct Dll elem;
|
||||
char *getln;
|
||||
};
|
||||
|
||||
extern uint64_t g_rando;
|
||||
struct Stdio {
|
||||
pthread_mutex_t lock; /* Subordinate to FILE::lock */
|
||||
struct Dll *files;
|
||||
};
|
||||
|
||||
int __fflush_impl(FILE *);
|
||||
int __fflush_register(FILE *);
|
||||
void __fflush_unregister(FILE *);
|
||||
extern struct Stdio __stdio;
|
||||
|
||||
void __stdio_lock(void);
|
||||
void __stdio_unlock(void);
|
||||
void __stdio_ref(FILE *);
|
||||
void __stdio_unref(FILE *);
|
||||
void __stdio_unref_unlocked(FILE *);
|
||||
bool __stdio_isok(FILE *);
|
||||
FILE *__stdio_alloc(void);
|
||||
void __stdio_free(FILE *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_STDIO_INTERNAL_H_ */
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 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/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Closes stream created by popen().
|
||||
*
|
||||
* This function may be interrupted or cancelled, however it won't
|
||||
* actually return until the child process has terminated. Thus we
|
||||
* always release the resource, and errors are purely advisory.
|
||||
*
|
||||
* @return termination status of subprocess, or -1 w/ ECHILD
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @raise ECHILD if child pid didn't exist
|
||||
* @raise EINTR if signal was delivered
|
||||
* @cancelationpoint
|
||||
*/
|
||||
int pclose(FILE *f) {
|
||||
int e, rc, ws, pid;
|
||||
bool iscancelled, wasinterrupted;
|
||||
pid = f->pid;
|
||||
fclose(f);
|
||||
if (!pid)
|
||||
return 0;
|
||||
iscancelled = false;
|
||||
wasinterrupted = false;
|
||||
for (e = errno;;) {
|
||||
if (wait4(pid, &ws, 0, 0) != -1) {
|
||||
rc = ws;
|
||||
break;
|
||||
} else if (errno == ECANCELED) {
|
||||
iscancelled = true;
|
||||
errno = e;
|
||||
} else if (errno == EINTR) {
|
||||
wasinterrupted = true;
|
||||
errno = e;
|
||||
} else {
|
||||
rc = echild();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (iscancelled) {
|
||||
return ecanceled();
|
||||
} else if (wasinterrupted) {
|
||||
return eintr();
|
||||
} else {
|
||||
return rc;
|
||||
}
|
||||
}
|
|
@ -17,9 +17,17 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/lcg.internal.h"
|
||||
|
||||
static uint64_t rando;
|
||||
|
||||
/**
|
||||
* Seeds random number generator that's used by rand().
|
||||
*/
|
||||
void srand(unsigned seed) {
|
||||
rando = seed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 31-bit linear congruential pseudorandom number, e.g.
|
||||
*
|
||||
|
@ -39,5 +47,5 @@
|
|||
* @threadunsafe
|
||||
*/
|
||||
int rand(void) {
|
||||
return KnuthLinearCongruentialGenerator(&g_rando) >> 33;
|
||||
return KnuthLinearCongruentialGenerator(&rando) >> 33;
|
||||
}
|
||||
|
|
|
@ -38,15 +38,13 @@ int setvbuf(FILE *f, char *buf, int mode, size_t size) {
|
|||
if (buf) {
|
||||
if (!size)
|
||||
size = BUFSIZ;
|
||||
if (!f->nofree && //
|
||||
f->buf != buf && //
|
||||
f->buf != f->mem && //
|
||||
_weaken(free)) {
|
||||
_weaken(free)(f->buf);
|
||||
}
|
||||
if (f->freebuf)
|
||||
if (f->buf != buf)
|
||||
if (_weaken(free))
|
||||
_weaken(free)(f->buf);
|
||||
f->buf = buf;
|
||||
f->size = size;
|
||||
f->nofree = true;
|
||||
f->freebuf = 0;
|
||||
}
|
||||
f->bufmode = mode;
|
||||
funlockfile(f);
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 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/rand.h"
|
||||
|
||||
extern uint64_t g_rando;
|
||||
|
||||
/**
|
||||
* Seeds random number generator that's used by rand().
|
||||
*/
|
||||
void srand(unsigned seed) {
|
||||
g_rando = seed;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
|
@ -16,18 +16,17 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
static FILE __stderr = {
|
||||
.fd = STDERR_FILENO,
|
||||
.bufmode = _IONBF,
|
||||
.iomode = O_WRONLY,
|
||||
.buf = __stderr.mem,
|
||||
.size = sizeof(stderr->mem),
|
||||
.oflags = O_WRONLY,
|
||||
.lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
|
||||
.elem = {&__stderr.elem, &__stderr.elem},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -35,6 +34,6 @@ static FILE __stderr = {
|
|||
*/
|
||||
FILE *stderr = &__stderr;
|
||||
|
||||
__attribute__((__constructor__(60))) static textstartup void errinit(void) {
|
||||
__fflush_register(stderr);
|
||||
__attribute__((__constructor__(60))) static textstartup void stderr_init(void) {
|
||||
dll_make_last(&__stdio.files, &__stderr.elem);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
|
@ -17,19 +17,25 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
__static_yoink("fflush");
|
||||
|
||||
static char __stdin_buf[BUFSIZ];
|
||||
|
||||
static FILE __stdin = {
|
||||
.fd = STDIN_FILENO,
|
||||
.iomode = O_RDONLY,
|
||||
.oflags = O_RDONLY,
|
||||
.bufmode = _IOFBF,
|
||||
.buf = __stdin.mem,
|
||||
.size = sizeof(stdin->mem),
|
||||
.buf = __stdin_buf,
|
||||
.size = sizeof(__stdin_buf),
|
||||
.lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
|
||||
.elem = {&__stdin.elem, &__stdin.elem},
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -37,9 +43,9 @@ static FILE __stdin = {
|
|||
*/
|
||||
FILE *stdin = &__stdin;
|
||||
|
||||
__attribute__((__constructor__(60))) static textstartup void initin(void) {
|
||||
__attribute__((__constructor__(60))) static textstartup void stdin_init(void) {
|
||||
struct stat st;
|
||||
if (fstat(STDIN_FILENO, &st) || !S_ISREG(st.st_mode))
|
||||
if (fstat(STDIN_FILENO, &st) || S_ISCHR(st.st_mode))
|
||||
stdin->bufmode = _IONBF;
|
||||
__fflush_register(stdin);
|
||||
dll_make_last(&__stdio.files, &__stdin.elem);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
|
@ -16,17 +16,22 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
__static_yoink("fflush");
|
||||
|
||||
static char __stdout_buf[BUFSIZ];
|
||||
|
||||
static FILE __stdout = {
|
||||
.fd = STDOUT_FILENO,
|
||||
.iomode = O_WRONLY,
|
||||
.buf = __stdout.mem,
|
||||
.size = sizeof(stdout->mem),
|
||||
.oflags = O_WRONLY,
|
||||
.buf = __stdout_buf,
|
||||
.size = sizeof(__stdout_buf),
|
||||
.lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
|
||||
.elem = {&__stdout.elem, &__stdout.elem},
|
||||
|
||||
// Unlike other C libraries we don't bother calling fstat() to check
|
||||
// if stdio is a character device and we instead choose to always
|
||||
|
@ -42,6 +47,6 @@ static FILE __stdout = {
|
|||
*/
|
||||
FILE *stdout = &__stdout;
|
||||
|
||||
__attribute__((__constructor__(60))) static textstartup void outinit(void) {
|
||||
__fflush_register(stdout);
|
||||
__attribute__((__constructor__(60))) static textstartup void stdout_init(void) {
|
||||
dll_make_last(&__stdio.files, &__stdout.elem);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue