Fix some issues and do some code cleanup

This commit is contained in:
Justine Tunney 2022-05-23 10:15:53 -07:00
parent 1f229e4efc
commit 312ed5c67c
72 changed files with 880 additions and 982 deletions

View file

@ -39,6 +39,7 @@ int fclose(FILE *f) {
if (!f) return 0;
__fflush_unregister(f);
fflush(f);
free_s(&f->getln);
if (!f->nofree) {
free_s(&f->buf);
}

View file

@ -1,7 +1,7 @@
/*-*- 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
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
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
@ -16,83 +16,19 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/alg/arraylist.internal.h"
#include "libc/bits/bits.h"
#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"
#include "libc/stdio/fflush.internal.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.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
* @threadsafe
*/
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) {
rc = -1;
}
}
}
_spunlock(&__fflush.lock);
} else if (f->fd != -1) {
if (__fflush_impl(f) == -1) {
rc = -1;
}
} else if (f->beg && f->beg < f->size) {
f->buf[f->beg] = 0;
}
return rc;
}
textstartup int __fflush_register(FILE *f) {
int fflush(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_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;
}
}
rc = append(&sf->handles, &f);
_spunlock(&__fflush.lock);
if (f) flockfile(f);
rc = fflush_unlocked(f);
if (f) funlockfile(f);
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);
break;
}
}
_spunlock(&__fflush.lock);
}

View file

@ -0,0 +1,98 @@
/*-*- 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/alg/arraylist.internal.h"
#include "libc/bits/bits.h"
#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"
#include "libc/stdio/fflush.internal.h"
#include "libc/stdio/internal.h"
#include "libc/stdio/stdio.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
*/
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) {
rc = -1;
}
}
}
_spunlock(&__fflush.lock);
} else if (f->fd != -1) {
if (__fflush_impl(f) == -1) {
rc = -1;
}
} else if (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;
_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_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;
}
}
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);
break;
}
}
_spunlock(&__fflush.lock);
}

View file

@ -18,12 +18,14 @@
*/
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/internal.h"
#include "libc/sysv/consts/o.h"
int __fflush_impl(FILE *f) {
size_t i;
ssize_t rc;
free_s(&f->getln);
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) {

View file

@ -18,13 +18,35 @@
*/
#include "libc/stdio/stdio.h"
/**
* Retrieves line from stream, e.g.
*
* char *line;
* while ((line = _chomp(fgetln(stdin, 0)))) {
* printf("%s\n", line);
* }
*
* The returned memory is owned by the stream. It'll be reused when
* fgetln() is called again. It's free()'d upon fclose() / fflush()
*
* @param stream specifies non-null open input stream
* @param len optionally receives byte length of line
* @return nul-terminated line string, including the `\n` character
* unless a line happened before EOF without `\n`, otherwise it
* returns `NULL` and feof() and ferror() can examine the state
* @see getdelim()
*/
char *fgetln(FILE *stream, size_t *len) {
char *res;
ssize_t rc;
size_t n = 0;
if ((rc = getdelim(&stream->getln, &n, '\n', stream)) > 0) {
*len = rc;
return stream->getln;
flockfile(stream);
if ((rc = getdelim_unlocked(&stream->getln, &n, '\n', stream)) > 0) {
if (len) *len = rc;
res = stream->getln;
} else {
return 0;
res = 0;
}
funlockfile(stream);
return res;
}

View file

@ -28,16 +28,18 @@
void flockfile(FILE *f) {
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();
if (__threaded) {
for (tries = 0, me = gettid();;) {
owner = 0;
if (_lockcmpxchgp(&f->lock, &owner, me) || owner == me) {
break;
}
if (++tries & 7) {
__builtin_ia32_pause();
} else {
sched_yield();
}
}
}
++f->reent;
}

View file

@ -29,7 +29,7 @@ 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;
return c & 255;
} else {
b = c;
if (!fwrite_unlocked(&b, 1, 1, f)) return -1;

View file

@ -34,5 +34,8 @@ int ftrylockfile(FILE *f) {
owner = 0;
}
}
if (!owner) {
++f->reent;
}
return owner;
}

View file

@ -16,12 +16,24 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/spinlock.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/stdio/stdio.h"
/**
* Releases lock on stdio object.
*/
void funlockfile(FILE *f) {
_spunlock(&f->lock);
int owner;
bool shouldunlock;
assert(f->reent > 0);
shouldunlock = --f->reent <= 0;
if (__threaded) {
assert(f->lock == gettid());
if (shouldunlock) {
owner = 0;
__atomic_store(&f->lock, &owner, __ATOMIC_RELAXED);
}
}
}

View file

