aboutsummaryrefslogtreecommitdiff
path: root/semestr-5/so/lista3/so21_lista_3/coro.c
diff options
context:
space:
mode:
Diffstat (limited to 'semestr-5/so/lista3/so21_lista_3/coro.c')
-rw-r--r--semestr-5/so/lista3/so21_lista_3/coro.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/semestr-5/so/lista3/so21_lista_3/coro.c b/semestr-5/so/lista3/so21_lista_3/coro.c
new file mode 100644
index 0000000..92cc199
--- /dev/null
+++ b/semestr-5/so/lista3/so21_lista_3/coro.c
@@ -0,0 +1,176 @@
+#include "queue.h"
+#include "csapp.h"
+
+#define CORO_STKSIZE 4096
+#define CORO_STKALIGN 16 /* As required by SysV ABI ! */
+
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+#ifndef NOTHING
+#define NOTHING (-2)
+#endif
+
+typedef struct coro {
+ TAILQ_ENTRY(coro) co_link;
+ const char *co_name;
+ void *co_stack;
+ Jmpbuf co_ctx;
+} coro_t;
+
+static TAILQ_HEAD(, coro) runqueue = TAILQ_HEAD_INITIALIZER(runqueue);
+static coro_t *running;
+static Jmpbuf dispatcher;
+
+/* Initialize coroutine stucture with stack. */
+static void coro_init(coro_t *co, const char *name) {
+ memset(co, 0, sizeof(coro_t));
+ co->co_name = name;
+ /* Allocates a fresh stack for the coroutine! */
+ if (posix_memalign(&co->co_stack, CORO_STKALIGN, CORO_STKSIZE) < 0)
+ unix_error("posix_memalign error");
+}
+
+/* Detach a stack from coroutine structure. */
+static void coro_destroy(coro_t *co) {
+ free(co->co_stack);
+}
+
+/*
+ * Switch between subsequent coroutines.
+ *
+ * Dead coroutines, i.e. ones that returned EOF, get removed from the run queue.
+ * Feed next coroutine (value returned from coro_yield) with the result from
+ * previous one (parameter passed to coro_yield).
+ * Return to dispatcher if there're no more coroutines to run.
+ */
+static noreturn void coro_switch(int v) {
+ coro_t *curr = running;
+ /* TODO: Use description above to implement the body. */
+
+ curr = TAILQ_NEXT(curr, co_link);
+ if (v == EOF) {
+ TAILQ_REMOVE(&runqueue, running, co_link);
+ }
+
+ if (TAILQ_EMPTY(&runqueue)) {
+ Longjmp(dispatcher, 1);
+ }
+
+ if (curr) {
+ running = curr;
+ Longjmp(running->co_ctx, v);
+ } else {
+ running = TAILQ_FIRST(&runqueue);
+ Longjmp(running->co_ctx, NOTHING);
+ }
+
+}
+
+/* Save caller context and switch back to next coroutine. */
+static int coro_yield(int v) {
+ int nv = Setjmp(running->co_ctx);
+ if (nv == 0)
+ coro_switch(v);
+ return nv;
+}
+
+/* Configure coroutine context to be executed. */
+static void coro_add(coro_t *co, void (*fn)(int)) {
+ int v = Setjmp(co->co_ctx);
+ if (v) {
+ /* This will get executed when coroutine is entered first time. */
+ fn(v);
+ /* Coroutine must pass EOF to be removed from runqueue! */
+ coro_switch(EOF);
+ }
+ /* Coroutine will be running on its private stack! */
+ co->co_ctx->rsp = co->co_stack + CORO_STKSIZE;
+ TAILQ_INSERT_TAIL(&runqueue, co, co_link);
+}
+
+/* Take first coroutine and feed it with passed value. */
+static int coro_run(int v) {
+ running = TAILQ_FIRST(&runqueue);
+ int nv = Setjmp(dispatcher);
+ if (nv == 0)
+ Longjmp(running->co_ctx, v);
+ return nv;
+}
+
+/*
+ * Actual coroutines that perform some useful work.
+ */
+
+static void func_1(int _) {
+ int words = 0;
+ char prev_ch = ' ';
+ char ch;
+
+ while (Read(0, &ch, 1) > 0) {
+ if (isspace(ch)) {
+ if (isspace(prev_ch))
+ continue;
+ words++;
+ }
+ coro_yield(ch);
+ prev_ch = ch;
+ }
+
+ if (!isspace(ch))
+ words++;
+
+ dprintf(STDERR_FILENO, "\nfunc_1: words = %d\n", words);
+}
+
+static void func_2(int ch) {
+ int removed = 0;
+
+ while (ch != EOF) {
+ if (!isalpha(ch)) {
+ removed++;
+ ch = NOTHING;
+ }
+ ch = coro_yield(ch);
+ }
+
+ dprintf(STDERR_FILENO, "func_2: removed = %d\n", removed);
+}
+
+static void func_3(int ch) {
+ int printed = 0;
+
+ while (ch != EOF) {
+ if (ch != NOTHING) {
+ printed++;
+ if (islower(ch))
+ ch = toupper(ch);
+ else if (isupper(ch))
+ ch = tolower(ch);
+ Write(STDOUT_FILENO, &ch, 1);
+ }
+ ch = coro_yield(NOTHING);
+ }
+
+ dprintf(STDERR_FILENO, "func_3: printed = %d\n", printed);
+}
+
+int main(void) {
+ coro_t co[3];
+
+ coro_init(&co[0], "func_1");
+ coro_init(&co[1], "func_2");
+ coro_init(&co[2], "func_3");
+ coro_add(&co[0], func_1);
+ coro_add(&co[1], func_2);
+ coro_add(&co[2], func_3);
+ coro_run(NOTHING);
+ coro_destroy(&co[0]);
+ coro_destroy(&co[1]);
+ coro_destroy(&co[2]);
+
+ dprintf(STDERR_FILENO, "Bye, bye!\n");
+
+ return EXIT_SUCCESS;
+}