No locks, more lox #290 (#358)

* README/CMake: only require doctest 2.3.5

* man pages: remove talk of locking #290

* Remove locking from notcurses core #290

* Purge locking from notcurses core #290
This commit is contained in:
Nick Black 2020-02-18 20:03:20 -05:00 committed by GitHub
parent 887d4f1f79
commit e5d6685c92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 70 additions and 192 deletions

View File

@ -32,7 +32,7 @@ pkg_check_modules(AVUTIL REQUIRED libavutil>=56.0)
pkg_check_modules(SWSCALE REQUIRED libswscale>=5.0) pkg_check_modules(SWSCALE REQUIRED libswscale>=5.0)
endif() endif()
find_library(LIBRT rt) find_library(LIBRT rt)
find_package(doctest 2.3.6 REQUIRED) find_package(doctest 2.3.5 REQUIRED)
# libnotcurses # libnotcurses
file(GLOB NCSRCS CONFIGURE_DEPENDS src/lib/*.c) file(GLOB NCSRCS CONFIGURE_DEPENDS src/lib/*.c)

View File

@ -113,7 +113,7 @@ that fine library.
* (build) CMake 3.14.0+ * (build) CMake 3.14.0+
* (build+runtime) From NCURSES: terminfo 6.1+ * (build+runtime) From NCURSES: terminfo 6.1+
* (OPTIONAL) (build+runtime) From FFMpeg: libswscale 5.0+, libavformat 57.0+, libavutil 56.0+ * (OPTIONAL) (build+runtime) From FFMpeg: libswscale 5.0+, libavformat 57.0+, libavutil 56.0+
* (OPTIONAL) (testing) [Doctest](https://github.com/onqtam/doctest) 2.3.6+ * (OPTIONAL) (testing) [Doctest](https://github.com/onqtam/doctest) 2.3.5+
* (OPTIONAL) (documentation) [pandoc](https://pandoc.org/index.html) 1.19.2+ * (OPTIONAL) (documentation) [pandoc](https://pandoc.org/index.html) 1.19.2+
* (OPTIONAL) (python bindings): Python 3.7+, CFFI 1.13.2+ * (OPTIONAL) (python bindings): Python 3.7+, CFFI 1.13.2+
* (OPTIONAL) (rust bindings, colloquy): rust 1.40.0+, cargo 0.40.0+, cmake-rs 0.1.42+ * (OPTIONAL) (rust bindings, colloquy): rust 1.40.0+, cargo 0.40.0+, cmake-rs 0.1.42+
@ -292,7 +292,8 @@ int notcurses_resize(struct notcurses* n, int* RESTRICT y, int* RESTRICT x);
// Return our current idea of the terminal dimensions in rows and cols. // Return our current idea of the terminal dimensions in rows and cols.
static inline void static inline void
notcurses_term_dim_yx(struct notcurses* n, int* RESTRICT rows, int* RESTRICT cols){ notcurses_term_dim_yx(const struct notcurses* n, int* RESTRICT rows,
int* RESTRICT cols){
ncplane_dim_yx(notcurses_stdplane(n), rows, cols); ncplane_dim_yx(notcurses_stdplane(n), rows, cols);
} }
@ -396,7 +397,7 @@ typedef enum {
// according to 'align' within ncplane 'n'. Returns INT_MAX on invalid 'align'. // according to 'align' within ncplane 'n'. Returns INT_MAX on invalid 'align'.
// Undefined behavior on negative 'c'. // Undefined behavior on negative 'c'.
static inline int static inline int
ncplane_align(struct ncplane* n, ncalign_e align, int c){ ncplane_align(const struct ncplane* n, ncalign_e align, int c){
if(align == NCALIGN_LEFT){ if(align == NCALIGN_LEFT){
return 0; return 0;
} }
@ -672,14 +673,14 @@ void ncplane_yx(struct ncplane* n, int* RESTRICT y, int* RESTRICT x);
void ncplane_dim_yx(struct ncplane* n, int* RESTRICT rows, int* RESTRICT cols); void ncplane_dim_yx(struct ncplane* n, int* RESTRICT rows, int* RESTRICT cols);
static inline int static inline int
ncplane_dim_y(struct ncplane* n){ ncplane_dim_y(const struct ncplane* n){
int dimy; int dimy;
ncplane_dim_yx(n, &dimy, NULL); ncplane_dim_yx(n, &dimy, NULL);
return dimy; return dimy;
} }
static inline int static inline int
ncplane_dim_x(struct ncplane* n){ ncplane_dim_x(const struct ncplane* n){
int dimx; int dimx;
ncplane_dim_yx(n, NULL, &dimx); ncplane_dim_yx(n, NULL, &dimx);
return dimx; return dimx;
@ -705,7 +706,7 @@ void ncplane_styles_on(struct ncplane* n, unsigned stylebits);
void ncplane_styles_off(struct ncplane* n, unsigned stylebits); void ncplane_styles_off(struct ncplane* n, unsigned stylebits);
// Return the current styling for this ncplane. // Return the current styling for this ncplane.
unsigned ncplane_styles(struct ncplane* n); unsigned ncplane_styles(const struct ncplane* n);
// Set the ncplane's base cell to this cell. It will be used for purposes of // Set the ncplane's base cell to this cell. It will be used for purposes of
// rendering anywhere that the ncplane's gcluster is 0. Erasing the ncplane // rendering anywhere that the ncplane's gcluster is 0. Erasing the ncplane

View File

@ -13,4 +13,6 @@ LICENSE=Apache-2.0
USE_GITHUB=yes USE_GITHUB=yes
GH_ACCOUNT=dankamongmen GH_ACCOUNT=dankamongmen
# deps: doxygen, dia, doctest (not present?), ffmpeg
.include <bsd.port.mk> .include <bsd.port.mk>

View File

@ -102,13 +102,17 @@ A few high-level widgets are included, all built atop ncplanes:
## Threads ## Threads
Notcurses explicitly supports use in multithreaded environments. Most functions Notcurses explicitly supports use in multithreaded environments, but it does
are safe to call concurrently, with exceptions including those which destroy not itself perform any locking. It is safe to output to multiple distinct
resources (**ncplane_destroy(3)**, **ncvisual_destroy(3)**, **notcurses_stop(3)**, ncplanes at the same time. It is safe to output to ncplanes while adding or
etc.). Multiple threads interacting with the same ncplane will block one another, deleting some other ncplane. It is **not** safe for multiple threads to output to
but threads operating on distinct ncplanes can run concurrently. the same ncplane. It is **not** safe to add, delete, or reorder ncplanes
**notcurses_render(3)** blocks the majority of functions. Input functions only from multiple threads, and it is never safe to invoke **notcurses_render**
block other input functions, not ncplane manipulation. while any other thread is touching that notcurses object (aside from input
functions; read on).
Only one thread may call **notcurses_getc** or any other input-related thread
at a time, but it **is** safe to call for input while another thread renders.
Since multiple threads can concurrently manipulate distinct ncplanes, peak Since multiple threads can concurrently manipulate distinct ncplanes, peak
performance sometimes requires dividing the screen into several planes, and performance sometimes requires dividing the screen into several planes, and

View File

@ -112,6 +112,10 @@ Like any other notcurses function, it is an error to call **notcurses_getc(3)**
during or after a call to **notcurses_stop(3)**. If a thread is always sitting during or after a call to **notcurses_stop(3)**. If a thread is always sitting
on blocking input, it can be tricky to guarantee that this doesn't happen. on blocking input, it can be tricky to guarantee that this doesn't happen.
Only one thread may call into the input stack at once, but unlike almost every
other function in notcurses, **notcurses_getc** and friends can be called
concurrently with **notcurses_render**.
# BUGS # BUGS
Failed escape sequences are not yet played back in their entirety; only an Failed escape sequences are not yet played back in their entirety; only an
@ -134,6 +138,7 @@ registers as **NCKEY_ENTER**. This will likely change in the future.
**poll(2)**, **poll(2)**,
**cfmakeraw(3)**, **cfmakeraw(3)**,
**notcurses(3)**, **notcurses(3)**,
**notcurses_render(3)**,
**notcurses_resize(3)**, **notcurses_resize(3)**,
**termios(3)**, **termios(3)**,
**terminfo(5)**, **terminfo(5)**,

View File

@ -48,11 +48,11 @@ notcurses_ncplane - operations on notcurses planes
**void* ncplane_userptr(struct ncplane* n);** **void* ncplane_userptr(struct ncplane* n);**
**void ncplane_dim_yx(struct ncplane* n, int* restrict rows, int* restrict cols);** **void ncplane_dim_yx(const struct ncplane* n, int* restrict rows, int* restrict cols);**
**static inline int ncplane_dim_y(struct ncplane* n);** **static inline int ncplane_dim_y(const struct ncplane* n);**
**static inline int ncplane_dim_x(struct ncplane* n);** **static inline int ncplane_dim_x(const struct ncplane* n);**
**int ncplane_cursor_move_yx(struct ncplane* n, int y, int x);** **int ncplane_cursor_move_yx(struct ncplane* n, int y, int x);**
@ -108,7 +108,7 @@ notcurses_ncplane - operations on notcurses planes
**void ncplane_styles_off(struct ncplane* n, unsigned stylebits);** **void ncplane_styles_off(struct ncplane* n, unsigned stylebits);**
**unsigned ncplane_styles(struct ncplane* n);** **unsigned ncplane_styles(const struct ncplane* n);**
**void ncplane_greyscale(struct ncplane* n);** **void ncplane_greyscale(struct ncplane* n);**
@ -137,6 +137,10 @@ anywhere. In addition to its framebuffer--a rectilinear matrix of cells
**notcurses_drop_planes** destroys all ncplanes other than the stdplane. Any **notcurses_drop_planes** destroys all ncplanes other than the stdplane. Any
references to such planes are, of course, invalidated. references to such planes are, of course, invalidated.
It is an error for two threads to concurrently access a single ncplane. So long
as rendering is not taking place, however, multiple threads may safely output
to multiple ncplanes.
# RETURN VALUES # RETURN VALUES
**ncplane_new(3)**, **ncplane_aligned(3)**, and **ncplane_dup(3)** all return a **ncplane_new(3)**, **ncplane_aligned(3)**, and **ncplane_dup(3)** all return a
@ -154,9 +158,6 @@ All other functions cannot fail (and return **void**).
# NOTES # NOTES
It would be reasonable to expect many of these functions to accept `const struct notcurses`
parameters. Alas, almost all must manipulate the mutex contained within the object.
# SEE ALSO # SEE ALSO
**notcurses(3)**, **notcurses_cell(3)**, **notcurses_output(3)**, **notcurses(3)**, **notcurses_cell(3)**, **notcurses_output(3)**,

View File

@ -23,17 +23,20 @@ ncplanes. Most of the notcurses statistics are updated as a result of a
render (see notcurses_stats(3)), and **notcurses_resize(3)** is called render (see notcurses_stats(3)), and **notcurses_resize(3)** is called
internally *following* the render. internally *following* the render.
While **notcurses_render** is called, you **must not call any other functions
on the same notcurses context**, with the one exception of **notcurses_getc**
(and its input-related helpers).
A render operation consists of two logical phases: generation of the rendered A render operation consists of two logical phases: generation of the rendered
scene, and blitting this scene to the terminal (these two phases might actually scene, and blitting this scene to the terminal (these two phases might actually
be interleaved, streaming the output as it is rendered). All ncplanes are be interleaved, streaming the output as it is rendered). Frame generation
locked while generating the frame. Frame generation requires determining an requires determining an extended grapheme cluster, foreground color, background
extended grapheme cluster, foreground color, background color, and style for color, and style for each cell of the physical terminal. Writing the scene
each cell of the physical terminal. Writing the scene requires synthesizing requires synthesizing a set of UTF-8-encoded characters and escape codes
a set of UTF-8-encoded characters and escape codes appropriate for the terminal appropriate for the terminal (relying on terminfo(5)), and writing this
(relying on terminfo(5)), and writing this sequence to the output **FILE**. If sequence to the output **FILE**. If the **renderfp** value was not NULL in the
the **renderfp** value was not NULL in the original call to notcurses_init(3), original call to notcurses_init(3), the frame will be written to that **FILE**
the frame will be written to that **FILE** as well. This write does not affect as well. This write does not affect statistics.
statistics.
Each cell can be rendered in isolation, though synthesis of the stream carries Each cell can be rendered in isolation, though synthesis of the stream carries
dependencies between cells. dependencies between cells.

View File

@ -416,23 +416,24 @@ typedef struct ncstats {
} ncstats; } ncstats;
// Acquire an atomic snapshot of the notcurses object's stats. // Acquire an atomic snapshot of the notcurses object's stats.
API void notcurses_stats(struct notcurses* nc, ncstats* stats); API void notcurses_stats(const struct notcurses* nc, ncstats* stats);
// Reset all cumulative stats (immediate ones, such as fbbytes, are not reset). // Reset all cumulative stats (immediate ones, such as fbbytes, are not reset).
API void notcurses_reset_stats(struct notcurses* nc, ncstats* stats); API void notcurses_reset_stats(struct notcurses* nc, ncstats* stats);
// Return the dimensions of this ncplane. // Return the dimensions of this ncplane.
API void ncplane_dim_yx(struct ncplane* n, int* RESTRICT rows, int* RESTRICT cols); API void ncplane_dim_yx(const struct ncplane* n, int* RESTRICT rows,
int* RESTRICT cols);
static inline int static inline int
ncplane_dim_y(struct ncplane* n){ ncplane_dim_y(const struct ncplane* n){
int dimy; int dimy;
ncplane_dim_yx(n, &dimy, NULL); ncplane_dim_yx(n, &dimy, NULL);
return dimy; return dimy;
} }
static inline int static inline int
ncplane_dim_x(struct ncplane* n){ ncplane_dim_x(const struct ncplane* n){
int dimx; int dimx;
ncplane_dim_yx(n, NULL, &dimx); ncplane_dim_yx(n, NULL, &dimx);
return dimx; return dimx;
@ -550,7 +551,7 @@ API void* ncplane_userptr(struct ncplane* n);
// according to 'align' within ncplane 'n'. Returns INT_MAX on invalid 'align'. // according to 'align' within ncplane 'n'. Returns INT_MAX on invalid 'align'.
// Undefined behavior on negative 'c'. // Undefined behavior on negative 'c'.
static inline int static inline int
ncplane_align(struct ncplane* n, ncalign_e align, int c){ ncplane_align(const struct ncplane* n, ncalign_e align, int c){
if(align == NCALIGN_LEFT){ if(align == NCALIGN_LEFT){
return 0; return 0;
} }
@ -907,7 +908,7 @@ channel_b(unsigned channel){
// Extract the three 8-bit R/G/B components from a 32-bit channel. // Extract the three 8-bit R/G/B components from a 32-bit channel.
static inline unsigned static inline unsigned
channel_rgb(unsigned channel, unsigned* RESTRICT r, unsigned* RESTRICT g, channel_rgb(unsigned channel, unsigned* RESTRICT r, unsigned* RESTRICT g,
unsigned* RESTRICT b){ unsigned* RESTRICT b){
*r = channel_r(channel); *r = channel_r(channel);
*g = channel_g(channel); *g = channel_g(channel);
*b = channel_b(channel); *b = channel_b(channel);
@ -1495,7 +1496,7 @@ API void ncplane_styles_on(struct ncplane* n, unsigned stylebits);
API void ncplane_styles_off(struct ncplane* n, unsigned stylebits); API void ncplane_styles_off(struct ncplane* n, unsigned stylebits);
// Return the current styling for this ncplane. // Return the current styling for this ncplane.
API unsigned ncplane_styles(struct ncplane* n); API unsigned ncplane_styles(const struct ncplane* n);
// Called for each delta performed in a fade on ncp. If anything but 0 is returned, // Called for each delta performed in a fade on ncp. If anything but 0 is returned,
// the fading operation ceases immediately, and that value is propagated out. If provided // the fading operation ceases immediately, and that value is propagated out. If provided

View File

@ -109,7 +109,7 @@ int ncplane_cursor_move_yx(struct ncplane* n, int y, int x);
void ncplane_cursor_yx(struct ncplane* n, int* y, int* x); void ncplane_cursor_yx(struct ncplane* n, int* y, int* x);
int ncplane_move_yx(struct ncplane* n, int y, int x); int ncplane_move_yx(struct ncplane* n, int y, int x);
void ncplane_yx(struct ncplane* n, int* y, int* x); void ncplane_yx(struct ncplane* n, int* y, int* x);
void ncplane_dim_yx(struct ncplane* n, int* rows, int* cols); void ncplane_dim_yx(const struct ncplane* n, int* rows, int* cols);
int ncplane_putc_yx(struct ncplane* n, int y, int x, const cell* c); int ncplane_putc_yx(struct ncplane* n, int y, int x, const cell* c);
int ncplane_putsimple_yx(struct ncplane* n, int y, int x, char c); int ncplane_putsimple_yx(struct ncplane* n, int y, int x, char c);
int ncplane_move_top(struct ncplane* n); int ncplane_move_top(struct ncplane* n);
@ -149,7 +149,7 @@ int ncplane_set_bg_palindex(struct ncplane* n, int idx);
void ncplane_styles_set(struct ncplane* n, unsigned stylebits); void ncplane_styles_set(struct ncplane* n, unsigned stylebits);
void ncplane_styles_on(struct ncplane* n, unsigned stylebits); void ncplane_styles_on(struct ncplane* n, unsigned stylebits);
void ncplane_styles_off(struct ncplane* n, unsigned stylebits); void ncplane_styles_off(struct ncplane* n, unsigned stylebits);
unsigned ncplane_styles(struct ncplane* n); unsigned ncplane_styles(const struct ncplane* n);
typedef struct ncstats { typedef struct ncstats {
uint64_t renders; // number of successful notcurses_render() runs uint64_t renders; // number of successful notcurses_render() runs
uint64_t failed_renders; // number of aborted renders, should be 0 uint64_t failed_renders; // number of aborted renders, should be 0

View File

@ -13,12 +13,10 @@ typedef struct planepalette {
// the maxima across each of the six components. // the maxima across each of the six components.
static int static int
alloc_ncplane_palette(ncplane* n, planepalette* pp){ alloc_ncplane_palette(ncplane* n, planepalette* pp){
ncplane_lock(n);
ncplane_dim_yx(n, &pp->rows, &pp->cols); ncplane_dim_yx(n, &pp->rows, &pp->cols);
// add an additional element for the background cell // add an additional element for the background cell
int size = pp->rows * pp->cols + 1; int size = pp->rows * pp->cols + 1;
if((pp->channels = malloc(sizeof(*pp->channels) * size)) == NULL){ if((pp->channels = malloc(sizeof(*pp->channels) * size)) == NULL){
ncplane_unlock(n);
return -1; return -1;
} }
pp->maxr = pp->maxg = pp->maxb = 0; pp->maxr = pp->maxg = pp->maxb = 0;
@ -75,7 +73,6 @@ alloc_ncplane_palette(ncplane* n, planepalette* pp){
if(bb > pp->maxbb){ if(bb > pp->maxbb){
pp->maxbb = bb; pp->maxbb = bb;
} }
ncplane_unlock(n);
return 0; return 0;
} }

View File

@ -209,7 +209,6 @@ typedef struct ncdirect {
} ncdirect; } ncdirect;
typedef struct notcurses { typedef struct notcurses {
pthread_mutex_t lock;
ncplane* top; // the contents of our topmost plane (initially entire screen) ncplane* top; // the contents of our topmost plane (initially entire screen)
ncplane* stdscr;// aliases some plane from the z-buffer, covers screen ncplane* stdscr;// aliases some plane from the z-buffer, covers screen
@ -318,16 +317,6 @@ void input_free_esctrie(struct esctrie** trie);
// initialize libav // initialize libav
int ncvisual_init(int loglevel); int ncvisual_init(int loglevel);
static inline void
ncplane_lock(const ncplane* n){
pthread_mutex_lock(&n->nc->lock);
}
static inline void
ncplane_unlock(const ncplane* n){
pthread_mutex_unlock(&n->nc->lock);
}
static inline int static inline int
fbcellidx(int row, int rowlen, int col){ fbcellidx(int row, int rowlen, int col){
return row * rowlen + col; return row * rowlen + col;

View File

@ -191,8 +191,7 @@ int ncplane_at_cursor(ncplane* n, cell* c){
return cell_duplicate(n, c, &n->fb[nfbcellidx(n, n->y, n->x)]); return cell_duplicate(n, c, &n->fb[nfbcellidx(n, n->y, n->x)]);
} }
static inline int int ncplane_at_yx(ncplane* n, int y, int x, cell* c){
ncplane_at_yx_locked(ncplane* n, int y, int x, cell* c){
int ret = -1; int ret = -1;
if(y < n->leny && x < n->lenx){ if(y < n->leny && x < n->lenx){
if(y >= 0 && x >= 0){ if(y >= 0 && x >= 0){
@ -202,19 +201,11 @@ ncplane_at_yx_locked(ncplane* n, int y, int x, cell* c){
return ret; return ret;
} }
int ncplane_at_yx(ncplane* n, int y, int x, cell* c){
int ret = -1;
pthread_mutex_lock(&n->nc->lock);
ret = ncplane_at_yx_locked(n, y, x, c);
pthread_mutex_unlock(&n->nc->lock);
return ret;
}
cell* ncplane_cell_ref_yx(ncplane* n, int y, int x){ cell* ncplane_cell_ref_yx(ncplane* n, int y, int x){
return &n->fb[nfbcellidx(n, y, x)]; return &n->fb[nfbcellidx(n, y, x)];
} }
void ncplane_dim_yx(ncplane* n, int* rows, int* cols){ void ncplane_dim_yx(const ncplane* n, int* rows, int* cols){
if(rows){ if(rows){
*rows = n->leny; *rows = n->leny;
} }
@ -263,10 +254,8 @@ term_verify_seq(char** gseq, const char* name){
static void static void
free_plane(ncplane* p){ free_plane(ncplane* p){
if(p){ if(p){
ncplane_lock(p);
--p->nc->stats.planes; --p->nc->stats.planes;
p->nc->stats.fbbytes -= sizeof(*p->fb) * p->leny * p->lenx; p->nc->stats.fbbytes -= sizeof(*p->fb) * p->leny * p->lenx;
ncplane_unlock(p);
egcpool_dump(&p->pool); egcpool_dump(&p->pool);
free(p->fb); free(p->fb);
free(p); free(p);
@ -300,13 +289,11 @@ ncplane_create(notcurses* nc, int rows, int cols, int yoff, int xoff){
p->channels = 0; p->channels = 0;
egcpool_init(&p->pool); egcpool_init(&p->pool);
cell_init(&p->basecell); cell_init(&p->basecell);
pthread_mutex_lock(&nc->lock);
p->z = nc->top; p->z = nc->top;
nc->top = p; nc->top = p;
p->nc = nc; p->nc = nc;
nc->stats.fbbytes += fbsize; nc->stats.fbbytes += fbsize;
++nc->stats.planes; ++nc->stats.planes;
pthread_mutex_unlock(&nc->lock);
return p; return p;
} }
@ -741,17 +728,13 @@ stash_stats(notcurses* nc){
reset_stats(&nc->stats); reset_stats(&nc->stats);
} }
void notcurses_stats(notcurses* nc, ncstats* stats){ void notcurses_stats(const notcurses* nc, ncstats* stats){
pthread_mutex_lock(&nc->lock);
memcpy(stats, &nc->stats, sizeof(*stats)); memcpy(stats, &nc->stats, sizeof(*stats));
pthread_mutex_unlock(&nc->lock);
} }
void notcurses_reset_stats(notcurses* nc, ncstats* stats){ void notcurses_reset_stats(notcurses* nc, ncstats* stats){
pthread_mutex_lock(&nc->lock);
memcpy(stats, &nc->stats, sizeof(*stats)); memcpy(stats, &nc->stats, sizeof(*stats));
stash_stats(nc); stash_stats(nc);
pthread_mutex_unlock(&nc->lock);
} }
// Convert a notcurses log level to its ffmpeg equivalent. // Convert a notcurses log level to its ffmpeg equivalent.
@ -847,10 +830,6 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){
if(ret == NULL){ if(ret == NULL){
return ret; return ret;
} }
if(pthread_mutex_init(&ret->lock, NULL)){
free(ret);
return NULL;
}
ret->stats.fbbytes = 0; ret->stats.fbbytes = 0;
ret->stashstats.fbbytes = 0; ret->stashstats.fbbytes = 0;
reset_stats(&ret->stats); reset_stats(&ret->stats);
@ -973,8 +952,8 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){
return ret; return ret;
err: err:
// FIXME looks like we have some memory leaks on this error path?
tcsetattr(ret->ttyfd, TCSANOW, &ret->tpreserved); tcsetattr(ret->ttyfd, TCSANOW, &ret->tpreserved);
pthread_mutex_destroy(&ret->lock);
free(ret); free(ret);
return NULL; return NULL;
} }
@ -1033,7 +1012,6 @@ int notcurses_stop(notcurses* nc){
free(nc->lastframe); free(nc->lastframe);
free(nc->rstate.mstream); free(nc->rstate.mstream);
input_free_esctrie(&nc->inputescapes); input_free_esctrie(&nc->inputescapes);
ret |= pthread_mutex_destroy(&nc->lock);
stash_stats(nc); stash_stats(nc);
if(!nc->suppress_banner){ if(!nc->suppress_banner){
double avg = 0; double avg = 0;
@ -1098,21 +1076,15 @@ uint32_t ncplane_attr(const ncplane* n){
} }
void ncplane_set_fg_default(struct ncplane* n){ void ncplane_set_fg_default(struct ncplane* n){
ncplane_lock(n);
channels_set_fg_default(&n->channels); channels_set_fg_default(&n->channels);
ncplane_unlock(n);
} }
void ncplane_set_bg_default(struct ncplane* n){ void ncplane_set_bg_default(struct ncplane* n){
ncplane_lock(n);
channels_set_bg_default(&n->channels); channels_set_bg_default(&n->channels);
ncplane_unlock(n);
} }
void ncplane_set_bg_rgb_clipped(ncplane* n, int r, int g, int b){ void ncplane_set_bg_rgb_clipped(ncplane* n, int r, int g, int b){
ncplane_lock(n);
channels_set_bg_rgb_clipped(&n->channels, r, g, b); channels_set_bg_rgb_clipped(&n->channels, r, g, b);
ncplane_unlock(n);
} }
int ncplane_set_bg_rgb(ncplane* n, int r, int g, int b){ int ncplane_set_bg_rgb(ncplane* n, int r, int g, int b){
@ -1120,9 +1092,7 @@ int ncplane_set_bg_rgb(ncplane* n, int r, int g, int b){
} }
void ncplane_set_fg_rgb_clipped(ncplane* n, int r, int g, int b){ void ncplane_set_fg_rgb_clipped(ncplane* n, int r, int g, int b){
ncplane_lock(n);
channels_set_fg_rgb_clipped(&n->channels, r, g, b); channels_set_fg_rgb_clipped(&n->channels, r, g, b);
ncplane_unlock(n);
} }
int ncplane_set_fg_rgb(ncplane* n, int r, int g, int b){ int ncplane_set_fg_rgb(ncplane* n, int r, int g, int b){
@ -1130,48 +1100,30 @@ int ncplane_set_fg_rgb(ncplane* n, int r, int g, int b){
} }
int ncplane_set_fg(ncplane* n, unsigned channel){ int ncplane_set_fg(ncplane* n, unsigned channel){
int ret; return channels_set_fg(&n->channels, channel);
ncplane_lock(n);
ret = channels_set_fg(&n->channels, channel);
ncplane_unlock(n);
return ret;
} }
int ncplane_set_bg(ncplane* n, unsigned channel){ int ncplane_set_bg(ncplane* n, unsigned channel){
int ret; return channels_set_bg(&n->channels, channel);
ncplane_lock(n);
ret = channels_set_bg(&n->channels, channel);
ncplane_unlock(n);
return ret;
} }
int ncplane_set_fg_alpha(ncplane* n, int alpha){ int ncplane_set_fg_alpha(ncplane* n, int alpha){
int ret; return channels_set_fg_alpha(&n->channels, alpha);
ncplane_lock(n);
ret = channels_set_fg_alpha(&n->channels, alpha);
ncplane_unlock(n);
return ret;
} }
int ncplane_set_bg_alpha(ncplane *n, int alpha){ int ncplane_set_bg_alpha(ncplane *n, int alpha){
int ret; return channels_set_bg_alpha(&n->channels, alpha);
ncplane_lock(n);
ret = channels_set_bg_alpha(&n->channels, alpha);
ncplane_unlock(n);
return ret;
} }
int ncplane_set_fg_palindex(ncplane* n, int idx){ int ncplane_set_fg_palindex(ncplane* n, int idx){
if(idx < 0 || idx >= NCPALETTESIZE){ if(idx < 0 || idx >= NCPALETTESIZE){
return -1; return -1;
} }
ncplane_lock(n);
n->channels |= CELL_FGDEFAULT_MASK; n->channels |= CELL_FGDEFAULT_MASK;
n->channels |= CELL_FG_PALETTE; n->channels |= CELL_FG_PALETTE;
n->channels &= ~(CELL_ALPHA_MASK << 32u); n->channels &= ~(CELL_ALPHA_MASK << 32u);
n->attrword &= 0xffff00ff; n->attrword &= 0xffff00ff;
n->attrword |= (idx << 8u); n->attrword |= (idx << 8u);
ncplane_unlock(n);
return 0; return 0;
} }
@ -1179,40 +1131,24 @@ int ncplane_set_bg_palindex(ncplane* n, int idx){
if(idx < 0 || idx >= NCPALETTESIZE){ if(idx < 0 || idx >= NCPALETTESIZE){
return -1; return -1;
} }
ncplane_lock(n);
n->channels |= CELL_BGDEFAULT_MASK; n->channels |= CELL_BGDEFAULT_MASK;
n->channels |= CELL_BG_PALETTE; n->channels |= CELL_BG_PALETTE;
n->channels &= ~CELL_ALPHA_MASK; n->channels &= ~CELL_ALPHA_MASK;
n->attrword &= 0xffffff00; n->attrword &= 0xffffff00;
n->attrword |= idx; n->attrword |= idx;
ncplane_unlock(n);
return 0; return 0;
} }
int ncplane_set_base_cell(ncplane* ncp, const cell* c){ int ncplane_set_base_cell(ncplane* ncp, const cell* c){
ncplane_lock(ncp); return cell_duplicate(ncp, &ncp->basecell, c);
int ret = cell_duplicate(ncp, &ncp->basecell, c);
ncplane_unlock(ncp);
if(ret < 0){
return -1;
}
return ret;
} }
int ncplane_set_base(ncplane* ncp, uint64_t channels, uint32_t attrword, const char* egc){ int ncplane_set_base(ncplane* ncp, uint64_t channels, uint32_t attrword, const char* egc){
int ret; return cell_prime(ncp, &ncp->basecell, egc, attrword, channels);
ncplane_lock(ncp);
ret = cell_prime(ncp, &ncp->basecell, egc, attrword, channels);
ncplane_unlock(ncp);
return ret;
} }
int ncplane_base(ncplane* ncp, cell* c){ int ncplane_base(ncplane* ncp, cell* c){
int ret; return cell_duplicate(ncp, c, &ncp->basecell);
ncplane_lock(ncp);
ret = cell_duplicate(ncp, c, &ncp->basecell);
ncplane_unlock(ncp);
return ret;
} }
const char* cell_extended_gcluster(const struct ncplane* n, const cell* c){ const char* cell_extended_gcluster(const struct ncplane* n, const cell* c){
@ -1232,66 +1168,52 @@ advance_cursor(ncplane* n, int cols){
// 'n' ends up above 'above' // 'n' ends up above 'above'
int ncplane_move_above_unsafe(ncplane* restrict n, ncplane* restrict above){ int ncplane_move_above_unsafe(ncplane* restrict n, ncplane* restrict above){
ncplane_lock(n);
if(n->z == above){ if(n->z == above){
ncplane_unlock(n);
return 0; return 0;
} }
ncplane** an = find_above_ncplane(n); ncplane** an = find_above_ncplane(n);
if(an == NULL){ if(an == NULL){
ncplane_unlock(n);
return -1; return -1;
} }
ncplane** aa = find_above_ncplane(above); ncplane** aa = find_above_ncplane(above);
if(aa == NULL){ if(aa == NULL){
ncplane_unlock(n);
return -1; return -1;
} }
*an = n->z; // splice n out *an = n->z; // splice n out
n->z = above; // attach above below n n->z = above; // attach above below n
*aa = n; // spline n in above *aa = n; // spline n in above
ncplane_unlock(n);
return 0; return 0;
} }
// 'n' ends up below 'below' // 'n' ends up below 'below'
int ncplane_move_below_unsafe(ncplane* restrict n, ncplane* restrict below){ int ncplane_move_below_unsafe(ncplane* restrict n, ncplane* restrict below){
ncplane_lock(n);
if(below->z == n){ if(below->z == n){
ncplane_unlock(n);
return 0; return 0;
} }
ncplane** an = find_above_ncplane(n); ncplane** an = find_above_ncplane(n);
if(an == NULL){ if(an == NULL){
ncplane_unlock(n);
return -1; return -1;
} }
*an = n->z; // splice n out *an = n->z; // splice n out
n->z = below->z; // reattach subbelow list to n n->z = below->z; // reattach subbelow list to n
below->z = n; // splice n in below below->z = n; // splice n in below
ncplane_unlock(n);
return 0; return 0;
} }
int ncplane_move_top(ncplane* n){ int ncplane_move_top(ncplane* n){
ncplane_lock(n);
ncplane** an = find_above_ncplane(n); ncplane** an = find_above_ncplane(n);
if(an == NULL){ if(an == NULL){
ncplane_unlock(n);
return -1; return -1;
} }
*an = n->z; // splice n out *an = n->z; // splice n out
n->z = n->nc->top; n->z = n->nc->top;
n->nc->top = n; n->nc->top = n;
ncplane_unlock(n);
return 0; return 0;
} }
int ncplane_move_bottom(ncplane* n){ int ncplane_move_bottom(ncplane* n){
ncplane_lock(n);
ncplane** an = find_above_ncplane(n); ncplane** an = find_above_ncplane(n);
if(an == NULL){ if(an == NULL){
ncplane_unlock(n);
return -1; return -1;
} }
*an = n->z; // splice n out *an = n->z; // splice n out
@ -1301,15 +1223,11 @@ int ncplane_move_bottom(ncplane* n){
} }
*an = n; *an = n;
n->z = NULL; n->z = NULL;
ncplane_unlock(n);
return 0; return 0;
} }
int ncplane_cursor_move_yx(ncplane* n, int y, int x){ int ncplane_cursor_move_yx(ncplane* n, int y, int x){
ncplane_lock(n); return ncplane_cursor_move_yx_locked(n, y, x);
int ret = ncplane_cursor_move_yx_locked(n, y, x);
ncplane_unlock(n);
return ret;
} }
void ncplane_cursor_yx(ncplane* n, int* y, int* x){ void ncplane_cursor_yx(ncplane* n, int* y, int* x){
@ -1333,14 +1251,11 @@ cell_obliterate(ncplane* n, cell* c){
} }
int ncplane_putc_yx(ncplane* n, int y, int x, const cell* c){ int ncplane_putc_yx(ncplane* n, int y, int x, const cell* c){
ncplane_lock(n);
if(ncplane_cursor_move_yx_locked(n, y, x)){ if(ncplane_cursor_move_yx_locked(n, y, x)){
ncplane_unlock(n);
return -1; return -1;
} }
bool wide = cell_double_wide_p(c); bool wide = cell_double_wide_p(c);
if(wide && (n->x + 1 == n->lenx)){ if(wide && (n->x + 1 == n->lenx)){
ncplane_unlock(n);
return -1; return -1;
} }
// A wide character obliterates anything to its immediate right (and marks // A wide character obliterates anything to its immediate right (and marks
@ -1358,7 +1273,6 @@ int ncplane_putc_yx(ncplane* n, int y, int x, const cell* c){
} }
} }
if(cell_duplicate(n, targ, c) < 0){ if(cell_duplicate(n, targ, c) < 0){
ncplane_unlock(n);
return -1; return -1;
} }
int cols = 1; int cols = 1;
@ -1382,7 +1296,6 @@ int ncplane_putc_yx(ncplane* n, int y, int x, const cell* c){
} }
} }
advance_cursor(n, cols); advance_cursor(n, cols);
ncplane_unlock(n);
return cols; return cols;
} }
@ -1492,31 +1405,21 @@ int notcurses_palette_size(const notcurses* nc){
// turn on any specified stylebits // turn on any specified stylebits
void ncplane_styles_on(ncplane* n, unsigned stylebits){ void ncplane_styles_on(ncplane* n, unsigned stylebits){
ncplane_lock(n);
n->attrword |= (stylebits & NCSTYLE_MASK); n->attrword |= (stylebits & NCSTYLE_MASK);
ncplane_unlock(n);
} }
// turn off any specified stylebits // turn off any specified stylebits
void ncplane_styles_off(ncplane* n, unsigned stylebits){ void ncplane_styles_off(ncplane* n, unsigned stylebits){
ncplane_lock(n);
n->attrword &= ~(stylebits & NCSTYLE_MASK); n->attrword &= ~(stylebits & NCSTYLE_MASK);
ncplane_unlock(n);
} }
// set the current stylebits to exactly those provided // set the current stylebits to exactly those provided
void ncplane_styles_set(ncplane* n, unsigned stylebits){ void ncplane_styles_set(ncplane* n, unsigned stylebits){
ncplane_lock(n);
n->attrword = (n->attrword & ~NCSTYLE_MASK) | ((stylebits & NCSTYLE_MASK)); n->attrword = (n->attrword & ~NCSTYLE_MASK) | ((stylebits & NCSTYLE_MASK));
ncplane_unlock(n);
} }
unsigned ncplane_styles(ncplane* n){ unsigned ncplane_styles(const ncplane* n){
unsigned ret; return (n->attrword & NCSTYLE_MASK);
ncplane_lock(n);
ret = (n->attrword & NCSTYLE_MASK);
ncplane_unlock(n);
return ret;
} }
// i hate the big allocation and two copies here, but eh what you gonna do? // i hate the big allocation and two copies here, but eh what you gonna do?
@ -1807,30 +1710,24 @@ int ncplane_box(ncplane* n, const cell* ul, const cell* ur,
} }
int ncplane_move_yx(ncplane* n, int y, int x){ int ncplane_move_yx(ncplane* n, int y, int x){
ncplane_lock(n);
if(n == n->nc->stdscr){ if(n == n->nc->stdscr){
ncplane_unlock(n);
return -1; return -1;
} }
n->absy = y; n->absy = y;
n->absx = x; n->absx = x;
ncplane_unlock(n);
return 0; return 0;
} }
void ncplane_yx(const ncplane* n, int* y, int* x){ void ncplane_yx(const ncplane* n, int* y, int* x){
ncplane_lock(n);
if(y){ if(y){
*y = n->absy; *y = n->absy;
} }
if(x){ if(x){
*x = n->absx; *x = n->absx;
} }
ncplane_unlock(n);
} }
void ncplane_erase(ncplane* n){ void ncplane_erase(ncplane* n){
ncplane_lock(n);
// we must preserve the background, but a pure cell_duplicate() would be // we must preserve the background, but a pure cell_duplicate() would be
// wiped out by the egcpool_dump(). do a duplication (to get the attrword // wiped out by the egcpool_dump(). do a duplication (to get the attrword
// and channels), and then reload. // and channels), and then reload.
@ -1840,7 +1737,6 @@ void ncplane_erase(ncplane* n){
egcpool_init(&n->pool); egcpool_init(&n->pool);
cell_load(n, &n->basecell, egc); cell_load(n, &n->basecell, egc);
free(egc); free(egc);
ncplane_unlock(n);
} }
void notcurses_cursor_enable(notcurses* nc){ void notcurses_cursor_enable(notcurses* nc){
@ -1891,11 +1787,9 @@ bool notcurses_canchangecolor(const notcurses* nc){
palette256* palette256_new(notcurses* nc){ palette256* palette256_new(notcurses* nc){
palette256* p = malloc(sizeof(*p)); palette256* p = malloc(sizeof(*p));
pthread_mutex_lock(&nc->lock);
if(p){ if(p){
memcpy(p, &nc->palette, sizeof(*p)); memcpy(p, &nc->palette, sizeof(*p));
} }
pthread_mutex_unlock(&nc->lock);
return p; return p;
} }
@ -1904,7 +1798,6 @@ int palette256_use(notcurses* nc, const palette256* p){
if(!nc->CCCflag){ if(!nc->CCCflag){
return -1; return -1;
} }
pthread_mutex_lock(&nc->lock);
for(size_t z = 0 ; z < sizeof(p->chans) / sizeof(*p->chans) ; ++z){ for(size_t z = 0 ; z < sizeof(p->chans) / sizeof(*p->chans) ; ++z){
if(nc->palette.chans[z] != p->chans[z]){ if(nc->palette.chans[z] != p->chans[z]){
nc->palette.chans[z] = p->chans[z]; nc->palette.chans[z] = p->chans[z];
@ -1912,7 +1805,6 @@ int palette256_use(notcurses* nc, const palette256* p){
} }
} }
ret = 0; ret = 0;
pthread_mutex_unlock(&nc->lock);
return ret; return ret;
} }
@ -1940,7 +1832,6 @@ rgb_greyscale(int r, int g, int b){
} }
void ncplane_greyscale(ncplane *n){ void ncplane_greyscale(ncplane *n){
ncplane_lock(n);
for(int y = 0 ; y < n->leny ; ++y){ for(int y = 0 ; y < n->leny ; ++y){
for(int x = 0 ; x < n->lenx ; ++x){ for(int x = 0 ; x < n->lenx ; ++x){
cell* c = &n->fb[nfbcellidx(n, y, x)]; cell* c = &n->fb[nfbcellidx(n, y, x)];
@ -1953,7 +1844,6 @@ void ncplane_greyscale(ncplane *n){
cell_set_bg_rgb(c, gy, gy, gy); cell_set_bg_rgb(c, gy, gy, gy);
} }
} }
ncplane_unlock(n);
} }
// if this is not polyfillable cell, we return 0. if it is, we attempt to fill // if this is not polyfillable cell, we return 0. if it is, we attempt to fill
@ -1998,13 +1888,11 @@ ncplane_polyfill_locked(ncplane* n, int y, int x, const cell* c){
// at the initial step only, invalid y, x is an error, so explicitly check. // at the initial step only, invalid y, x is an error, so explicitly check.
int ncplane_polyfill_yx(ncplane* n, int y, int x, const cell* c){ int ncplane_polyfill_yx(ncplane* n, int y, int x, const cell* c){
int ret = -1; int ret = -1;
ncplane_lock(n);
if(y < n->leny && x < n->lenx){ if(y < n->leny && x < n->lenx){
if(y >= 0 && x >= 0){ if(y >= 0 && x >= 0){
ret = ncplane_polyfill_locked(n, y, x, c); ret = ncplane_polyfill_locked(n, y, x, c);
} }
} }
ncplane_unlock(n);
return ret; return ret;
} }

View File

@ -4,11 +4,6 @@
#include <sys/poll.h> #include <sys/poll.h>
#include "internal.h" #include "internal.h"
static void
mutex_unlock(void* vlock){
pthread_mutex_unlock(vlock);
}
static int static int
blocking_write(int fd, const char* buf, size_t buflen){ blocking_write(int fd, const char* buf, size_t buflen){
//fprintf(stderr, "writing %zu to %d...\n", buflen, fd); //fprintf(stderr, "writing %zu to %d...\n", buflen, fd);
@ -36,8 +31,6 @@ blocking_write(int fd, const char* buf, size_t buflen){
int notcurses_refresh(notcurses* nc){ int notcurses_refresh(notcurses* nc){
int ret; int ret;
pthread_mutex_lock(&nc->lock);
pthread_cleanup_push(mutex_unlock, &nc->lock);
if(nc->rstate.mstream == NULL){ if(nc->rstate.mstream == NULL){
ret = -1; // haven't rendered yet, and thus don't know what should be there ret = -1; // haven't rendered yet, and thus don't know what should be there
}else if(blocking_write(nc->ttyfd, nc->rstate.mstream, nc->rstate.mstrsize)){ }else if(blocking_write(nc->ttyfd, nc->rstate.mstream, nc->rstate.mstrsize)){
@ -45,7 +38,6 @@ int notcurses_refresh(notcurses* nc){
}else{ }else{
ret = 0; ret = 0;
} }
pthread_cleanup_pop(1);
return ret; return ret;
} }
@ -962,8 +954,6 @@ int notcurses_render(notcurses* nc){
struct timespec start, done; struct timespec start, done;
int ret; int ret;
clock_gettime(CLOCK_MONOTONIC_RAW, &start); clock_gettime(CLOCK_MONOTONIC_RAW, &start);
pthread_mutex_lock(&nc->lock);
pthread_cleanup_push(mutex_unlock, &nc->lock);
int bytes = -1; int bytes = -1;
size_t crenderlen = sizeof(struct crender) * nc->stdscr->leny * nc->stdscr->lenx; size_t crenderlen = sizeof(struct crender) * nc->stdscr->leny * nc->stdscr->lenx;
struct crender* crender = malloc(crenderlen); struct crender* crender = malloc(crenderlen);
@ -977,13 +967,11 @@ int notcurses_render(notcurses* nc){
clock_gettime(CLOCK_MONOTONIC_RAW, &done); clock_gettime(CLOCK_MONOTONIC_RAW, &done);
update_render_stats(&done, &start, &nc->stats, bytes); update_render_stats(&done, &start, &nc->stats, bytes);
ret = bytes >= 0 ? 0 : -1; ret = bytes >= 0 ? 0 : -1;
pthread_cleanup_pop(1);
return ret; return ret;
} }
char* notcurses_at_yx(notcurses* nc, int y, int x, cell* c){ char* notcurses_at_yx(notcurses* nc, int y, int x, cell* c){
char* egc = NULL; char* egc = NULL;
pthread_mutex_lock(&nc->lock);
if(nc->lastframe){ if(nc->lastframe){
if(y >= 0 && y < nc->lfdimy){ if(y >= 0 && y < nc->lfdimy){
if(x >= 0 || x < nc->lfdimx){ if(x >= 0 || x < nc->lfdimx){
@ -995,6 +983,5 @@ char* notcurses_at_yx(notcurses* nc, int y, int x, cell* c){
} }
} }
} }
pthread_mutex_unlock(&nc->lock);
return egc; return egc;
} }