@ -16,64 +16,18 @@
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/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
static ssize_t getdelim_unlocked(char **s, size_t *n, int delim, FILE *f) {
char *p;
ssize_t rc;
size_t i, m;
if ((f->iomode & O_ACCMODE) == O_WRONLY) {
f->state = errno = EBADF;
return -1;
}
if (f->beg > f->end) {
f->state = errno = EINVAL;
return -1;
}
if (!*s) *n = 0;
for (i = 0;; i += m) {
m = f->end - f->beg;
if ((p = memchr(f->buf + f->beg, delim, m))) m = p + 1 - (f->buf + f->beg);
if (i + m + 1 > *n) {
*n = i + m + 1;
*s = realloc(*s, *n);
if (!*s) {
abort();
}
}
memcpy(*s + i, f->buf + f->beg, m);
(*s)[i + m] = '\0';
if ((f->beg += m) == f->end) f->beg = f->end = 0;
if (p) {
return i + m;
} else if (f->fd == -1) {
break;
} else if ((rc = read(f->fd, f->buf, f->size)) != -1) {
if (!rc) break;
f->end = rc;
} else if (errno != EINTR) {
f->state = errno;
return -1;
}
}
f->state = -1;
if (i + m) {
return i + m;
} else {
return -1;
}
}
/**
* Reads string from stream.
* Reads string from stream, e.g.
*
* char *line = NULL;
* size_t linesize = 0;
* while (getdelim(&line, &linesize, '\n', stdin) > 0) {
* _chomp(line);
* printf("%s\n", line);
* }
* free(line);
*
* @param s is the caller's buffer (in/out) which is extended or
* allocated automatically, also NUL-terminated is guaranteed
@ -81,8 +35,9 @@ static ssize_t getdelim_unlocked(char **s, size_t *n, int delim, FILE *f) {
* @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()
* @note this function will ignore EINTR if it occurs mid-line
* @raises EBADF if stream isn't open for reading
* @see fgetln(), getline(), _chomp(), gettok_r()
*/
ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) {
ssize_t rc;

View file

@ -1,7 +1,7 @@
/*-*- 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
/*-*- 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 2020 Justine Alexandra Roberts Tunney
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
@ -16,18 +16,66 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
// Blocks until data from stream buffer is written out.
//
// @param rdi is the stream handle, or 0 for all streams
// @return 0 on success or -1 w/ errno
// @see fflush_unlocked()
// @threadsafe
fflush: ezlea fflush_unlocked,ax
test %rdi,%rdi
jz 1f
mov %rdi,%r11
jmp stdio_unlock
1: jmp *%rax
.endfn fflush,globl
/**
* Reads string from unlocked stream.
* @see getdelim() for documentation
*/
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) {
f->state = errno = EBADF;
return -1;
}
if (f->beg > f->end) {
f->state = errno = EINVAL;
return -1;
}
if (!*s) *n = 0;
for (i = 0;; i += m) {
m = f->end - f->beg;
if ((p = memchr(f->buf + f->beg, delim, m))) {
m = p + 1 - (f->buf + f->beg);
}
if (i + m + 1 > *n) {
n2 = i + m + 1;
s2 = realloc(*s, n2);
if (s2) {
*s = s2;
*n = n2;
} else {
f->state = errno;
return -1;
}
}
memcpy(*s + i, f->buf + f->beg, m);
(*s)[i + m] = '\0';
if ((f->beg += m) == f->end) {
f->beg = f->end = 0;
}
if (p) {
return i + m;
} else if (f->fd == -1) {
break;
} else if ((rc = read(f->fd, f->buf, f->size)) != -1) {
if (!rc) break;
f->end = rc;
} else if (errno != EINTR) {
f->state = errno;
return -1;
}
}
f->state = -1;
if (i + m) {
return i + m;
} else {
return -1;
}
}

View file

@ -19,7 +19,15 @@
#include "libc/stdio/stdio.h"
/**
* Reads line from stream.
* Reads line from stream, e.g.
*
* char *line = NULL;
* size_t linesize = 0;
* while (getline(&line, &linesize, stdin) > 0) {
* _chomp(line);
* printf("%s\n", line);
* }
* free(line);
*
* This function delegates to getdelim(), which provides further
* documentation. Concerning lines, please note the \n or \r\n are
@ -30,7 +38,7 @@
* NUL-termination is guaranteed FTMP
* @return number of bytes read, including delim, excluding NUL, or -1
* w/ errno on EOF or error; see ferror() and feof()
* @see xgetline(), getdelim(), gettok_r()
* @see fgetln(), xgetline(), getdelim(), gettok_r()
*/
ssize_t getline(char **line, size_t *n, FILE *f) {
return getdelim(line, n, '\n', f);

View file

@ -25,7 +25,8 @@ typedef struct FILE {
uint32_t nofree; /* 0x24 */
int pid; /* 0x28 */
int lock; /* 0x2c */
char *getln; /* 0x30 */
int reent; /* 0x30 */
char *getln; /* 0x38 */
} FILE;
extern FILE *stdin;
@ -39,6 +40,7 @@ int getc(FILE *) paramsnonnull();
int putc(int, FILE *) paramsnonnull();
int fflush(FILE *);
int fgetc(FILE *) paramsnonnull();
char *fgetln(FILE *, size_t *) paramsnonnull((1));
int ungetc(int, FILE *) paramsnonnull();
int fileno(FILE *) paramsnonnull() nosideeffect;
int fputc(int, FILE *) paramsnonnull();
@ -120,9 +122,9 @@ int fwide(FILE *, int);
cosmopolitan § standard i/o » without mutexes
*/
void flockfile(FILE *);
void funlockfile(FILE *);
int ftrylockfile(FILE *);
void flockfile(FILE *) paramsnonnull();
void funlockfile(FILE *) paramsnonnull();
int ftrylockfile(FILE *) paramsnonnull();
int getc_unlocked(FILE *) paramsnonnull();
int getchar_unlocked(void);
int putc_unlocked(int, FILE *) paramsnonnull();
@ -149,6 +151,7 @@ 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();
ssize_t getdelim_unlocked(char **, size_t *, int, FILE *) paramsnonnull();
int fprintf_unlocked(FILE *, const char *, ...) printfesque(2)
paramsnonnull((1, 2)) dontthrow nocallback;
int vfprintf_unlocked(FILE *, const char *, va_list)