/* coroutine_chan.h - Simple channel system for coroutine.{h,c} * * Copyright (C) 2024 Luke T. Shumaker * SPDX-Licence-Identifier: AGPL-3.0-or-later */ /** * The cr_chan_* macros form a simple Go-like unbuffered channel * mechanism built on top of the cr_pause_and_yeild() and cr_unpause() * primitives. */ #ifndef _COROUTINE_CHAN_H_ #define _COROUTINE_CHAN_H_ #include #include "coroutine.h" /** * cr_chan_t(val_t) returns the type definition for a channel on * that transports values of type `val_t`. */ #define cr_chan_t(val_t) struct { \ bool ok_to_write; \ cid_t reader, *tail_reader; \ val_t *reader_val; \ \ bool ok_to_read; \ cid_t writer, *tail_writer; \ val_t *writer_val; \ } /* These "functions" are preprocessor macros instead of real C * functions so that the compiler can do type-checking instead of * having these functions take `void*`. */ /** * ch_chan_send(*ch, val) sends `val` over `ch`. */ #define cr_chan_send(ch, _val) do { \ if ((ch)->ok_to_write) { /* non-blocking */ \ *((ch)->reader_val) = (_val); \ (ch)->ok_to_write = false; \ cr_unpause((ch)->reader); \ } else { /* blocking */ \ cid_t next; \ if ((ch)->writer) { \ *((ch)->tail_writer) = cr_getcid(); \ (ch)->tail_writer = &next; \ cr_pause_and_yield(); \ } else { \ (ch)->writer = cr_getcid(); \ (ch)->tail_writer = &next; \ } \ assert((ch)->writer == cr_getcid()); \ (ch)->writer_val = &(_val); \ (ch)->ok_to_read = true; \ cr_pause_and_yield(); \ assert((ch)->ok_to_read == false); \ if (next) { \ (ch)->writer = next; \ cr_unpause(next); \ } else { \ (ch)->writer = 0; \ (ch)->tail_writer = NULL; \ } \ } \ } while (0) /** * cr_chan_recv(ch, val_p) reads a value from ch into `*val_p`. */ #define cr_chan_recv(ch, _val_p) do { \ if ((ch)->ok_to_read) { /* non-blocking */ \ *(_val_p) = *((ch)->writer_val); \ (ch)->ok_to_read = false; \ cr_unpause((ch)->writer; \ } else { /* blocking */ \ cid_t next; \ if ((ch)->reader) { \ *((ch)->tail_reader) = cr_getcid(); \ (ch)->tail_reader = &next; \ cr_pause_and_yield(); \ } else { \ (ch)->reader = cr_getcid(); \ (ch)->tail_reader = &next; \ } \ assert((ch)->reader == cr_getcid()); \ (ch)->reader_val = (_val_p); \ (ch)->ok_to_write = true; \ cr_pause_and_yield(); \ assert((ch)->ok_to_write == false); \ if (next) { \ (ch)->reader = next; \ cr_unpause(next); \ } else { \ (ch)->reader = 0; \ (ch)->tail_reader = NULL; \ } \ } \ } while (0) /** * cr_chan_can_send(ch) returns whether cr_chan_send(ch, val) would * run without blocking. */ #define cr_chan_can_send(ch) ((ch)->ok_to_write) /** * cr_chan_can_recv(ch) returns whether cr_chan_recv(ch, &val) would * run without blocking. */ #define cr_chan_can_recv(ch) ((ch)->ok_to_read) #endif /* _COROUTINE_CHAN_H_ */