From 615a79cc3cafb8aa90ad2ee9f722d0a4700a7b59 Mon Sep 17 00:00:00 2001 From: lillo Date: Mon, 19 Aug 2002 08:28:39 +0000 Subject: [PATCH] Implemented send_data/receive_data/has_data thread syscalls git-svn-id: file:///srv/svn/repos/haiku/trunk/current@815 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/kernel/OS.h | 4 + headers/os/kernel/thread_types.h | 9 ++ headers/private/kernel/ksyscalls.h | 3 + headers/private/kernel/thread.h | 3 + src/kernel/apps/tests/thread_test.c | 43 ++++++- src/kernel/core/syscalls.c | 9 ++ src/kernel/core/thread.c | 179 ++++++++++++++++++++++++++-- src/kernel/libroot/libroot.c | 6 +- 8 files changed, 240 insertions(+), 16 deletions(-) diff --git a/headers/os/kernel/OS.h b/headers/os/kernel/OS.h index c5eced19a1..08365705f7 100644 --- a/headers/os/kernel/OS.h +++ b/headers/os/kernel/OS.h @@ -352,6 +352,10 @@ int suspend_thread(thread_id thread); thread_id find_thread(const char *); +status_t send_data(thread_id thread, int32 code, const void *buffer, size_t buffer_size); +status_t receive_data(thread_id *sender, void *buffer, size_t buffer_size); +bool has_data(thread_id thread); + status_t snooze(bigtime_t); status_t _get_thread_info(thread_id id, thread_info *info, size_t size); diff --git a/headers/os/kernel/thread_types.h b/headers/os/kernel/thread_types.h index 4079c01553..685d3370f9 100644 --- a/headers/os/kernel/thread_types.h +++ b/headers/os/kernel/thread_types.h @@ -18,6 +18,7 @@ extern "C" { #include #include +#include #include #include #include @@ -108,6 +109,14 @@ struct thread { int sem_deleted_retcode; int sem_errcode; int sem_flags; + struct { + sem_id write_sem; + sem_id read_sem; + thread_id sender; + int32 code; + size_t size; + cbuf *buffer; + } msg; addr fault_handler; addr entry; void *args; diff --git a/headers/private/kernel/ksyscalls.h b/headers/private/kernel/ksyscalls.h index 68af99db4b..f837a6f14f 100755 --- a/headers/private/kernel/ksyscalls.h +++ b/headers/private/kernel/ksyscalls.h @@ -102,6 +102,9 @@ enum { SYSCALL_GET_NEXT_TEAM_INFO, SYSCALL_CREATE_LINK, SYSCALL_REMOVE_DIR, + SYSCALL_SEND_DATA, + SYSCALL_RECEIVE_DATA, + SYSCALL_HAS_DATA, }; int syscall_dispatcher(unsigned long call_num, void *arg_buffer, uint64 *call_ret); diff --git a/headers/private/kernel/thread.h b/headers/private/kernel/thread.h index 00cfe2615e..c1ecd699d2 100755 --- a/headers/private/kernel/thread.h +++ b/headers/private/kernel/thread.h @@ -83,6 +83,9 @@ int user_team_wait_on_team(team_id id, int *uretcode); thread_id user_thread_create_user_thread(addr, team_id, const char*, int, void *); +status_t user_send_data(thread_id thread, int32 code, const void *buffer, size_t buffer_size); +status_t user_receive_data(thread_id *sender, void *buffer, size_t buffer_size); + status_t user_get_thread_info(thread_id id, thread_info *info); status_t user_get_next_thread_info(team_id team, int32 *cookie, thread_info *info); status_t user_get_team_info(team_id id, team_info *info); diff --git a/src/kernel/apps/tests/thread_test.c b/src/kernel/apps/tests/thread_test.c index 467e3d4a62..d760f0e75e 100644 --- a/src/kernel/apps/tests/thread_test.c +++ b/src/kernel/apps/tests/thread_test.c @@ -36,7 +36,7 @@ static int priority_test(void *data) { int i; - for (i=0; i<10; i++) { + for (i=0; i<6; i++) { printf("%s: %d\n", (char *)data, i); // sys_snooze(1000); } @@ -44,12 +44,32 @@ static int priority_test(void *data) } +static int communication_test(void *data) +{ + int times = (int)data; + int i, code; + char buffer[1024]; + thread_id sender; + + for (i=0; i received (%d): \"%s\"\n", code, buffer); + } + return 0; +} + + int main(int argc, char **argv) { thread_id t[THREADS]; sem_id lock; int i; int expected = START_VAL, current_val = START_VAL; + char *comm_test[] = { "This is a test", + "of the send_data", + "and receive_data", + "syscalls implementation", + "for OpenBeOS" }; printf("OpenBeOS Thread Test - Simple\n" "=============================\n"); @@ -75,12 +95,29 @@ int main(int argc, char **argv) delete_sem(lock); - t[0] = spawn_thread(priority_test, "thread0", B_DISPLAY_PRIORITY, "thread0"); - t[1] = spawn_thread(priority_test, "thread1", B_URGENT_DISPLAY_PRIORITY, "thread1"); + t[0] = spawn_thread(priority_test, "thread0", 11, "thread0"); + t[1] = spawn_thread(priority_test, "thread1", 12, "thread1"); + t[2] = spawn_thread(priority_test, "thread2", 13, "thread2"); resume_thread(t[0]); resume_thread(t[1]); + resume_thread(t[2]); + + sys_snooze(100000); + sys_wait_on_thread(t[0], NULL); sys_wait_on_thread(t[1], NULL); + sys_wait_on_thread(t[2], NULL); + + t[0] = spawn_thread(communication_test, "commthread", B_NORMAL_PRIORITY, (void *)5); + resume_thread(t[0]); + + for (i=0; i<5; i++) { + printf("sending"); + send_data(t[0], i, comm_test[i], strlen(comm_test[i]) + 1); + // Give time to the commthread to display info + sys_snooze(10000); + } + sys_wait_on_thread(t[0], NULL); return 0; } diff --git a/src/kernel/core/syscalls.c b/src/kernel/core/syscalls.c index bb66d7e8ed..2b657bde5a 100644 --- a/src/kernel/core/syscalls.c +++ b/src/kernel/core/syscalls.c @@ -219,6 +219,15 @@ int syscall_dispatcher(unsigned long call_num, void *arg_buffer, uint64 *call_re case SYSCALL_RESUME_THREAD: *call_ret = thread_resume_thread((thread_id)arg0); break; + case SYSCALL_SEND_DATA: + *call_ret = user_send_data((thread_id)arg0, (int32)arg1, (const void *)arg2, (size_t)arg3); + break; + case SYSCALL_RECEIVE_DATA: + *call_ret = user_receive_data((thread_id *)arg0, (void *)arg1, (size_t)arg2); + break; + case SYSCALL_HAS_DATA: + *call_ret = has_data((thread_id)arg0); + break; case SYSCALL_KILL_TEAM: *call_ret = team_kill_team((team_id)arg0); break; diff --git a/src/kernel/core/thread.c b/src/kernel/core/thread.c index ea7feb003d..a83cc511dd 100644 --- a/src/kernel/core/thread.c +++ b/src/kernel/core/thread.c @@ -35,6 +35,7 @@ #include #include +#define THREAD_MAX_MESSAGE_SIZE 65536 struct thread_key { thread_id id; @@ -214,6 +215,7 @@ static struct thread *create_thread_struct(const char *name) { struct thread *t; int state; + char temp[64]; state = disable_interrupts(); GRAB_THREAD_LOCK(); @@ -247,21 +249,32 @@ static struct thread *create_thread_struct(const char *name) t->in_kernel = true; t->user_time = 0; t->kernel_time = 0; - t->last_time = 0; - { - char temp[64]; + t->last_time = 0; - sprintf(temp, "thread_0x%x_retcode_sem", t->id); - t->return_code_sem = create_sem(0, temp); - if(t->return_code_sem < 0) - goto err1; - } + sprintf(temp, "thread_0x%x_retcode_sem", t->id); + t->return_code_sem = create_sem(0, temp); + if(t->return_code_sem < 0) + goto err1; + + sprintf(temp, "%s data write sem", t->name); + t->msg.write_sem = create_sem(1, temp); + if (t->msg.write_sem < 0) + goto err2; + + sprintf(temp, "%s data read sem", t->name); + t->msg.read_sem = create_sem(0, temp); + if (t->msg.read_sem < 0) + goto err3; if(arch_thread_init_thread_struct(t) < 0) - goto err2; + goto err4; return t; +err4: + delete_sem_etc(t->msg.read_sem, -1); +err3: + delete_sem_etc(t->msg.write_sem, -1); err2: delete_sem_etc(t->return_code_sem, -1); err1: @@ -272,8 +285,12 @@ err: static void delete_thread_struct(struct thread *t) { - if(t->return_code_sem >= 0) + if (t->return_code_sem >= 0) delete_sem_etc(t->return_code_sem, -1); + if (t->msg.write_sem >= 0) + delete_sem_etc(t->msg.write_sem, -1); + if (t->msg.read_sem >= 0) + delete_sem_etc(t->msg.read_sem, -1); kfree(t); } @@ -1309,6 +1326,148 @@ void thread_atkernel_exit(void) } +status_t +user_send_data(thread_id tid, int32 code, const void *buffer, size_t buffer_size) +{ + if (((addr)buffer >= KERNEL_BASE) && ((addr)buffer <= KERNEL_TOP)) + return B_BAD_ADDRESS; + return send_data(tid, code, buffer, buffer_size); +} + + +status_t +send_data(thread_id tid, int32 code, const void *buffer, size_t buffer_size) +{ + struct thread *target; + sem_id cached_sem; + int state; + status_t rv; + cbuf *data; + + state = disable_interrupts(); + GRAB_THREAD_LOCK(); + target = thread_get_thread_struct_locked(tid); + if (!target) { + RELEASE_THREAD_LOCK(); + restore_interrupts(state); + return B_BAD_THREAD_ID; + } + cached_sem = target->msg.write_sem; + RELEASE_THREAD_LOCK(); + restore_interrupts(state); + + if (buffer_size > THREAD_MAX_MESSAGE_SIZE) + return B_NO_MEMORY; + + rv = acquire_sem_etc(cached_sem, 1, B_CAN_INTERRUPT, 0); + if (rv == B_INTERRUPTED) + // We got interrupted by a signal + return rv; + if (rv != B_OK) + // Any other acquisition problems may be due to thread deletion + return B_BAD_THREAD_ID; + + if (buffer_size > 0) { + data = cbuf_get_chain(buffer_size); + if (!data) + return B_NO_MEMORY; + rv = cbuf_user_memcpy_to_chain(data, 0, buffer, buffer_size); + if (rv < 0) { + cbuf_free_chain(data); + return B_NO_MEMORY; + } + } else + data = NULL; + + state = disable_interrupts(); + GRAB_THREAD_LOCK(); + + // The target thread could have been deleted at this point + target = thread_get_thread_struct_locked(tid); + if (!target) { + RELEASE_THREAD_LOCK(); + restore_interrupts(state); + cbuf_free_chain(data); + return B_BAD_THREAD_ID; + } + + // Save message informations + target->msg.sender = thread_get_current_thread()->id; + target->msg.code = code; + target->msg.size = buffer_size; + target->msg.buffer = data; + cached_sem = target->msg.read_sem; + + RELEASE_THREAD_LOCK(); + restore_interrupts(state); + + release_sem(cached_sem); + + return B_OK; +} + + +status_t +user_receive_data(thread_id *sender, void *buffer, size_t buffer_size) +{ + thread_id ksender; + status_t code; + status_t rv; + + if (((addr)sender >= KERNEL_BASE) && ((addr)sender <= KERNEL_TOP)) + return B_BAD_ADDRESS; + if (((addr)buffer >= KERNEL_BASE) && ((addr)buffer <= KERNEL_TOP)) + return B_BAD_ADDRESS; + + code = receive_data(&ksender, buffer, buffer_size); + + rv = user_memcpy(sender, &ksender, sizeof(thread_id)); + if (rv < 0) + return rv; + + return code; +} + + +status_t +receive_data(thread_id *sender, void *buffer, size_t buffer_size) +{ + struct thread *t = thread_get_current_thread(); + status_t rv; + size_t size; + int32 code; + + acquire_sem(t->msg.read_sem); + + size = min(buffer_size, t->msg.size); + rv = cbuf_user_memcpy_from_chain(buffer, t->msg.buffer, 0, size); + if (rv < 0) { + cbuf_free_chain(t->msg.buffer); + release_sem(t->msg.write_sem); + return rv; + } + + *sender = t->msg.sender; + code = t->msg.code; + + cbuf_free_chain(t->msg.buffer); + release_sem(t->msg.write_sem); + + return code; +} + + +bool +has_data(thread_id thread) +{ + int32 count; + + if (get_sem_count(thread_get_current_thread()->msg.read_sem, &count) != B_OK) + return false; + return (count == 0 ? false : true); +} + + status_t user_get_thread_info(thread_id id, thread_info *info) { diff --git a/src/kernel/libroot/libroot.c b/src/kernel/libroot/libroot.c index 322164ad88..f6ff08882d 100755 --- a/src/kernel/libroot/libroot.c +++ b/src/kernel/libroot/libroot.c @@ -196,11 +196,11 @@ thread_id find_thread(const char *name); #define get_team_usage_info(tmid, who, info) _get_team_usage_info((tmid), (who), (info), sizeof(*(info))) */ -// TO DO +// OK status_t send_data(thread_id thread, int32 code, const void *buf, size_t buffer_size); -// TO DO +// OK status_t receive_data(thread_id *sender, void *buf, size_t buffer_size); -// TO DO +// OK bool has_data(thread_id thread); // TO DO status_t snooze(bigtime_t microseconds);