diff --git a/cia.c b/cia.c index 717b678..ed5852e 100644 --- a/cia.c +++ b/cia.c @@ -9,6 +9,7 @@ #include #include #include +#include // Module cia_memory #include "src/cia-mem/util.c" @@ -16,6 +17,9 @@ #include "src/cia-mem/arena.c" #include "src/cia-mem/pool.c" +// Module cia_sync +#include "src/cia-sync/mutex.c" + // Module stdlib_program #include "src/stdlib-program/program.c" @@ -24,3 +28,4 @@ // Module stdlib_file #include "src/stdlib-file/file.c" + diff --git a/include/cia/sync.h b/include/cia/sync.h index 58336ac..4eb436a 100644 --- a/include/cia/sync.h +++ b/include/cia/sync.h @@ -1,4 +1,17 @@ #pragma once +#include +int cia_wait(u32 *addr, u32 compare_with, u64 time); +int cia_wake_one(u32 *addr, u32 *n_woken); +int cia_wake_all(u32 *addr, u32 *n_woken); + +struct Cia_Mutex typedef Cia_Mutex; +struct Cia_Mutex { + u32 tag; +}; + +void cia_mutex_init(Cia_Mutex *mutex); +void cia_mutex_lock(Cia_Mutex *mutex); +void cia_mutex_unlock(Cia_Mutex *mutex); diff --git a/include/linux/futex.h b/include/linux/futex.h new file mode 100644 index 0000000..fa4e726 --- /dev/null +++ b/include/linux/futex.h @@ -0,0 +1,77 @@ + +#pragma once + +#include + +#define FUTEX_WAIT 0 +#define FUTEX_WAKE 1 +#define FUTEX_FD 2 +#define FUTEX_REQUEUE 3 +#define FUTEX_CMP_REQUEUE 4 +#define FUTEX_WAKE_OP 5 +#define FUTEX_LOCK_PI 6 +#define FUTEX_UNLOCK_PI 7 +#define FUTEX_TRYLOCK_PI 8 +#define FUTEX_WAIT_BITSET 9 +#define FUTEX_WAKE_BITSET 10 +#define FUTEX_WAIT_REQUEUE_PI 11 +#define FUTEX_CMP_REQUEUE_PI 12 +#define FUTEX_LOCK_PI2 13 + +#define FUTEX_PRIVATE_FLAG 128 +#define FUTEX_CLOCK_REALTIME 256 +#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) + +#define FUTEX_WAIT_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_WAIT) +#define FUTEX_WAKE_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_WAKE) +#define FUTEX_REQUEUE_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_REQUEUE) +#define FUTEX_CMP_REQUEUE_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_CMP_REQUEUE) +#define FUTEX_WAKE_OP_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_WAKE_OP) +#define FUTEX_LOCK_PI_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_LOCK_PI) +#define FUTEX_LOCK_PI2_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_LOCK_PI2) +#define FUTEX_UNLOCK_PI_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_UNLOCK_PI) +#define FUTEX_TRYLOCK_PI_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_TRYLOCK_PI) +#define FUTEX_WAIT_BITSET_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_WAIT_BITSET) +#define FUTEX_WAKE_BITSET_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_WAKE_BITSET) +#define FUTEX_WAIT_REQUEUE_PI_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_WAIT_REQUEUE_PI) +#define FUTEX_CMP_REQUEUE_PI_PRIVATE (FUTEX_PRIVATE_FLAG | FUTEX_CMP_REQUEUE_PI) + +#define FUTEX_32 2 +#define FUTEX_WAITV_MAX 128 + +struct futex_waitv { + u64 val; + u64 uaddr; + u32 flags; + u32 __reserved; +}; + +struct robust_list { + struct robust_list *next; +}; + +struct robust_list_head { + struct robust_list list; + long futex_offset; + struct robust_list *list_op_pending; +}; + +#define FUTEX_WAITERS 0x80000000 +#define FUTEX_OWNER_DIED 0x40000000 +#define FUTEX_TID_MASK 0x3fffffff +#define ROBUST_LIST_LIMIT 2048 +#define FUTEX_BITSET_MATCH_ANY 0xffffffff + +#define FUTEX_OP_SET 0 /* *(int *)UADDR2 = OPARG; */ +#define FUTEX_OP_ADD 1 /* *(int *)UADDR2 += OPARG; */ +#define FUTEX_OP_OR 2 /* *(int *)UADDR2 |= OPARG; */ +#define FUTEX_OP_ANDN 3 /* *(int *)UADDR2 &= ~OPARG; */ +#define FUTEX_OP_XOR 4 /* *(int *)UADDR2 ^= OPARG; */ +#define FUTEX_OP_OPARG_SHIFT 8 /* Use (1 << OPARG) instead of OPARG. */ +#define FUTEX_OP_CMP_EQ 0 /* if (oldval == CMPARG) wake */ +#define FUTEX_OP_CMP_NE 1 /* if (oldval != CMPARG) wake */ +#define FUTEX_OP_CMP_LT 2 /* if (oldval < CMPARG) wake */ +#define FUTEX_OP_CMP_LE 3 /* if (oldval <= CMPARG) wake */ +#define FUTEX_OP_CMP_GT 4 /* if (oldval > CMPARG) wake */ +#define FUTEX_OP_CMP_GE 5 /* if (oldval >= CMPARG) wake */ + diff --git a/include/time.h b/include/time.h new file mode 100644 index 0000000..a106cf5 --- /dev/null +++ b/include/time.h @@ -0,0 +1,7 @@ + +#pragma once + +struct timespec { + time_t tv_sec; + long tv_nsec; +}; \ No newline at end of file diff --git a/include/tinyrt.h b/include/tinyrt.h index 7d93f9a..870a0d3 100644 --- a/include/tinyrt.h +++ b/include/tinyrt.h @@ -1,13 +1,15 @@ #pragma once -// Common errors +#include + +// Common status #define _RT_STATUS_OK 0 // No errors #define _RT_ERROR_NOT_IMPLEMENTED -1 // Function not implemented #define _RT_ERROR_BAD_PARAM -2 // One of the function parameters was wrong #define _RT_ERROR_GENERIC -3 // Just any random error -// File API errors +// File API status #define _RT_STATUS_FILE_ACCESS 1 // No access to the file #define _RT_STATUS_FILE_NO_SPACE 2 // Storage device has no space for the file #define _RT_STATUS_FILE_EXISTS 3 // File exists when shouldn't @@ -30,6 +32,9 @@ #define _RT_FILE_EXCLUSIVE 0x08 #define _RT_FILE_TRUNCATE 0x10 +// Sync API sleep constant +#define _RT_SYNC_WAIT_INFINITE 0xffffffffffffffffULL + typedef i32 _RT_Status; // API implementation flags (managed and used by the layers on top of tinyrt) @@ -82,3 +87,8 @@ static _RT_Status _rt_file_close(_RT_File *file); // Memory API static _RT_Status _rt_mem_alloc(void *optional_desired_addr, u64 size, void **out_addr); static _RT_Status _rt_mem_free(void *ptr, u64 size); + +// Synchronization API +static _RT_Status _rt_sync_wait(u32 *addr, u32 compare_with, u64 time); +static _RT_Status _rt_sync_wake_one(u32 *addr, u32 *n_woken); +static _RT_Status _rt_sync_wake_all(u32 *addr, u32 *n_woken); \ No newline at end of file diff --git a/loader/loader.c b/loader/loader.c index e0558e4..ead5367 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -1,10 +1,12 @@ #include + #include #include #include #include "loader.h" +#include #include #include #include diff --git a/os/linux/conf.h b/os/linux/conf.h index 482dc6b..9091388 100644 --- a/os/linux/conf.h +++ b/os/linux/conf.h @@ -1,6 +1,7 @@ #pragma once +#include #include #include #include diff --git a/os/linux/tinyrt.c b/os/linux/tinyrt.c index 16ffe90..630bd0a 100644 --- a/os/linux/tinyrt.c +++ b/os/linux/tinyrt.c @@ -150,4 +150,33 @@ static _RT_Status _rt_mem_free(void *ptr, u64 len) { return _RT_ERROR_GENERIC; } return _RT_STATUS_OK; -} \ No newline at end of file +} + +static _RT_Status _rt_sync_wait(u32 *addr, u32 compare_with, u64 sleep) { + i64 result = syscall(SYS_futex, addr, FUTEX_WAIT_PRIVATE, compare_with, NULL, 0, 0); + if(-result == EAGAIN) { + return _RT_STATUS_OK; + } + else if(-result > 0) { + return _RT_ERROR_GENERIC; + } + return _RT_STATUS_OK; +} + +static _RT_Status _rt_sync_wake_one(u32 *addr, u32 *n_woken) { + i64 result = syscall(SYS_futex, addr, FUTEX_WAKE_PRIVATE, 1, NULL, 0, 0); + if(result < 0) { + return _RT_ERROR_GENERIC; + } + *n_woken = (u32)result; + return _RT_STATUS_OK; +} + +static _RT_Status _rt_sync_wake_all(u32 *addr, u32 *n_woken) { + i64 result = syscall(SYS_futex, addr, FUTEX_WAKE_PRIVATE, 0x7fffffff, NULL, 0, 0); + if(result < 0) { + return _RT_ERROR_GENERIC; + } + *n_woken = (u32)result; + return _RT_STATUS_OK; +} diff --git a/src/cia-sync/futex.c b/src/cia-sync/futex.c new file mode 100644 index 0000000..dfbd48b --- /dev/null +++ b/src/cia-sync/futex.c @@ -0,0 +1,13 @@ + +int cia_wait(u32 *addr, u32 compare_with, u64 time) { + _rt_sync_wait(addr, compare_with, time); +} + +int cia_wake_one(u32 *addr, u32 *n_woken) { + _rt_sync_wake_one(addr, n_woken); +} + +int cia_wake_all(u32 *addr, u32 *n_woken) { + _rt_sync_wake_all(addr, n_woken); +} + diff --git a/src/cia-sync/mutex.c b/src/cia-sync/mutex.c new file mode 100644 index 0000000..d4254a8 --- /dev/null +++ b/src/cia-sync/mutex.c @@ -0,0 +1,21 @@ + +#define _CIA_MUTEX_TAG_UNLOCKED 0x00000000 +#define _CIA_MUTEX_TAG_LOCKED 0x00000001 + +void cia_mutex_init(Cia_Mutex *mutex) { + mutex->tag = _CIA_MUTEX_TAG_UNLOCKED; +} + +void cia_mutex_lock(Cia_Mutex *mutex) { + do { + _rt_sync_wait(&mutex->tag, _CIA_MUTEX_TAG_LOCKED, _RT_SYNC_WAIT_INFINITE); + } while(mutex->tag == _CIA_MUTEX_TAG_LOCKED); + mutex->tag = _CIA_MUTEX_TAG_LOCKED; +} + +void cia_mutex_unlock(Cia_Mutex *mutex) { + mutex->tag = _CIA_MUTEX_TAG_UNLOCKED; + u32 woken = 0; + _rt_sync_wake_one(&mutex->tag, &woken); +} + diff --git a/tests/threaded.c b/tests/threaded.c index f34fe5c..bd5ed48 100644 --- a/tests/threaded.c +++ b/tests/threaded.c @@ -3,6 +3,7 @@ #include #include #include +#include static void print_string_n(char *str, u64 len) { fwrite(str, 1, len, stdout); @@ -33,22 +34,39 @@ static void print_int(i64 number) { print_string(p); } -volatile _Atomic i64 counter = 0; +static Cia_Mutex g_print_mutex; +static Cia_Mutex g_mutex; +static volatile _Atomic i64 n_completed = 0; +static volatile i64 counter = 0; int thrd_func(void *arg) { print_string("child thread: ok!\n"); for(int i = 0; i < 100000; ++i) { + cia_mutex_lock(&g_mutex); counter += 1; + print_int(counter); + print_char('\n'); + cia_mutex_unlock(&g_mutex); } + atomic_fetch_add(&n_completed, 1); + for(;;) { + if(n_completed == 2) { + break; + } + } + cia_mutex_lock(&g_print_mutex); print_string("child thread: counter = "); print_int(counter); print_char('\n'); - for(;;); + cia_mutex_unlock(&g_print_mutex); + exit(1); return 0; } int main() { print_string("main thread: before\n"); + cia_mutex_init(&g_mutex); + cia_mutex_init(&g_print_mutex); thrd_t thrd; int status = thrd_create(&thrd, thrd_func, NULL); if(status == thrd_error) { @@ -57,10 +75,22 @@ int main() { } print_string("main thread: after!\n"); for(int i = 0; i < 100000; ++i) { + cia_mutex_lock(&g_mutex); counter += 1; + print_int(counter); + print_char('\n'); + cia_mutex_unlock(&g_mutex); } + atomic_fetch_add(&n_completed, 1); + for(;;) { + if(n_completed == 2) { + break; + } + } + cia_mutex_lock(&g_print_mutex); print_string("main thread: counter = "); print_int(counter); print_char('\n'); + cia_mutex_unlock(&g_print_mutex); return 0; }