From c5b0ba4dd0d57551bae8141799a1c958238c3774 Mon Sep 17 00:00:00 2001 From: nick black Date: Mon, 9 Aug 2021 22:35:15 -0400 Subject: [PATCH] fbuf_{flush,finalize}(): don't use unreliable stdio --- src/lib/debug.c | 3 +-- src/lib/direct.c | 8 +++---- src/lib/fbuf.h | 51 ++++++++++++++++++++++++++++++++++----------- src/lib/internal.h | 31 --------------------------- src/lib/notcurses.c | 2 +- src/lib/render.c | 8 +++---- 6 files changed, 49 insertions(+), 54 deletions(-) diff --git a/src/lib/debug.c b/src/lib/debug.c index bcb2ece21..6724fe979 100644 --- a/src/lib/debug.c +++ b/src/lib/debug.c @@ -14,6 +14,5 @@ void notcurses_debug(const notcurses* nc, FILE* debugfp){ return; } notcurses_debug_fbuf(nc, &f); - fbuf_finalize(&f, debugfp, true); - fbuf_free(&f); + fbuf_finalize(&f, debugfp); } diff --git a/src/lib/direct.c b/src/lib/direct.c index 25a87956f..abe002a9f 100644 --- a/src/lib/direct.c +++ b/src/lib/direct.c @@ -524,7 +524,7 @@ ncdirect_dump_plane(ncdirect* n, const ncplane* np, int xoff){ fbuf_free(&f); return -1; } - if(fbuf_finalize(&f, n->ttyfp, true)){ + if(fbuf_finalize(&f, n->ttyfp)){ return -1; } return 0; @@ -973,7 +973,7 @@ int ncdirect_on_styles(ncdirect* n, unsigned stylebits){ fbuf_free(&f); return -1; } - if(fbuf_finalize(&f, n->ttyfp, false)){ + if(fbuf_finalize(&f, n->ttyfp)){ return -1; } return 0; @@ -998,7 +998,7 @@ int ncdirect_off_styles(ncdirect* n, unsigned stylebits){ fbuf_free(&f); return -1; } - if(fbuf_finalize(&f, n->ttyfp, false)){ + if(fbuf_finalize(&f, n->ttyfp)){ return -1; } return 0; @@ -1022,7 +1022,7 @@ int ncdirect_set_styles(ncdirect* n, unsigned stylebits){ fbuf_free(&f); return -1; } - if(fbuf_finalize(&f, n->ttyfp, false)){ + if(fbuf_finalize(&f, n->ttyfp)){ return -1; } return 0; diff --git a/src/lib/fbuf.h b/src/lib/fbuf.h index 3910b8789..d5a95f70a 100644 --- a/src/lib/fbuf.h +++ b/src/lib/fbuf.h @@ -9,8 +9,10 @@ extern "C" { #include #include #include +#include #include #include "compat/compat.h" +#include "logging.h" // a growable buffer into which one can perform formatted i/o, like the // ten thousand that came before it, and the ten trillion which shall @@ -243,36 +245,61 @@ fbuf_free(fbuf* f){ } } +// write(2) until we've written it all. uses poll(2) to avoid spinning on +// EAGAIN, at the possible cost of some small latency. +static inline int +blocking_write(int fd, const char* buf, size_t buflen){ +//fprintf(stderr, "writing %zu to %d...\n", buflen, fd); + size_t written = 0; + while(written < buflen){ + ssize_t w = write(fd, buf + written, buflen - written); + if(w < 0){ + if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR && errno != EBUSY){ + logerror("Error writing out data on %d (%s)\n", fd, strerror(errno)); + return -1; + } + }else{ + written += w; + } + // FIXME ought probably use WSAPoll() on windows +#ifndef __MINGW64__ + if(written < buflen){ + struct pollfd pfd = { + .fd = fd, + .events = POLLOUT, + .revents = 0, + }; + poll(&pfd, 1, -1); + } +#endif + } + return 0; +} + // attempt to write the contents of |f| to the FILE |fp|, if there are any -// contents. reset the fbuf either way. if |flushfp| is set, fflush(fp). +// contents. reset the fbuf either way. static inline int -fbuf_flush(fbuf* f, FILE* fp, bool flushfp){ +fbuf_flush(fbuf* f, FILE* fp){ int ret = 0; if(f->used){ - if(fwrite(f->buf, f->used, 1, fp) != 1){ + if(blocking_write(fileno(fp), f->buf, f->used)){ ret = -1; } } - if(flushfp && ret == 0 && fflush(fp) == EOF){ - ret = -1; - } fbuf_reset(f); return ret; } // attempt to write the contents of |f| to the FILE |fp|, if there are any -// contents, and free the fbuf either way. if |flushfp| is set, fflush(fp). +// contents, and free the fbuf either way. static inline int -fbuf_finalize(fbuf* f, FILE* fp, bool flushfp){ +fbuf_finalize(fbuf* f, FILE* fp){ int ret = 0; if(f->used){ - if(fwrite(f->buf, f->used, 1, fp) != 1){ + if(blocking_write(fileno(fp), f->buf, f->used)){ ret = -1; } } - if(flushfp && ret == 0 && fflush(fp) == EOF){ - ret = -1; - } fbuf_free(f); return ret; } diff --git a/src/lib/internal.h b/src/lib/internal.h index 0271fa97a..f91793c3e 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -1059,37 +1059,6 @@ int ncvisual_blit(struct ncvisual* ncv, int rows, int cols, ncplane* n, const struct blitset* bset, const blitterargs* bargs); -// write(2) until we've written it all. uses poll(2) to avoid spinning on -// EAGAIN, at the possible cost of some small latency. -static inline int -blocking_write(int fd, const char* buf, size_t buflen){ -//fprintf(stderr, "writing %zu to %d...\n", buflen, fd); - size_t written = 0; - while(written < buflen){ - ssize_t w = write(fd, buf + written, buflen - written); - if(w < 0){ - if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR){ - logerror("Error writing out data on %d (%s)\n", fd, strerror(errno)); - return -1; - } - }else{ - written += w; - } - // FIXME ought use WSAPoll() on windows -#ifndef __MINGW64__ - if(written < buflen){ - struct pollfd pfd = { - .fd = fd, - .events = POLLOUT, - .revents = 0, - }; - poll(&pfd, 1, -1); - } -#endif - } - return 0; -} - static inline int tty_emit(const char* seq, int fd){ if(!seq){ diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 9e56132aa..7d7621810 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -1289,7 +1289,7 @@ int notcurses_stop(notcurses* nc){ } fbuf_reset(&nc->rstate.f); goto_location(nc, &nc->rstate.f, targy, 0); - fbuf_finalize(&nc->rstate.f, stdout, true); + fbuf_finalize(&nc->rstate.f, stdout); } ret |= set_fd_nonblocking(nc->tcache.input.infd, nc->stdio_blocking_save, NULL); if(nc->stdplane){ diff --git a/src/lib/render.c b/src/lib/render.c index c849973a9..60b5bd49d 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -1244,7 +1244,7 @@ notcurses_rasterize(notcurses* nc, ncpile* p, fbuf* f){ notcurses_cursor_enable(nc, cursory, cursorx); }else if(nc->rstate.logendy >= 0){ goto_location(nc, f, nc->rstate.logendy, nc->rstate.logendx); - if(fbuf_flush(f, nc->ttyfp, true)){ + if(fbuf_flush(f, nc->ttyfp)){ ret = -1; } } @@ -1449,14 +1449,14 @@ int ncpile_render(ncplane* n){ } int notcurses_render(notcurses* nc){ -//fprintf(stderr, "--------------- BEGIN RENDER %d/%d\n", nc->rstate.y, nc->rstate.x); +//fprintf(stderr, "--------------- BEGIN RENDER\n"); //notcurses_debug(nc, stderr); ncplane* stdn = notcurses_stdplane(nc); if(ncpile_render(stdn)){ return -1; } int i = ncpile_rasterize(stdn); -//fprintf(stderr, "----------------- END RENDER %d/%d\n", nc->rstate.y, nc->rstate.x); +//fprintf(stderr, "----------------- END RENDER\n"); return i; } @@ -1600,7 +1600,7 @@ int notcurses_cursor_enable(notcurses* nc, int y, int x){ fbuf_free(&f); return -1; } - if(fbuf_finalize(&f, nc->ttyfp, true)){ + if(fbuf_finalize(&f, nc->ttyfp)){ return -1; } // if we were already positive, we're already visible, no need to write cnorm