From 6dd488b87fdc47fb377ba648a6cd598bdab87f59 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Sun, 8 Mar 2015 19:07:48 +0100 Subject: Implement select ; add two tests for channels. --- src/common/include/fs.h | 13 ++++ src/common/include/proc.h | 2 + src/common/include/syscallproto.h | 4 +- src/kernel/core/idt.c | 15 +++-- src/kernel/core/thread.c | 86 +++++++++++++++++--------- src/kernel/dev/pciide.c | 1 + src/kernel/fs/iso9660.c | 2 + src/kernel/include/thread.h | 4 +- src/kernel/include/vfs.h | 2 + src/kernel/user/ipc.c | 22 ++++++- src/kernel/user/nullfs.c | 2 + src/kernel/user/syscall.c | 54 ++++++++++++++++ src/kernel/user/vfs.c | 9 +++ src/lib/include/syscall.h | 3 +- src/lib/libkogata/syscall.c | 8 ++- src/tests/utests/chan1/Makefile | 1 + src/tests/utests/chan1/test.c | 30 +++++++++ src/tests/utests/chan2/Makefile | 1 + src/tests/utests/chan2/test.c | 126 ++++++++++++++++++++++++++++++++++++++ 19 files changed, 344 insertions(+), 41 deletions(-) create mode 120000 src/tests/utests/chan1/Makefile create mode 100644 src/tests/utests/chan1/test.c create mode 120000 src/tests/utests/chan2/Makefile create mode 100644 src/tests/utests/chan2/test.c (limited to 'src') diff --git a/src/common/include/fs.h b/src/common/include/fs.h index 431c16f..5a2fc84 100644 --- a/src/common/include/fs.h +++ b/src/common/include/fs.h @@ -3,6 +3,8 @@ #include #include +typedef int fd_t; + #define FT_REGULAR 0 // no flags = regular file #define FT_DIR (0x01) #define FT_DEV (0x02) @@ -28,6 +30,7 @@ #define FM_ALL_MODES (0xFFFF) + typedef struct { int type; int access; @@ -46,4 +49,14 @@ typedef struct { #define IOCTL_BLOCKDEV_GET_BLOCK_SIZE 40 #define IOCTL_BLOCKDEV_GET_BLOCK_COUNT 41 + +#define SEL_READ 0x01 +#define SEL_WRITE 0x02 +#define SEL_ERROR 0x04 + +typedef struct { + fd_t fd; + uint16_t req_flags, got_flags; // rq_flags : what caller is interested in +} sel_fd_t; + /* vim: set ts=4 sw=4 tw=0 noet :*/ diff --git a/src/common/include/proc.h b/src/common/include/proc.h index 831e5c6..29b5b91 100644 --- a/src/common/include/proc.h +++ b/src/common/include/proc.h @@ -3,6 +3,8 @@ #include #include +typedef int pid_t; + #define PS_LOADING 1 #define PS_RUNNING 2 #define PS_FINISHED 3 diff --git a/src/common/include/syscallproto.h b/src/common/include/syscallproto.h index bbb49b3..9f300d3 100644 --- a/src/common/include/syscallproto.h +++ b/src/common/include/syscallproto.h @@ -1,9 +1,8 @@ #pragma once #include +#include -typedef int fd_t; -typedef int pid_t; typedef struct { fd_t a, b; } fd_pair_t; #define SC_MAX 128 // maximum number of syscalls @@ -33,6 +32,7 @@ typedef struct { fd_t a, b; } fd_pair_t; #define SC_STAT_OPEN 35 // args: fd, out stat_t *data -- stat on open file handle #define SC_IOCTL 36 // args: fd, command, out void* data #define SC_GET_MODE 37 // args: fd -- get mode for open file handle +#define SC_SELECT 38 // args: sel_fd_t*, count, timeout #define SC_MK_CHANNEL 40 // args: blocking?, (int, int)* #define SC_GEN_TOKEN 41 // args: fd, token_t* diff --git a/src/kernel/core/idt.c b/src/kernel/core/idt.c index fcf8074..6bfe8b5 100644 --- a/src/kernel/core/idt.c +++ b/src/kernel/core/idt.c @@ -119,12 +119,17 @@ void idt_irq_handler(registers_t *regs) { } outb(0x20, 0x20); - if (regs->err_code != 0) dbg_printf("IRQ%d\n", regs->err_code); - if (irq_handlers[regs->err_code] != 0) { - irq_handlers[regs->err_code](regs); - } + if (regs->err_code == 0) { + irq0_handler(regs, st); + } else { + dbg_printf("IRQ%d\n", regs->err_code); - exit_critical(st); + if (irq_handlers[regs->err_code] != 0) { + irq_handlers[regs->err_code](regs); + } + + exit_critical(st); + } // maybe exit if (current_thread != 0 && regs->eip < K_HIGHHALF_ADDR && current_thread->must_exit) { diff --git a/src/kernel/core/thread.c b/src/kernel/core/thread.c index d59b71d..58c92d3 100644 --- a/src/kernel/core/thread.c +++ b/src/kernel/core/thread.c @@ -147,7 +147,8 @@ static void run_thread(void (*entry)(void*), void* data) { switch_pagedir(get_kernel_pagedir()); - asm volatile("sti"); + exit_critical(CL_USER); + entry(data); exit(); @@ -192,11 +193,10 @@ thread_t *new_thread(entry_t entry, void* data) { t->state = T_STATE_LOADING; t->last_ran = 0; - t->waiting_on = 0; t->must_exit = false; t->current_pd_d = get_kernel_pagedir(); - t->critical_level = CL_USER; + t->critical_level = CL_EXCL; // used by user processes t->proc = 0; @@ -220,8 +220,11 @@ static void delete_thread(thread_t *t) { // SETUP CODE // // ========== // -static void irq0_handler(registers_t *regs) { +void irq0_handler(registers_t *regs, int crit_level) { notify_time_pass(1000000 / TASK_SWITCH_FREQUENCY); + + exit_critical(crit_level); + if (current_thread != 0 && current_thread->critical_level == CL_USER) { save_context_and_enter_scheduler(¤t_thread->ctx); } @@ -231,7 +234,7 @@ void threading_setup(entry_t cont, void* arg) { ASSERT(waiters != 0); set_pit_frequency(TASK_SWITCH_FREQUENCY); - idt_set_irq_handler(IRQ0, irq0_handler); + // no need to set irq0 handler thread_t *t = new_thread(cont, arg); ASSERT(t != 0); @@ -267,34 +270,59 @@ void yield() { } bool wait_on(void* x) { + return wait_on_many(&x, 1); +} + +bool wait_on_many(void** x, size_t n) { ASSERT(current_thread != 0 && current_thread->critical_level != CL_EXCL); + ASSERT(n > 0); mutex_lock(&waiters_mutex); - void* prev_th = hashtbl_find(waiters, x); - if (prev_th == 0) { - bool add_ok = hashtbl_add(waiters, x, (void*)1); - if (!add_ok) return false; // should not happen to often, I hope - } else if (prev_th != (void*)1) { + // ---- Check we can wait on all the requested objects + bool ok = true; + for (size_t i = 0; ok && i < n; i++) { + void* prev_th = hashtbl_find(waiters, x[i]); + if (prev_th == 0) { + bool add_ok = hashtbl_add(waiters, x[i], (void*)1); + if (!add_ok) { + ok = false; + } + } else if (prev_th != (void*)1) { + ok = false; + } + } + if (!ok) { mutex_unlock(&waiters_mutex); return false; } + // ---- Set ourselves as the waiting thread for all the requested objets int st = enter_critical(CL_NOSWITCH); - if (current_thread->must_exit) return false; - - current_thread->waiting_on = x; + for (size_t i = 0; i < n; i++) { + ASSERT(hashtbl_change(waiters, x[i], current_thread)); + } - ASSERT(hashtbl_change(waiters, x, current_thread)); + // ---- Go to sleep mutex_unlock(&waiters_mutex); current_thread->state = T_STATE_PAUSED; save_context_and_enter_scheduler(¤t_thread->ctx); + // ---- Remove ourselves from the list + mutex_lock(&waiters_mutex); + + for (size_t i = 0; i < n; i++) { + ASSERT(hashtbl_change(waiters, x[i], (void*)1)); + } + + mutex_unlock(&waiters_mutex); exit_critical(st); + // ---- Check that we weren't waked up because of a kill request if (current_thread->must_exit) return false; + return true; } @@ -333,26 +361,27 @@ void exit() { bool resume_on(void* x) { thread_t *thread; - { mutex_lock(&waiters_mutex); + bool ret = false; - thread = hashtbl_find(waiters, x); - hashtbl_change(waiters, x, (void*)1); + mutex_lock(&waiters_mutex); + int st = enter_critical(CL_NOINT); - mutex_unlock(&waiters_mutex); } - - if (thread == 0 || thread == (void*)1) return false; + thread = hashtbl_find(waiters, x); - { int st = enter_critical(CL_NOINT); + if (thread != 0 && thread != (void*)1) { + if (thread->state == T_STATE_PAUSED) { + thread->state = T_STATE_RUNNING; - ASSERT(thread->state == T_STATE_PAUSED); - thread->state = T_STATE_RUNNING; - thread->waiting_on = 0; + enqueue_thread(thread, false); - enqueue_thread(thread, false); + ret = true; + } + } - exit_critical(st); } + mutex_unlock(&waiters_mutex); + exit_critical(st); - return true; + return ret; } void kill_thread(thread_t *thread) { @@ -365,7 +394,8 @@ void kill_thread(thread_t *thread) { int i = 0; while (thread->state != T_STATE_FINISHED) { if (thread->state == T_STATE_PAUSED) { - resume_on(thread->waiting_on); + thread->state = T_STATE_RUNNING; + enqueue_thread(thread, false); } yield(); if (i++ > 100) dbg_printf("Thread 0x%p must be killed but will not exit.\n", thread); diff --git a/src/kernel/dev/pciide.c b/src/kernel/dev/pciide.c index 56d4e4f..8fcaf02 100644 --- a/src/kernel/dev/pciide.c +++ b/src/kernel/dev/pciide.c @@ -677,6 +677,7 @@ static fs_node_ops_t ide_vfs_node_ops = { .ioctl = ide_vfs_ioctl, .close = ide_vfs_close, .readdir = 0, + .poll = 0, }; void ide_register_device(ide_controller_t *c, uint8_t device, fs_t *iofs) { diff --git a/src/kernel/fs/iso9660.c b/src/kernel/fs/iso9660.c index 89bfdf8..a3db67e 100644 --- a/src/kernel/fs/iso9660.c +++ b/src/kernel/fs/iso9660.c @@ -39,6 +39,7 @@ static fs_node_ops_t iso9660_dir_ops = { .read = 0, .write = 0, .ioctl = 0, + .poll = 0, }; static fs_node_ops_t iso9660_file_ops = { @@ -54,6 +55,7 @@ static fs_node_ops_t iso9660_file_ops = { .read = iso9660_file_read, .write = 0, .ioctl = 0, + .poll = 0, }; void register_iso9660_driver() { diff --git a/src/kernel/include/thread.h b/src/kernel/include/thread.h index 09995fd..f9df0ea 100644 --- a/src/kernel/include/thread.h +++ b/src/kernel/include/thread.h @@ -36,7 +36,6 @@ typedef struct thread { struct thread *next_in_queue; struct thread *next_in_proc; - void* waiting_on; bool must_exit; } thread_t; @@ -46,6 +45,8 @@ void threading_setup(entry_t cont, void* data); // never returns thread_t *new_thread(entry_t entry, void* data); // thread is PAUSED, and must be started with start_thread void start_thread(thread_t *t); +void irq0_handler(registers_t *regs, int crit_level); + extern thread_t *current_thread; void yield(); @@ -60,6 +61,7 @@ void usleep(int usecs); // killed and must terminate its kernel-land processing as soon as possible. bool wait_on(void* x); // true : resumed normally, false : resumed because thread was killed, or someone else already waiting +bool wait_on_many(void** x, size_t count); // true only if we could wait on ALL objects bool resume_on(void* x); void kill_thread(thread_t *thread); // cannot be called for current thread diff --git a/src/kernel/include/vfs.h b/src/kernel/include/vfs.h index 371ffb7..a56ecae 100644 --- a/src/kernel/include/vfs.h +++ b/src/kernel/include/vfs.h @@ -86,6 +86,7 @@ typedef struct fs_node_ops { size_t (*read)(fs_handle_t *f, size_t offset, size_t len, char* buf); size_t (*write)(fs_handle_t *f, size_t offset, size_t len, const char* buf); bool (*readdir)(fs_handle_t *f, size_t ent_no, dirent_t *d); + int (*poll)(fs_handle_t *f, void** out_wait_obj); void (*close)(fs_handle_t *f); bool (*stat)(fs_node_ptr n, stat_t *st); @@ -181,5 +182,6 @@ size_t file_read(fs_handle_t *f, size_t offset, size_t len, char* buf); size_t file_write(fs_handle_t *f, size_t offset, size_t len, const char* buf); int file_ioctl(fs_handle_t *f, int command, void* data); bool file_readdir(fs_handle_t *f, size_t ent_no, dirent_t *d); +int file_poll(fs_handle_t *f, void** out_wait_obj); // just polls the file & returns a mask of SEL_* (see ) /* vim: set ts=4 sw=4 tw=0 noet :*/ diff --git a/src/kernel/user/ipc.c b/src/kernel/user/ipc.c index 648fdf2..dce1847 100644 --- a/src/kernel/user/ipc.c +++ b/src/kernel/user/ipc.c @@ -11,6 +11,7 @@ static size_t channel_read(fs_handle_t *c, size_t offset, size_t len, char* buf); static size_t channel_write(fs_handle_t *c, size_t offset, size_t len, const char* buf); +static int channel_poll(fs_handle_t *c, void** out_wait_obj); static bool channel_stat(fs_node_ptr c, stat_t *st); static void channel_close(fs_handle_t *c); @@ -18,6 +19,7 @@ static fs_node_ops_t channel_ops = { .read = channel_read, .write = channel_write, .close = channel_close, + .poll = channel_poll, .open = 0, .readdir = 0, .ioctl = 0, @@ -157,6 +159,20 @@ size_t channel_write(fs_handle_t *h, size_t offset, size_t req_len, const char* return ret; } +int channel_poll(fs_handle_t *h, void** out_wait_obj) { + channel_t *c = (channel_t*)h->data; + + int ret = 0; + + if (c->other_side == 0) ret |= SEL_ERROR; + if (c->other_side && c->other_side->buf_used < CHANNEL_BUFFER_SIZE) ret |= SEL_WRITE; + if (c->buf_used > 0) ret |= SEL_READ; + + if (out_wait_obj) *out_wait_obj = c; + + return ret; +} + bool channel_stat(fs_node_ptr ch, stat_t *st) { channel_t *c = (channel_t*)ch; @@ -176,7 +192,11 @@ void channel_close(fs_handle_t *ch) { mutex_lock(&c->lock); - c->other_side->other_side = 0; + if (c->other_side) { + resume_on(c->other_side); + c->other_side->other_side = 0; + } + free(c); } diff --git a/src/kernel/user/nullfs.c b/src/kernel/user/nullfs.c index 60872ab..0dc1b22 100644 --- a/src/kernel/user/nullfs.c +++ b/src/kernel/user/nullfs.c @@ -53,6 +53,7 @@ static fs_node_ops_t nullfs_d_ops = { .read = 0, .write = 0, .ioctl = 0, + .poll = 0, }; static fs_node_ops_t nullfs_f_ops = { @@ -68,6 +69,7 @@ static fs_node_ops_t nullfs_f_ops = { .close = nullfs_f_close, .readdir = 0, .ioctl =0, + .poll = 0, }; diff --git a/src/kernel/user/syscall.c b/src/kernel/user/syscall.c index c352ff4..67ed317 100644 --- a/src/kernel/user/syscall.c +++ b/src/kernel/user/syscall.c @@ -6,6 +6,7 @@ #include #include +#include typedef struct { uint32_t sc_id, a, b, c, d, e; // a: ebx, b: ecx, c: edx, d: esi, e: edi @@ -282,6 +283,58 @@ static uint32_t get_mode_sc(sc_args_t args) { return file_get_mode(h); } +static uint32_t select_sc(sc_args_t args) { + sel_fd_t *fds = (sel_fd_t*)args.a; + size_t n = args.b; + int timeout = args.c; + + probe_for_write(fds, n * sizeof(sel_fd_t)); + + uint64_t select_begin_time = get_kernel_time(); + + void** wait_objs = (void**)malloc((n+1) * sizeof(void*)); + if (!wait_objs) return false; + + bool ret = false; + + int st = enter_critical(CL_NOSWITCH); + + while (true) { + // ---- Poll FDs, if any is ok then return it + size_t n_wait_objs = 0; + if (timeout > 0) wait_objs[n_wait_objs++] = current_thread; + for (size_t i = 0; i < n; i++) { + fs_handle_t *h = proc_read_fd(current_process(), fds[i].fd); + if (h) { + fds[i].got_flags = file_poll(h, &wait_objs[n_wait_objs]); + if (wait_objs[n_wait_objs]) n_wait_objs++; + if (fds[i].got_flags & fds[i].req_flags) ret = true; + } + } + + uint64_t time = get_kernel_time(); + + // ---- If none of the handles given is a valid handle, return false + if (n_wait_objs == 0) break; + // ---- If any is ok, return true + if (ret) break; + // ---- If the timeout is over, return false + if (timeout >= 0 && time - select_begin_time >= (uint64_t)timeout) break; + + // ---- Do a wait, if interrupted (killed or whatever) return false + void resume_on_v(void*x) { + resume_on(x); + } + if (timeout > 0) worker_push_in(time - select_begin_time - timeout, resume_on_v, current_thread); + if (!wait_on_many(wait_objs, n_wait_objs)) break; + } + + exit_critical(st); + + free(wait_objs); + return ret; +} + // ---- IPC static uint32_t make_channel_sc(sc_args_t args) { @@ -642,6 +695,7 @@ void setup_syscall_table() { sc_handlers[SC_STAT_OPEN] = stat_open_sc; sc_handlers[SC_IOCTL] = ioctl_sc; sc_handlers[SC_GET_MODE] = get_mode_sc; + sc_handlers[SC_SELECT] = select_sc; sc_handlers[SC_MK_CHANNEL] = make_channel_sc; sc_handlers[SC_GEN_TOKEN] = gen_token_sc; diff --git a/src/kernel/user/vfs.c b/src/kernel/user/vfs.c index 4e8cf53..05b4a2c 100644 --- a/src/kernel/user/vfs.c +++ b/src/kernel/user/vfs.c @@ -494,4 +494,13 @@ bool file_readdir(fs_handle_t *f, size_t ent_no, dirent_t *d) { return f->ops->readdir && f->ops->readdir(f, ent_no, d); } +int file_poll(fs_handle_t *f, void** out_wait_obj) { + if (!f->ops->poll) { + if (out_wait_obj) *out_wait_obj = 0; + return 0; + } + + return f->ops->poll(f, out_wait_obj); +} + /* vim: set ts=4 sw=4 tw=0 noet :*/ diff --git a/src/lib/include/syscall.h b/src/lib/include/syscall.h index 7579be3..79e930f 100644 --- a/src/lib/include/syscall.h +++ b/src/lib/include/syscall.h @@ -17,7 +17,7 @@ void dbg_print(const char* str); void yield(); void exit(int code); void usleep(int usecs); -bool new_thread(entry_t entry, void* data); +bool sys_new_thread(void* eip, void* esp); void exit_thread(); bool mmap(void* addr, size_t size, int mode); @@ -38,6 +38,7 @@ bool readdir(fd_t file, size_t ent_no, dirent_t *d); bool stat_open(fd_t file, stat_t *s); int ioctl(fd_t file, int command, void* data); int get_mode(fd_t file); +bool select(sel_fd_t* fds, size_t nfds, int timeout); fd_pair_t make_channel(bool blocking); bool gen_token(fd_t file, token_t *tok); diff --git a/src/lib/libkogata/syscall.c b/src/lib/libkogata/syscall.c index 7ae0283..b724e69 100644 --- a/src/lib/libkogata/syscall.c +++ b/src/lib/libkogata/syscall.c @@ -39,9 +39,8 @@ void usleep(int usecs) { call(SC_USLEEP, usecs, 0, 0, 0, 0); } -bool new_thread(entry_t entry, void* data) { - // TODO - return false; +bool sys_new_thread(void* eip, void* esp) { + return call(SC_NEW_THREAD, (uint32_t)eip, (uint32_t)esp, 0, 0, 0); } void exit_thread() { @@ -98,6 +97,9 @@ int ioctl(fd_t file, int command, void* data) { int get_mode(fd_t file) { return call(SC_GET_MODE, file, 0, 0, 0, 0); } +bool select(sel_fd_t* fds, size_t nfds, int timeout) { + return call(SC_SELECT, (uint32_t)fds, nfds, timeout, 0, 0); +} fd_pair_t make_channel(bool blocking) { fd_pair_t ret; diff --git a/src/tests/utests/chan1/Makefile b/src/tests/utests/chan1/Makefile new file mode 120000 index 0000000..4630a7c --- /dev/null +++ b/src/tests/utests/chan1/Makefile @@ -0,0 +1 @@ +../rules.make \ No newline at end of file diff --git a/src/tests/utests/chan1/test.c b/src/tests/utests/chan1/test.c new file mode 100644 index 0000000..1b89255 --- /dev/null +++ b/src/tests/utests/chan1/test.c @@ -0,0 +1,30 @@ +#include + +#include + +#include +#include + +int main(int argc, char **argv) { + + fd_pair_t ch = make_channel(false); + + char* s = "Hello, world!"; + ASSERT(write(ch.a, 0, strlen(s), s) == strlen(s)); + char buf[128]; + ASSERT(read(ch.b, 0, 128, buf) == strlen(s)); + ASSERT(strncmp(s, buf, strlen(s)) == 0); + + close(ch.a); + sel_fd_t sel = { .fd = ch.b, .req_flags = SEL_ERROR }; + ASSERT(select(&sel, 1, 0)); + ASSERT(sel.got_flags & SEL_ERROR); + + close(ch.b); + + dbg_printf("(TEST-OK)\n"); + + return 0; +} + +/* vim: set ts=4 sw=4 tw=0 noet :*/ diff --git a/src/tests/utests/chan2/Makefile b/src/tests/utests/chan2/Makefile new file mode 120000 index 0000000..4630a7c --- /dev/null +++ b/src/tests/utests/chan2/Makefile @@ -0,0 +1 @@ +../rules.make \ No newline at end of file diff --git a/src/tests/utests/chan2/test.c b/src/tests/utests/chan2/test.c new file mode 100644 index 0000000..a2f8b3f --- /dev/null +++ b/src/tests/utests/chan2/test.c @@ -0,0 +1,126 @@ +#include + +#include + +#include +#include + +volatile int step = 0; // volatile because thread-shared + +const char* msg_a = "ping"; +const char* msg_b = "potato salad"; +const char* msg_c = "what?"; +const char* msg_d = "hello world"; + +void listen_thread(fd_t c1, fd_t c2) { + dbg_printf("listen_thread started\n"); + + sel_fd_t sel[2]; + sel[0].fd = c1; + sel[1].fd = c2; + sel[0].req_flags = sel[1].req_flags = SEL_READ | SEL_ERROR; + + char buf[128]; + + dbg_printf("Doing first select...\n"); + ASSERT(select(sel, 2, -1)); + dbg_printf("%x %x\n", sel[0].got_flags, sel[1].got_flags); + ASSERT(sel[0].got_flags & SEL_READ); + ASSERT(!(sel[1].got_flags & SEL_READ)); + ASSERT(read(c1, 0, strlen(msg_a), buf) == strlen(msg_a)); + ASSERT(strncmp(buf, msg_a, strlen(msg_a)) == 0); + ASSERT(write(c1, 0, strlen(msg_c), msg_c) == strlen(msg_c)); + step = 1; + + dbg_printf("Doing second select...\n"); + ASSERT(select(sel, 2, -1)); + dbg_printf("%x %x\n", sel[0].got_flags, sel[1].got_flags); + ASSERT(sel[1].got_flags & SEL_READ); + ASSERT(!(sel[0].got_flags & SEL_READ)); + ASSERT(read(c2, 0, strlen(msg_b), buf) == strlen(msg_b)); + ASSERT(strncmp(buf, msg_b, strlen(msg_b)) == 0); + ASSERT(write(c2, 0, strlen(msg_d), msg_d) == strlen(msg_d)); + step = 2; + + dbg_printf("Doing third select...\n"); + ASSERT(select(sel, 2, -1)); + dbg_printf("%x %x\n", sel[0].got_flags, sel[1].got_flags); + ASSERT(sel[0].got_flags & SEL_ERROR); + ASSERT(!(sel[1].got_flags & SEL_ERROR)); + close(c1); + step = 3; + + dbg_printf("Doing fourth select...\n"); + ASSERT(select(sel + 1, 1, -1)); + dbg_printf("%x\n", sel[1].got_flags); + ASSERT(sel[1].got_flags & SEL_ERROR); + close(c2); + step = 4; + + exit_thread(); +} + +int main(int argc, char **argv) { + + fd_pair_t ch1 = make_channel(false); + ASSERT(ch1.a != 0 && ch1.b != 0); + + fd_pair_t ch2 = make_channel(false); + ASSERT(ch2.a != 0 && ch2.b != 0); + + void* stack = malloc(0x1000); + ASSERT(stack != 0); + + char buf[128]; + + uint32_t *esp = (uint32_t*)(stack + 0x1000); + *(--esp) = ch2.a; + *(--esp) = ch1.a; + *(--esp) = 0; // false return address + dbg_printf("launching waiter thread\n"); + ASSERT(sys_new_thread(listen_thread, esp)); + + fd_t c1 = ch1.b, c2 = ch2.b; + + // -- the test on the first channel + + ASSERT(write(c1, 0, strlen(msg_a), msg_a) == strlen(msg_a)); + + dbg_printf("wait for step1 signal\n"); + while(step != 1); + dbg_printf("got step1 signal, proceeding\n"); + + ASSERT(read(c1, 0, strlen(msg_c), buf) == strlen(msg_c)); + ASSERT(strncmp(msg_c, buf, strlen(msg_c)) == 0); + + // -- the test on the second channel + + ASSERT(write(c2, 0, strlen(msg_b), msg_b) == strlen(msg_b)); + + dbg_printf("wait for step2 signal\n"); + while(step != 2); + dbg_printf("got step2 signal, proceeding\n"); + + ASSERT(read(c2, 0, strlen(msg_d), buf) == strlen(msg_d)); + ASSERT(strncmp(msg_d, buf, strlen(msg_d)) == 0); + + // -- closing stuff + + close(c1); + + dbg_printf("waiting for step3 signal\n"); + while(step != 3); + dbg_printf("got step3 signal, proceeding\n"); + + close(c2); + + dbg_printf("waiting for step4 signal\n"); + while(step != 4); + dbg_printf("got step4 signal, proceeding\n"); + + dbg_printf("(TEST-OK)\n"); + + return 0; +} + +/* vim: set ts=4 sw=4 tw=0 noet :*/ -- cgit v1.2.3