mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-30 00:08:30 +00:00
Add raw memory visualization tool to redbean
This change introduces a `-W /dev/pts/1` flag to redbean. What it does is use the mincore() system call to create a dual-screen terminal display that lets you troubleshoot the virtual address space. This is useful since page faults are an important thing to consider when using a forking web server. Now we have a colorful visualization of which pages are going to fault and which ones are resident in memory. The memory monitor, if enabled, spawns as a thread that just outputs ANSI codes to the second terminal in a loop. In order to make this happen using the new clone() polyfill, stdio is now thread safe. This change also introduces some new demo pages to redbean. It also polishes the demos we already have, to look a bit nicer and more presentable for the upcoming release, with better explanations too.
This commit is contained in:
parent
578cb21591
commit
80b211e314
106 changed files with 1483 additions and 592 deletions
|
@ -18,6 +18,6 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
void clearerr(FILE *f) {
|
||||
void clearerr_unlocked(FILE *f) {
|
||||
f->state = 0;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#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"
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
/**
|
||||
* Returns true if stream is in end-of-file state.
|
||||
*/
|
||||
int feof(FILE *f) {
|
||||
int feof_unlocked(FILE *f) {
|
||||
return f->state == -1;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,6 @@
|
|||
* @note EOF doesn't count
|
||||
* @see feof()
|
||||
*/
|
||||
errno_t ferror(FILE *f) {
|
||||
errno_t ferror_unlocked(FILE *f) {
|
||||
return f->state > 0 ? f->state : 0;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/bits/pushpop.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"
|
||||
|
@ -35,49 +36,63 @@
|
|||
* @param f is the stream handle
|
||||
* @return is 0 on success or -1 on error
|
||||
*/
|
||||
int fflush(FILE *f) {
|
||||
int fflush_unlocked(FILE *f) {
|
||||
int rc = 0;
|
||||
size_t i;
|
||||
if (!f) {
|
||||
_spinlock(&__fflush.lock);
|
||||
for (i = __fflush.handles.i; i; --i) {
|
||||
if ((f = __fflush.handles.p[i - 1])) {
|
||||
if (fflush(f) == -1) return -1;
|
||||
if (fflush(f) == -1) {
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
_spunlock(&__fflush.lock);
|
||||
} else if (f->fd != -1) {
|
||||
if (__fflush_impl(f) == -1) return -1;
|
||||
if (__fflush_impl(f) == -1) {
|
||||
rc = -1;
|
||||
}
|
||||
} else if (f->beg && f->beg < f->size) {
|
||||
f->buf[f->beg] = 0;
|
||||
}
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
textstartup int __fflush_register(FILE *f) {
|
||||
int rc;
|
||||
size_t i;
|
||||
struct StdioFlush *sf;
|
||||
_spinlock(&__fflush.lock);
|
||||
sf = &__fflush;
|
||||
if (!sf->handles.p) {
|
||||
sf->handles.p = sf->handles_initmem;
|
||||
pushmov(&sf->handles.n, ARRAYLEN(sf->handles_initmem));
|
||||
__cxa_atexit(fflush, 0, 0);
|
||||
__cxa_atexit(fflush_unlocked, 0, 0);
|
||||
}
|
||||
for (i = sf->handles.i; i; --i) {
|
||||
if (!sf->handles.p[i - 1]) {
|
||||
sf->handles.p[i - 1] = f;
|
||||
_spunlock(&__fflush.lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return append(&sf->handles, &f);
|
||||
rc = append(&sf->handles, &f);
|
||||
_spunlock(&__fflush.lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void __fflush_unregister(FILE *f) {
|
||||
size_t i;
|
||||
struct StdioFlush *sf;
|
||||
_spinlock(&__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);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_spunlock(&__fflush.lock);
|
||||
}
|
||||
|
|
|
@ -10,11 +10,12 @@ struct StdioFlushHandles {
|
|||
};
|
||||
|
||||
struct StdioFlush {
|
||||
char lock;
|
||||
struct StdioFlushHandles handles;
|
||||
FILE *handles_initmem[8];
|
||||
};
|
||||
|
||||
extern struct StdioFlush __fflush hidden;
|
||||
hidden extern struct StdioFlush __fflush;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -22,12 +22,12 @@
|
|||
* Reads byte from stream.
|
||||
* @return byte in range 0..255, or -1 w/ errno
|
||||
*/
|
||||
int fgetc(FILE *f) {
|
||||
int fgetc_unlocked(FILE *f) {
|
||||
unsigned char b[1];
|
||||
if (f->beg < f->end) {
|
||||
return f->buf[f->beg++] & 0xff;
|
||||
} else {
|
||||
if (!fread(b, 1, 1, f)) return -1;
|
||||
if (!fread_unlocked(b, 1, 1, f)) return -1;
|
||||
return b[0];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,28 +16,36 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Reads content from stream.
|
||||
* Reads line from stream.
|
||||
*
|
||||
* This function is similar to getline() except it'll truncate lines
|
||||
* exceeding size. The line ending marker is included and may be removed
|
||||
* using _chomp().
|
||||
*
|
||||
* @param s is output buffer
|
||||
* @param size is capacity of s
|
||||
* @param f is non-null file oject stream pointer
|
||||
* @return s on success, NULL on error, or NULL if EOF happens when
|
||||
* zero characters have been read
|
||||
*/
|
||||
char *fgets(char *s, int size, FILE *f) {
|
||||
char *fgets_unlocked(char *s, int size, FILE *f) {
|
||||
int c;
|
||||
char *p;
|
||||
p = s;
|
||||
if (size > 0) {
|
||||
while (--size > 0) {
|
||||
if ((c = getc(f)) == -1) {
|
||||
if (ferror(f) == EINTR) continue;
|
||||
break;
|
||||
if ((c = fgetc_unlocked(f)) == -1) {
|
||||
if (ferror_unlocked(f) == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p++ = c & 0xff;
|
||||
*p++ = c & 255;
|
||||
if (c == '\n') break;
|
||||
}
|
||||
*p = '\0';
|
||||
|
|
|
@ -24,12 +24,12 @@
|
|||
* Reads UTF-8 character from stream.
|
||||
* @return wide character or -1 on EOF or error
|
||||
*/
|
||||
wint_t fgetwc(FILE *f) {
|
||||
wint_t fgetwc_unlocked(FILE *f) {
|
||||
int c, n;
|
||||
wint_t b, x, y;
|
||||
if (f->beg < f->end) {
|
||||
b = f->buf[f->beg++] & 0xff;
|
||||
} else if ((c = fgetc(f)) != -1) {
|
||||
} else if ((c = fgetc_unlocked(f)) != -1) {
|
||||
b = c;
|
||||
} else {
|
||||
return -1;
|
||||
|
@ -38,12 +38,12 @@ wint_t fgetwc(FILE *f) {
|
|||
n = ThomPikeLen(b);
|
||||
x = ThomPikeByte(b);
|
||||
while (--n) {
|
||||
if ((c = fgetc(f)) == -1) return -1;
|
||||
if ((c = fgetc_unlocked(f)) == -1) return -1;
|
||||
y = c;
|
||||
if (ThomPikeCont(y)) {
|
||||
x = ThomPikeMerge(x, y);
|
||||
} else {
|
||||
ungetc(y, f);
|
||||
ungetc_unlocked(y, f);
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,13 +23,13 @@
|
|||
/**
|
||||
* Reads UTF-8 content from stream into UTF-32 buffer.
|
||||
*/
|
||||
wchar_t *fgetws(wchar_t *s, int size, FILE *f) {
|
||||
wchar_t *fgetws_unlocked(wchar_t *s, int size, FILE *f) {
|
||||
wint_t c;
|
||||
wchar_t *p = s;
|
||||
if (size > 0) {
|
||||
while (--size > 0) {
|
||||
if ((c = fgetwc(f)) == -1) {
|
||||
if (ferror(f) == EINTR) continue;
|
||||
if ((c = fgetwc_unlocked(f)) == -1) {
|
||||
if (ferror_unlocked(f) == EINTR) continue;
|
||||
break;
|
||||
}
|
||||
*p++ = c;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
/**
|
||||
* Returns file descriptor associated with stream.
|
||||
*/
|
||||
int fileno(FILE *f) {
|
||||
int fileno_unlocked(FILE *f) {
|
||||
if (f->fd != -1) {
|
||||
return f->fd;
|
||||
} else {
|
||||
|
|
|
@ -16,23 +16,12 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Does nothing since Cosmopolitan currently doesn't support threads.
|
||||
* Acquires lock on stdio object, blocking if needed.
|
||||
*/
|
||||
void flockfile(FILE *f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing since Cosmopolitan currently doesn't support threads.
|
||||
*/
|
||||
void funlockfile(FILE *f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing since Cosmopolitan currently doesn't support threads.
|
||||
*/
|
||||
int ftrylockfile(FILE *f) {
|
||||
return 0;
|
||||
_spinlock(&f->lock);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/stdio/fflush.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/stdio_ext.h"
|
||||
|
||||
/**
|
||||
|
@ -25,9 +27,16 @@
|
|||
*/
|
||||
void _flushlbf(void) {
|
||||
int i;
|
||||
FILE *f;
|
||||
_spinlock(&__fflush.lock);
|
||||
for (i = 0; i < __fflush.handles.i; ++i) {
|
||||
if (__fflush.handles.p[i]->bufmode == _IOLBF) {
|
||||
fflush(__fflush.handles.p[i]);
|
||||
if ((f = __fflush.handles.p[i])) {
|
||||
_spinlock(&f->lock);
|
||||
if (f->bufmode == _IOLBF) {
|
||||
fflush_unlocked(f);
|
||||
}
|
||||
_spunlock(&f->lock);
|
||||
}
|
||||
}
|
||||
_spunlock(&__fflush.lock);
|
||||
}
|
||||
|
|
|
@ -25,14 +25,14 @@
|
|||
* @param c is byte to buffer or write, which is masked
|
||||
* @return c as unsigned char if written or -1 w/ errno
|
||||
*/
|
||||
int fputc(int c, FILE *f) {
|
||||
int fputc_unlocked(int c, FILE *f) {
|
||||
unsigned char b;
|
||||
if (c != '\n' && f->beg < f->size && f->bufmode != _IONBF) {
|
||||
f->buf[f->beg++] = c;
|
||||
return c & 0xff;
|
||||
} else {
|
||||
b = c;
|
||||
if (!fwrite(&b, 1, 1, f)) return -1;
|
||||
if (!fwrite_unlocked(&b, 1, 1, f)) return -1;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,10 +30,10 @@
|
|||
* @param f is an open stream
|
||||
* @return bytes written, or -1 w/ errno
|
||||
*/
|
||||
int fputs(const char *s, FILE *f) {
|
||||
int fputs_unlocked(const char *s, FILE *f) {
|
||||
size_t n, r;
|
||||
n = strlen(s);
|
||||
r = fwrite(s, 1, n, f);
|
||||
r = fwrite_unlocked(s, 1, n, f);
|
||||
if (!r && n) return -1;
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -24,12 +24,12 @@
|
|||
*
|
||||
* @return wc if written or -1 w/ errno
|
||||
*/
|
||||
wint_t fputwc(wchar_t wc, FILE *f) {
|
||||
wint_t fputwc_unlocked(wchar_t wc, FILE *f) {
|
||||
uint64_t w;
|
||||
if (wc != -1) {
|
||||
w = tpenc(wc);
|
||||
do {
|
||||
if (fputc(w, f) == -1) {
|
||||
if (fputc_unlocked(w, f) == -1) {
|
||||
return -1;
|
||||
}
|
||||
} while ((w >>= 8));
|
||||
|
|
|
@ -30,12 +30,16 @@
|
|||
* @param f is an open stream
|
||||
* @return strlen(s) or -1 w/ errno on error
|
||||
*/
|
||||
int fputws(const wchar_t *s, FILE *f) {
|
||||
int fputws_unlocked(const wchar_t *s, FILE *f) {
|
||||
int res = 0;
|
||||
while (*s) {
|
||||
if (fputwc(*s++, f) == -1) {
|
||||
if (ferror(f) == EINTR) continue;
|
||||
if (feof(f)) errno = f->state = EPIPE;
|
||||
if (fputwc_unlocked(*s++, f) == -1) {
|
||||
if (ferror_unlocked(f) == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (feof_unlocked(f)) {
|
||||
errno = f->state = EPIPE;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
++res;
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
* @param count is the number of strides to fetch
|
||||
* @return count on success, [0,count) on eof, or 0 on error or count==0
|
||||
*/
|
||||
size_t fread(void *buf, size_t stride, size_t count, FILE *f) {
|
||||
size_t fread_unlocked(void *buf, size_t stride, size_t count, FILE *f) {
|
||||
char *p;
|
||||
ssize_t rc;
|
||||
size_t n, m;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ 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"
|
||||
|
@ -37,9 +38,11 @@
|
|||
*/
|
||||
FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
|
||||
int fd;
|
||||
FILE *res;
|
||||
unsigned flags;
|
||||
flags = fopenflags(mode);
|
||||
fflush(stream);
|
||||
_spinlock(&stream->lock);
|
||||
fflush_unlocked(stream);
|
||||
if (pathname) {
|
||||
/* open new stream, overwriting existing alloc */
|
||||
if ((fd = open(pathname, flags, 0666)) != -1) {
|
||||
|
@ -48,13 +51,15 @@ FILE *freopen(const char *pathname, const char *mode, FILE *stream) {
|
|||
stream->iomode = flags;
|
||||
stream->beg = 0;
|
||||
stream->end = 0;
|
||||
return stream;
|
||||
res = stream;
|
||||
} else {
|
||||
return NULL;
|
||||
res = NULL;
|
||||
}
|
||||
} else {
|
||||
fcntl(stream->fd, F_SETFD, !!(flags & O_CLOEXEC));
|
||||
fcntl(stream->fd, F_SETFL, flags & ~O_CLOEXEC);
|
||||
return stream;
|
||||
res = stream;
|
||||
}
|
||||
_spunlock(&stream->lock);
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#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,8 +37,10 @@
|
|||
* @returns 0 on success or -1 on error
|
||||
*/
|
||||
int fseeko(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) {
|
||||
|
@ -46,10 +49,10 @@ int fseeko(FILE *f, int64_t offset, int whence) {
|
|||
if (lseek(f->fd, offset, whence) != -1) {
|
||||
f->beg = 0;
|
||||
f->end = 0;
|
||||
return 0;
|
||||
res = 0;
|
||||
} else {
|
||||
f->state = errno == ESPIPE ? EBADF : errno;
|
||||
return -1;
|
||||
res = -1;
|
||||
}
|
||||
} else {
|
||||
switch (whence) {
|
||||
|
@ -68,10 +71,12 @@ int fseeko(FILE *f, int64_t offset, int whence) {
|
|||
}
|
||||
if (0 <= pos && pos <= f->end) {
|
||||
f->beg = pos;
|
||||
return 0;
|
||||
res = 0;
|
||||
} else {
|
||||
f->state = errno = EINVAL;
|
||||
return -1;
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
_spunlock(&f->lock);
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
#include "libc/stdio/stdio_ext.h"
|
||||
|
||||
/**
|
||||
* Does nothing and returns `FSETLOCKING_BYCALLER`.
|
||||
* Does nothing and returns `FSETLOCKING_INTERNAL`.
|
||||
*/
|
||||
int __fsetlocking(FILE *f, int type) {
|
||||
return FSETLOCKING_BYCALLER;
|
||||
return FSETLOCKING_INTERNAL;
|
||||
}
|
||||
|
|
|
@ -18,18 +18,13 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#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"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Returns current position of stream.
|
||||
*
|
||||
* @param stream is a non-null stream handle
|
||||
* @returns current byte offset from beginning, or -1 w/ errno
|
||||
*/
|
||||
int64_t ftello(FILE *f) {
|
||||
static int64_t ftello_unlocked(FILE *f) {
|
||||
int64_t pos;
|
||||
uint32_t skew;
|
||||
if (f->fd != -1) {
|
||||
|
@ -45,3 +40,17 @@ int64_t ftello(FILE *f) {
|
|||
return f->beg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current position of stream.
|
||||
*
|
||||
* @param stream is a non-null stream handle
|
||||
* @returns current byte offset from beginning, or -1 w/ errno
|
||||
*/
|
||||
int64_t ftello(FILE *f) {
|
||||
int64_t rc;
|
||||
_spinlock(&f->lock);
|
||||
rc = ftello_unlocked(f);
|
||||
_spunlock(&f->lock);
|
||||
return rc;
|
||||
}
|
||||
|
|
28
libc/stdio/ftrylockfile.c
Normal file
28
libc/stdio/ftrylockfile.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*-*- 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/intrin/spinlock.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
|
||||
*/
|
||||
int ftrylockfile(FILE *f) {
|
||||
return _trylock(&f->lock);
|
||||
}
|
27
libc/stdio/funlockfile.c
Normal file
27
libc/stdio/funlockfile.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*-*- 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/intrin/spinlock.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Releases lock on stdio object.
|
||||
*/
|
||||
void funlockfile(FILE *f) {
|
||||
_spunlock(&f->lock);
|
||||
}
|
|
@ -36,7 +36,7 @@
|
|||
* @param count is the number of strides to write
|
||||
* @return count on success, [0,count) on EOF, 0 on error or count==0
|
||||
*/
|
||||
size_t fwrite(const void *data, size_t stride, size_t count, FILE *f) {
|
||||
size_t fwrite_unlocked(const void *data, size_t stride, size_t count, FILE *f) {
|
||||
ldiv_t d;
|
||||
ssize_t rc;
|
||||
size_t n, m;
|
||||
|
|
|
@ -22,6 +22,6 @@
|
|||
* Reads byte from stream.
|
||||
* @return byte in range 0..255, or -1 w/ errno
|
||||
*/
|
||||
int(getc)(FILE *f) {
|
||||
return fgetc(f);
|
||||
int(getc_unlocked)(FILE *f) {
|
||||
return fgetc_unlocked(f);
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
#include "libc/stdio/stdio.h"
|
||||
|
||||
/**
|
||||
* Reads byte from stream.
|
||||
* Reads byte from stdin.
|
||||
* @return byte in range 0..255, or -1 w/ errno
|
||||
*/
|
||||
int getchar(void) {
|
||||
return fgetc(stdin);
|
||||
int getchar_unlocked(void) {
|
||||
return fgetc_unlocked(stdin);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#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"
|
||||
|
@ -26,19 +27,7 @@
|
|||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
/**
|
||||
* Reads string from stream.
|
||||
*
|
||||
* @param s is the caller's buffer (in/out) which is extended or
|
||||
* allocated automatically, also NUL-terminated is guaranteed
|
||||
* @param n is the capacity of s (in/out)
|
||||
* @param delim is the stop char (and NUL is implicitly too)
|
||||
* @return number of bytes read >0, including delim, excluding NUL,
|
||||
* or -1 w/ errno on EOF or error; see ferror() and feof()
|
||||
* @note this function can't punt EINTR to caller
|
||||
* @see getline(), _chomp(), gettok_r()
|
||||
*/
|
||||
ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
|
||||
static ssize_t getdelim_unlocked(char **s, size_t *n, int delim, FILE *f) {
|
||||
char *p;
|
||||
ssize_t rc;
|
||||
size_t i, m;
|
||||
|
@ -83,3 +72,23 @@ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads string from stream.
|
||||
*
|
||||
* @param s is the caller's buffer (in/out) which is extended or
|
||||
* allocated automatically, also NUL-terminated is guaranteed
|
||||
* @param n is the capacity of s (in/out)
|
||||
* @param delim is the stop char (and NUL is implicitly too)
|
||||
* @return number of bytes read >0, including delim, excluding NUL,
|
||||
* or -1 w/ errno on EOF or error; see ferror() and feof()
|
||||
* @note this function can't punt EINTR to caller
|
||||
* @see getline(), _chomp(), gettok_r()
|
||||
*/
|
||||
ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
|
||||
ssize_t rc;
|
||||
_spinlock(&f->lock);
|
||||
rc = getdelim_unlocked(s, n, delim, f);
|
||||
_spunlock(&f->lock);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,6 @@
|
|||
* Reads UTF-8 character from stream.
|
||||
* @return wide character or -1 on EOF or error
|
||||
*/
|
||||
wint_t(getwc)(FILE *f) {
|
||||
return fgetwc(f);
|
||||
wint_t(getwc_unlocked)(FILE *f) {
|
||||
return fgetwc_unlocked(f);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,6 @@
|
|||
* Reads UTF-8 character from stream.
|
||||
* @return wide character or -1 on EOF or error
|
||||
*/
|
||||
wint_t getwchar(void) {
|
||||
return fgetwc(stdin);
|
||||
wint_t getwchar_unlocked(void) {
|
||||
return fgetwc_unlocked(stdin);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,6 @@
|
|||
* @param c is byte to buffer or write, which is masked
|
||||
* @return c as unsigned char if written or -1 w/ errno
|
||||
*/
|
||||
int(putc)(int c, FILE *f) {
|
||||
return fputc(c, f);
|
||||
int(putc_unlocked)(int c, FILE *f) {
|
||||
return fputc_unlocked(c, f);
|
||||
}
|
||||
|
|
|
@ -23,4 +23,6 @@
|
|||
*
|
||||
* @return c (as unsigned char) if written or -1 w/ errno
|
||||
*/
|
||||
int putchar(int c) { return fputc(c, stdout); }
|
||||
int putchar_unlocked(int c) {
|
||||
return fputc_unlocked(c, stdout);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,6 @@
|
|||
*
|
||||
* @return wc if written or -1 w/ errno
|
||||
*/
|
||||
wint_t(putwc)(wchar_t wc, FILE *f) {
|
||||
return fputwc(wc, f);
|
||||
wint_t(putwc_unlocked)(wchar_t wc, FILE *f) {
|
||||
return fputwc_unlocked(wc, f);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,6 @@
|
|||
* Writes wide character to stdout.
|
||||
* @return wc if written or -1 w/ errno
|
||||
*/
|
||||
wint_t putwchar(wchar_t wc) {
|
||||
return fputwc(wc, stdout);
|
||||
wint_t putwchar_unlocked(wchar_t wc) {
|
||||
return fputwc_unlocked(wc, stdout);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ typedef struct FILE {
|
|||
uint32_t size; /* 0x20 */
|
||||
uint32_t nofree; /* 0x24 */
|
||||
int pid; /* 0x28 */
|
||||
char *getln;
|
||||
char lock; /* 0x2c */
|
||||
char *getln; /* 0x30 */
|
||||
} FILE;
|
||||
|
||||
extern FILE *stdin;
|
||||
|
@ -135,11 +136,48 @@ int fwide(FILE *, int);
|
|||
#define vfscanf(F, FMT, VA) (vfscanf)(F, SFLINK(FMT), VA)
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
||||
#define stdin SYMBOLIC(stdin)
|
||||
#define stdout SYMBOLIC(stdout)
|
||||
#define stderr SYMBOLIC(stderr)
|
||||
|
||||
/*───────────────────────────────────────────────────────────────────────────│─╗
|
||||
│ cosmopolitan § standard i/o » without mutexes ─╬─│┼
|
||||
╚────────────────────────────────────────────────────────────────────────────│*/
|
||||
|
||||
void flockfile(FILE *);
|
||||
void funlockfile(FILE *);
|
||||
int ftrylockfile(FILE *);
|
||||
int getc_unlocked(FILE *) paramsnonnull();
|
||||
int getchar_unlocked(void);
|
||||
int putc_unlocked(int, FILE *) paramsnonnull();
|
||||
int putchar_unlocked(int);
|
||||
void clearerr_unlocked(FILE *);
|
||||
int feof_unlocked(FILE *);
|
||||
int ferror_unlocked(FILE *);
|
||||
int fileno_unlocked(FILE *);
|
||||
int fflush_unlocked(FILE *);
|
||||
int fgetc_unlocked(FILE *);
|
||||
int fputc_unlocked(int, FILE *);
|
||||
size_t fread_unlocked(void *, size_t, size_t, FILE *);
|
||||
size_t fwrite_unlocked(const void *, size_t, size_t, FILE *);
|
||||
char *fgets_unlocked(char *, int, FILE *);
|
||||
int fputs_unlocked(const char *, FILE *);
|
||||
wint_t getwc_unlocked(FILE *);
|
||||
wint_t getwchar_unlocked(void);
|
||||
wint_t fgetwc_unlocked(FILE *);
|
||||
wint_t fputwc_unlocked(wchar_t, FILE *);
|
||||
wint_t putwc_unlocked(wchar_t, FILE *);
|
||||
wint_t putwchar_unlocked(wchar_t);
|
||||
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();
|
||||
|
||||
#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_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_STDIO_STDIO_H_ */
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
/**
|
||||
* Pushes byte back to stream.
|
||||
*/
|
||||
int ungetc(int c, FILE *f) {
|
||||
int ungetc_unlocked(int c, FILE *f) {
|
||||
if (c == -1) return -1;
|
||||
if (f->beg) {
|
||||
f->buf[--f->beg] = c;
|
||||
|
@ -32,5 +32,5 @@ int ungetc(int c, FILE *f) {
|
|||
} else {
|
||||
return -1;
|
||||
}
|
||||
return c & 0xff;
|
||||
return c & 255;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
/**
|
||||
* Pushes wide character back to stream.
|
||||
*/
|
||||
wint_t ungetwc(wint_t c, FILE *f) {
|
||||
wint_t ungetwc_unlocked(wint_t c, FILE *f) {
|
||||
char b[6];
|
||||
unsigned n;
|
||||
uint64_t w;
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_STDIO_UNLOCKED_H_
|
||||
#define COSMOPOLITAN_LIBC_STDIO_UNLOCKED_H_
|
||||
#include "libc/stdio/stdio.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void flockfile(FILE *);
|
||||
void funlockfile(FILE *);
|
||||
int ftrylockfile(FILE *);
|
||||
int getc_unlocked(FILE *) paramsnonnull();
|
||||
int getchar_unlocked(void);
|
||||
int putc_unlocked(int, FILE *) paramsnonnull();
|
||||
int putchar_unlocked(int);
|
||||
void clearerr_unlocked(FILE *);
|
||||
int feof_unlocked(FILE *);
|
||||
int ferror_unlocked(FILE *);
|
||||
int fileno_unlocked(FILE *);
|
||||
int fflush_unlocked(FILE *);
|
||||
int fgetc_unlocked(FILE *);
|
||||
int fputc_unlocked(int, FILE *);
|
||||
size_t fread_unlocked(void *, size_t, size_t, FILE *);
|
||||
size_t fwrite_unlocked(const void *, size_t, size_t, FILE *);
|
||||
char *fgets_unlocked(char *, int, FILE *);
|
||||
int fputs_unlocked(const char *, FILE *);
|
||||
wint_t getwc_unlocked(FILE *);
|
||||
wint_t getwchar_unlocked(void);
|
||||
wint_t fgetwc_unlocked(FILE *);
|
||||
wint_t fputwc_unlocked(wchar_t, FILE *);
|
||||
wint_t putwc_unlocked(wchar_t, FILE *);
|
||||
wint_t putwchar_unlocked(wchar_t);
|
||||
wchar_t *fgetws_unlocked(wchar_t *, int, FILE *);
|
||||
int fputws_unlocked(const wchar_t *, FILE *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_STDIO_UNLOCKED_H_ */
|
|
@ -18,11 +18,12 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
clearerr_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call clearerr
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn clearerr_unlocked,globl
|
||||
// Clears error state on stream.
|
||||
//
|
||||
// @param rdi has stream pointer
|
||||
// @see clearerr_unlocked()
|
||||
clearerr:
|
||||
mov %rdi,%r11
|
||||
ezlea clearerr_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn clearerr,globl
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
feof_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call feof
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn feof_unlocked,globl
|
||||
// Returns true if stream is in end-of-file state.
|
||||
//
|
||||
// @param rdi has file stream object pointer
|
||||
// @note EOF doesn't count
|
||||
// @see feof_unlocked()
|
||||
feof: mov %rdi,%r11
|
||||
ezlea feof_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn feof,globl
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
ferror_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call ferror
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn ferror_unlocked,globl
|
||||
// Returns nonzero if stream is in error state.
|
||||
//
|
||||
// @param rdi has file stream object pointer
|
||||
// @note EOF doesn't count
|
||||
// @see ferror_unlocked()
|
||||
ferror: mov %rdi,%r11
|
||||
ezlea ferror_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn ferror,globl
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fflush_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fflush
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fflush_unlocked,globl
|
||||
// Blocks until data from stream buffer is written out.
|
||||
//
|
||||
// @param rdi is the stream handle
|
||||
// @return 0 on success or -1 w/ errno
|
||||
// @see fflush_unlocked()
|
||||
fflush: mov %rdi,%r11
|
||||
ezlea fflush_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fflush,globl
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fgetc_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fgetc
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fgetc_unlocked,globl
|
||||
// Reads byte from stream.
|
||||
//
|
||||
// @param rdi has stream object pointer
|
||||
// @return byte in range 0..255, or -1 w/ errno
|
||||
// @see fgetc_unlocked()
|
||||
fgetc: mov %rdi,%r11
|
||||
ezlea fgetc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fgetc,globl
|
||||
|
|
|
@ -18,11 +18,19 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fgets_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fgets
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fgets_unlocked,globl
|
||||
// Reads line from stream.
|
||||
//
|
||||
// This function is similar to getline() except it'll truncate
|
||||
// lines exceeding size. The line ending marker is included
|
||||
// and may be removed using _chomp().
|
||||
//
|
||||
// @param rdi is output buffer
|
||||
// @param rsi is size of rdi buffer
|
||||
// @param rdx is file stream object pointer
|
||||
// @return rax has rdi on success, NULL on error or
|
||||
// NULL if EOF happens with zero chars read
|
||||
// @see fgets_unlocked()
|
||||
fgets: mov %rdx,%r11
|
||||
ezlea fgets_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fgets,globl
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fgetwc_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fgetwc
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fgetwc_unlocked,globl
|
||||
// Reads UTF-8 wide character from stream.
|
||||
//
|
||||
// @param rdi has stream object pointer
|
||||
// @return wide character or -1 on EOF or error
|
||||
// @see fgetwc_unlocked()
|
||||
fgetwc: mov %rdi,%r11
|
||||
ezlea fgetwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fgetwc,globl
|
||||
|
|
|
@ -18,11 +18,17 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fgetws_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fgetws
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fgetws_unlocked,globl
|
||||
// Reads UTF-8 content from stream into UTF-32 buffer.
|
||||
//
|
||||
// This function is similar to getline() except it'll truncate lines
|
||||
// exceeding size. The line ending marker is included and may be removed
|
||||
// using _chomp().
|
||||
//
|
||||
// @param rdi is nul-terminated string that's non-null
|
||||
// @param rsi is size of rdi buffer
|
||||
// @param rsi is file stream object pointer
|
||||
// @see fgetws_unlocked()
|
||||
fgetws: mov %rdx,%r11
|
||||
ezlea fgetws_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fgetws,globl
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fileno_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fileno
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fileno_unlocked,globl
|
||||
// Returns file descriptor associated with stream.
|
||||
//
|
||||
// @param rdi has file stream object pointer
|
||||
// @see fileno_unlocked()
|
||||
fileno: mov %rdi,%r11
|
||||
ezlea fileno_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fileno,globl
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fputc_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fputc
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fputc_unlocked,globl
|
||||
// Writes character to stream.
|
||||
//
|
||||
// @param rdi c is byte to buffer or write, which is masked
|
||||
// @param rsi has stream object pointer
|
||||
// @return c as unsigned char if written or -1 w/ errno
|
||||
// @see fputc_unlocked()
|
||||
fputc: mov %rsi,%r11
|
||||
ezlea fputc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fputc,globl
|
||||
|
|
|
@ -18,11 +18,17 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fputs_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fputs
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fputs_unlocked,globl
|
||||
// Writes string to stream.
|
||||
//
|
||||
// Writing stops at the NUL-terminator, which isn't included in output.
|
||||
// This function blocks until the full string is written, unless an
|
||||
// unrecoverable error happens.
|
||||
//
|
||||
// @param rdi is nul-terminated string that's non-null
|
||||
// @param rsi is file object stream pointer
|
||||
// @return strlen(rdi) on success or -1 w/ errno
|
||||
// @see fputs_unlocked()
|
||||
fputs: mov %rsi,%r11
|
||||
ezlea fputs_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fputs,globl
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fputwc_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fputwc
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fputwc_unlocked,globl
|
||||
// Writes wide character to stream.
|
||||
//
|
||||
// @param rdi has wide character
|
||||
// @param rsi has file object stream pointer
|
||||
// @return rax is wide character if written or -1 w/ errno
|
||||
// @see fputwc_unlocked()
|
||||
fputwc: mov %rsi,%r11
|
||||
ezlea fputwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fputwc,globl
|
||||
|
|
|
@ -18,11 +18,17 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fputws_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fputws
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fputws_unlocked,globl
|
||||
// Writes wide character string to stream.
|
||||
//
|
||||
// Writing stops at the NUL-terminator, which isn't included in output.
|
||||
// This function blocks until the full string is written, unless an
|
||||
// unrecoverable error happens.
|
||||
//
|
||||
// @param rdi is nul-terminated string that's non-null
|
||||
// @param rsi is file object stream pointer
|
||||
// @return strlen(rdi) on success or -1 w/ errno
|
||||
// @see fputws_unlocked()
|
||||
fputws: mov %rsi,%r11
|
||||
ezlea fputws_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fputws,globl
|
||||
|
|
|
@ -18,11 +18,15 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fread_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fread
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fread_unlocked,globl
|
||||
// Reads data from stream.
|
||||
//
|
||||
// @param rdi has pointer to data to read
|
||||
// @param rsi stride specifies the size of individual items
|
||||
// @param rdx count is the number of strides to read
|
||||
// @param rcx has file object stream pointer
|
||||
// @return count on success, [0,count) on EOF, 0 on error or count==0
|
||||
// @see fread_unlocked()
|
||||
fread: mov %rcx,%r11
|
||||
ezlea fread_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fread,globl
|
||||
|
|
|
@ -18,11 +18,15 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
fwrite_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fwrite
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn fwrite_unlocked,globl
|
||||
// Writes data to stream.
|
||||
//
|
||||
// @param rdi has pointer to data to write
|
||||
// @param rsi stride specifies the size of individual items
|
||||
// @param rdx count is the number of strides to write
|
||||
// @param rcx has file object stream pointer
|
||||
// @return count on success, [0,count) on EOF, 0 on error or count==0
|
||||
// @see fwrite_unlocked()
|
||||
fwrite: mov %rcx,%r11
|
||||
ezlea fwrite_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn fwrite,globl
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
getc_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call getc
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn getc_unlocked,globl
|
||||
// Reads character from stream.
|
||||
//
|
||||
// @param rdi has file stream object pointer
|
||||
// @return byte in range 0..255, or -1 w/ errno
|
||||
// @see fgetc_unlocked()
|
||||
getc: mov %rdi,%r11
|
||||
ezlea fgetwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn getc,globl
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
getchar_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call getchar
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn getchar_unlocked,globl
|
||||
// Reads character from stdin.
|
||||
//
|
||||
// @return byte in range 0..255, or -1 w/ errno
|
||||
// @see fgetc_unlocked()
|
||||
getchar:
|
||||
lea stdin(%rip),%rdi
|
||||
mov %rdi,%r11
|
||||
ezlea fgetc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn getchar,globl
|
||||
|
|
|
@ -18,11 +18,12 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
getwc_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fgetwc_unlocked
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn getwc_unlocked,globl
|
||||
// Reads UTF-8 character from stream.
|
||||
//
|
||||
// @param rdi has file stream object pointer
|
||||
// @return wide character or -1 on EOF or error
|
||||
// @see fgetwc_unlocked()
|
||||
getwc: mov %rdi,%r11
|
||||
ezlea fgetwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn getwc,globl
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
getwchar_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call getwchar
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn getwchar_unlocked,globl
|
||||
// Reads UTF-8 character from stdin.
|
||||
//
|
||||
// @return wide character or -1 on EOF or error
|
||||
// @see fgetwc_unlocked()
|
||||
getwchar:
|
||||
lea stdin(%rip),%rdi
|
||||
mov %rdi,%r11
|
||||
ezlea fgetwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn getwchar,globl
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
putc_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call putc
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn putc_unlocked,globl
|
||||
// Writes character to stream.
|
||||
//
|
||||
// @param rdi c is byte to buffer or write, which is masked
|
||||
// @param rsi has stream object pointer
|
||||
// @return c as unsigned char if written or -1 w/ errno
|
||||
// @see fputc_unlocked()
|
||||
putc: mov %rsi,%r11
|
||||
ezlea fputc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn putc,globl
|
||||
|
|
|
@ -18,11 +18,14 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
putchar_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call putchar
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn putchar_unlocked,globl
|
||||
// Writes character to stdout.
|
||||
//
|
||||
// @param rdi has character
|
||||
// @return c (as unsigned char) if written or -1 w/ errno
|
||||
// @see fputc_unlocked()
|
||||
putchar:
|
||||
lea stdout(%rip),%rsi
|
||||
mov %rsi,%r11
|
||||
ezlea fputc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn putchar,globl
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
putwc_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call fputwc_unlocked
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn putwc_unlocked,globl
|
||||
// Writes wide character to stream.
|
||||
//
|
||||
// @param rdi has wide character
|
||||
// @param rsi has file object
|
||||
// @return wc if written or -1 w/ errno
|
||||
// @see putwc_unlocked()
|
||||
putwc: mov %rsi,%r11
|
||||
ezlea fputwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn putwc,globl
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
|
@ -19,11 +18,14 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
putwchar_unlocked:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
.profilable # note: no consensus for threads exists in abis
|
||||
call putwchar
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn putwchar_unlocked,globl
|
||||
// Writes wide character to stdout.
|
||||
//
|
||||
// @param rdi has wide character
|
||||
// @return wc if written or -1 w/ errno
|
||||
// @see fputwc_unlocked()
|
||||
putwchar:
|
||||
lea stdout(%rip),%rsi
|
||||
mov %rsi,%r11
|
||||
ezlea fputwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn putwchar,globl
|
||||
|
|
70
libc/stdio/unlocked/stdio_unlock.S
Normal file
70
libc/stdio/unlocked/stdio_unlock.S
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*-*- 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 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/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 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
|
||||
stdio_unlock:
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
|
||||
// acquires mutex
|
||||
push %rcx
|
||||
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
|
||||
pop %rcx
|
||||
|
||||
// calls delegate
|
||||
push %rsi
|
||||
push %r11
|
||||
call *%rax
|
||||
pop %r11
|
||||
pop %rsi
|
||||
|
||||
// releases mutex
|
||||
movb $0,LOCK(%r11)
|
||||
|
||||
pop %rbp
|
||||
ret
|
||||
.endfn stdio_unlock,globl
|
30
libc/stdio/unlocked/ungetc_unlocked.S
Normal file
30
libc/stdio/unlocked/ungetc_unlocked.S
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*-*- 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 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/macros.internal.h"
|
||||
|
||||
// Pushes byte back to stream.
|
||||
//
|
||||
// @param rdi has character to push
|
||||
// @param rds has stream object pointer
|
||||
// @return rax has rdi on success or -1 w/ errno
|
||||
// @see ungetc_unlocked()
|
||||
ungetc: mov %rsi,%r11
|
||||
ezlea ungetc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn ungetc,globl
|
31
libc/stdio/unlocked/ungetwc_unlocked.S
Normal file
31
libc/stdio/unlocked/ungetwc_unlocked.S
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*-*- 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 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/macros.internal.h"
|
||||
|
||||
// Pushes byte back to stream.
|
||||
//
|
||||
// @param rdi has character to push
|
||||
// @param rds has stream object pointer
|
||||
// @return rax has rdi on success or -1 w/ errno
|
||||
// @see ungetwc_unlocked()
|
||||
ungetwc:
|
||||
mov %rsi,%r11
|
||||
ezlea ungetwc_unlocked,ax
|
||||
jmp stdio_unlock
|
||||
.endfn ungetwc,globl
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#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"
|
||||
|
@ -28,14 +29,23 @@ struct state {
|
|||
};
|
||||
|
||||
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;
|
||||
} else if (!fwrite(s, 1, n, t->f)) {
|
||||
return -1;
|
||||
t->n += n;
|
||||
rc = 0;
|
||||
} else if (!fwrite_unlocked(s, 1, n, t->f)) {
|
||||
rc = -1;
|
||||
} else {
|
||||
t->n += n;
|
||||
rc = 0;
|
||||
}
|
||||
t->n += n;
|
||||
_spunlock(&t->f->lock);
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue