From 4f308829ad0e81a59971e172284c018cf2bdca3d Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 22 Mar 2019 20:48:40 +0100 Subject: Add mutex for lhs expr, add error for missing lhs expr for func, struct TODO: Use mutex in lhs expr and set resolved_type --- include/ast.h | 10 +++++++- include/parser.h | 3 ++- include/std/scoped_allocator.h | 4 ++++ include/std/thread.h | 13 ++++++---- src/ast.c | 39 ++++++++++++++++++++++++++---- src/compiler.c | 6 ++++- src/parser.c | 54 ++++++++++++++++++++++++------------------ src/std/scoped_allocator.c | 39 +++++++++++++++++++++++++----- src/std/thread.c | 6 ++++- tests/main.amal | 3 ++- 10 files changed, 134 insertions(+), 43 deletions(-) diff --git a/include/ast.h b/include/ast.h index 7b16796..25d9034 100644 --- a/include/ast.h +++ b/include/ast.h @@ -2,6 +2,7 @@ #define AMALGAM_AST_H #include "defs.h" +#include "std/defs.h" #include "std/buffer_view.h" #include "std/buffer.h" #include "std/misc.h" @@ -108,6 +109,13 @@ struct LhsExpr { BufferView var_name; Variable type; Ast rhs_expr; + /* + A mutex is only available if the LhsExpr is public, because other threads (files) + can access it. It's used to prevent usage of unresolved data and also to decide which + thread has responsibility of resolving data. + TODO: Find a better way to store this. This most likely will use too much memory. + */ + amal_mutex *mutex; }; struct Import { @@ -149,7 +157,7 @@ CHECK_RESULT int funcdecl_init(FunctionDecl *self, Scope *parent, ScopedAllocato CHECK_RESULT int funccall_init(FunctionCall *self, BufferView name, ScopedAllocator *allocator); CHECK_RESULT int structdecl_init(StructDecl *self, Scope *parent, ScopedAllocator *allocator); void structfield_init(StructField *self, BufferView name, BufferView type_name); -void lhsexpr_init(LhsExpr *self, bool is_pub, bool is_const, BufferView var_name); +CHECK_RESULT int lhsexpr_init(LhsExpr *self, bool is_pub, bool is_const, BufferView var_name, ScopedAllocator *allocator); void import_init(Import *self, BufferView path); CHECK_RESULT int string_init(String *self, BufferView str); void number_init(Number *self, i64 value, bool is_integer); diff --git a/include/parser.h b/include/parser.h index 84d46bd..92e6d46 100644 --- a/include/parser.h +++ b/include/parser.h @@ -29,7 +29,8 @@ struct ParserThreadData { typedef enum { ERROR_CONTEXT_NONE, - ERROR_CONTEXT_RHS_START + ERROR_CONTEXT_RHS_STANDALONE, + ERROR_CONTEXT_NO_LHS } ErrorContext; struct Parser { diff --git a/include/std/scoped_allocator.h b/include/std/scoped_allocator.h index 9090767..d7ec1e1 100644 --- a/include/std/scoped_allocator.h +++ b/include/std/scoped_allocator.h @@ -15,7 +15,10 @@ struct ScopedAllocatorNode { struct ScopedAllocator { ScopedAllocatorNode head; ScopedAllocatorNode *current; + /* TODO: Use linked-list instead, since the elements are dynamically allocated anyways */ + /* Find another way to store mutexes */ Buffer/**/ buffers; + Buffer/**/ mutexes; }; CHECK_RESULT int scoped_allocator_node_init(ScopedAllocatorNode *self); @@ -25,5 +28,6 @@ CHECK_RESULT int scoped_allocator_init(ScopedAllocator *self); void scoped_allocator_deinit(ScopedAllocator *self); CHECK_RESULT int scoped_allocator_alloc(ScopedAllocator *self, usize size, void **mem); CHECK_RESULT int scoped_allocator_add_buffer(ScopedAllocator *self, Buffer *buffer); +CHECK_RESULT int scoped_allocator_create_mutex(ScopedAllocator *self, amal_mutex **mutex); #endif diff --git a/include/std/thread.h b/include/std/thread.h index 6eedb08..356ebf0 100644 --- a/include/std/thread.h +++ b/include/std/thread.h @@ -13,6 +13,8 @@ typedef void* (AmalThreadCallbackFunc)(void *userdata); #define AMAL_THREAD_ERR -1 #define AMAL_THREAD_NOT_JOINABLE -23 +/*#define AMAL_MUTEX_DEBUG*/ + struct amal_thread { pthread_t thread_id; pthread_attr_t thread_attr; @@ -28,7 +30,9 @@ typedef enum { struct amal_mutex { pthread_mutex_t mutex; + #ifdef AMAL_MUTEX_DEBUG const char *lock_identifier; + #endif bool locked; pthread_t owner_thread; }; @@ -39,6 +43,10 @@ CHECK_RESULT int amal_thread_deinit(amal_thread *self); CHECK_RESULT int amal_thread_detach(amal_thread *self); CHECK_RESULT int amal_thread_join(amal_thread *self, void **result); +bool amal_thread_is_main(); +/* Returns 0 if the number of usable threads is unknown */ +int amal_get_usable_thread_count(); + void amal_mutex_init(amal_mutex *self); void amal_mutex_deinit(amal_mutex *self); CHECK_RESULT int amal_mutex_lock(amal_mutex *self, const char *lock_identifier); @@ -46,9 +54,4 @@ CHECK_RESULT int amal_mutex_lock(amal_mutex *self, const char *lock_identifier); CHECK_RESULT int amal_mutex_unlock(amal_mutex *self); void amal_mutex_tryunlock(amal_mutex *self); -bool amal_thread_is_main(); - -/* Returns 0 if the number of usable threads is unknown */ -int amal_get_usable_thread_count(); - #endif diff --git a/src/ast.c b/src/ast.c index 1bb1eb3..d0a5030 100644 --- a/src/ast.c +++ b/src/ast.c @@ -82,12 +82,17 @@ void structfield_init(StructField *self, BufferView name, BufferView type_name) variable_init(&self->type, type_name); } -void lhsexpr_init(LhsExpr *self, bool is_pub, bool is_const, BufferView var_name) { +int lhsexpr_init(LhsExpr *self, bool is_pub, bool is_const, BufferView var_name, ScopedAllocator *allocator) { self->is_pub = is_pub; self->is_const = is_const; variable_init(&self->type, create_buffer_view_null()); self->var_name = var_name; self->rhs_expr = ast_none(); + if(is_pub && allocator) + return_if_error(scoped_allocator_create_mutex(allocator, &self->mutex)); + else + self->mutex = NULL; + return 0; } void import_init(Import *self, BufferView path) { @@ -208,7 +213,7 @@ static void variable_resolve(Variable *self, AstCompilerContext *context, LhsExp *resolved_type = scope_get_resolved_variable(context->scope, context, self->name); } -static void lhs_resolve(Ast *self, AstCompilerContext *context) { +static void lhsexpr_resolve(Ast *self, AstCompilerContext *context) { LhsExpr *lhs_expr; lhs_expr = self->value.lhs_expr; @@ -228,6 +233,22 @@ static void lhs_resolve(Ast *self, AstCompilerContext *context) { self->resolved_type = lhs_expr->rhs_expr.resolved_type; } +/* LhsExpr has to be resolved before this is called */ +static Scope* lhsexpr_get_scope(LhsExpr *self) { + switch(self->rhs_expr.type) { + case AST_FUNCTION_DECL: + return &self->rhs_expr.value.func_decl->body; + case AST_STRUCT_DECL: + return &self->rhs_expr.value.struct_decl->body; + case AST_IMPORT: + return self->rhs_expr.value.import->file_scope->scope; + default: + break; + } + assert(bool_false && "Expected lhsexpr_get_scope to only be called for function decl and struct decl"); + return NULL; +} + static void import_resolve(Ast *self, AstCompilerContext *context) { Import *import; import = self->value.import; @@ -278,7 +299,17 @@ static void structfield_resolve(Ast *self, AstCompilerContext *context) { static void binop_resolve(Binop *self, AstCompilerContext *context) { /* TODO: Implement */ ast_resolve(&self->lhs, context); - ast_resolve(&self->rhs, context); + if(self->rhs.type == AST_VARIABLE) { + Scope *prev_scope; + assert(self->lhs.resolved_type); + prev_scope = context->scope; + context->scope = lhsexpr_get_scope(self->lhs.resolved_type); + assert(context->scope); + ast_resolve(&self->rhs, context); + context->scope = prev_scope; + } else { + ast_resolve(&self->rhs, context); + } } void ast_resolve(Ast *self, AstCompilerContext *context) { @@ -317,7 +348,7 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { structfield_resolve(self, context); break; case AST_LHS: - lhs_resolve(self, context); + lhsexpr_resolve(self, context); break; case AST_IMPORT: /* TODO: When @import(...).data syntax is added, implement the resolve for it */ diff --git a/src/compiler.c b/src/compiler.c index 6f9cc92..50678d8 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -47,9 +47,13 @@ static CHECK_RESULT int create_default_type(amal_compiler *compiler, const char return_if_error(structdecl_init(struct_decl, &compiler->root_scope, &compiler->allocator)); return_if_error(scoped_allocator_alloc(&compiler->allocator, sizeof(LhsExpr), (void**)&lhs_expr)); - lhsexpr_init(lhs_expr, bool_true, bool_true, create_buffer_view(name, strnlen(name, PATH_MAX))); + return_if_error(lhsexpr_init(lhs_expr, bool_true, bool_true, + create_buffer_view(name, strnlen(name, PATH_MAX)), + NULL)); ast_init(&lhs_expr->rhs_expr, struct_decl, AST_STRUCT_DECL); ast_init(&expr, lhs_expr, AST_LHS); + expr.resolved_type = lhs_expr; + expr.resolve_status = AST_RESOLVED; return scope_add_child(&compiler->root_scope, &expr); } diff --git a/src/parser.c b/src/parser.c index 57606b5..b1764a1 100644 --- a/src/parser.c +++ b/src/parser.c @@ -33,7 +33,7 @@ do { \ throw(return_if_result); \ } while(0) -static THROWABLE parser_parse_rhs_start(Parser *self, Ast *rhs_expr); +static THROWABLE parser_parse_rhs_start(Parser *self, Ast *rhs_expr, bool is_standalone); static THROWABLE parser_parse_body(Parser *self, Ast *ast); static THROWABLE parser_parse_struct_body(Parser *self, Ast *ast); static THROWABLE parser_queue_file(Parser *self, BufferView path, FileScopeReference **file_scope); @@ -188,7 +188,7 @@ static THROWABLE parser_parse_lhs(Parser *self, LhsExpr **result, bool *assignme throw_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER)); var_name = self->tokenizer.value.identifier; throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)result)); - lhsexpr_init(*result, is_pub, is_const, var_name); + throw_if_error(lhsexpr_init(*result, is_pub, is_const, var_name, self->allocator)); try(parser_parse_var_type_def(self, &(*result)->type.name)); @@ -292,7 +292,7 @@ static THROWABLE parser_parse_function_args(Parser *self, FunctionCall *func_cal throw_if_error(tokenizer_accept(&self->tokenizer, TOK_COMMA)); first_arg = bool_false; - try(parser_parse_rhs_start(self, &arg_expr)); + try(parser_parse_rhs_start(self, &arg_expr, bool_false)); throw_if_error(buffer_append(&func_call->args, &arg_expr, sizeof(arg_expr))); } @@ -457,31 +457,33 @@ static THROWABLE parser_parse_rhs(Parser *self, Ast *rhs_expr) { /* RHS_START = CLOSURE | IMPORT | RHS */ -int parser_parse_rhs_start(Parser *self, Ast *rhs_expr) { +int parser_parse_rhs_start(Parser *self, Ast *rhs_expr, bool is_standalone) { FunctionDecl *func_decl; StructDecl *struct_decl; Import *import; - try(parser_parse_function_decl(self, &func_decl)); - if(func_decl) { - ast_init(rhs_expr, func_decl, AST_FUNCTION_DECL); - return PARSER_OK; - } - - try(parser_parse_struct_decl(self, &struct_decl)); - if(struct_decl) { - ast_init(rhs_expr, struct_decl, AST_STRUCT_DECL); - return PARSER_OK; - } + if(!is_standalone) { + try(parser_parse_function_decl(self, &func_decl)); + if(func_decl) { + ast_init(rhs_expr, func_decl, AST_FUNCTION_DECL); + return PARSER_OK; + } + + try(parser_parse_struct_decl(self, &struct_decl)); + if(struct_decl) { + ast_init(rhs_expr, struct_decl, AST_STRUCT_DECL); + return PARSER_OK; + } - try(parser_parse_import(self, &import)); - if(import) { - try(parser_queue_file(self, import->path, &import->file_scope)); - ast_init(rhs_expr, import, AST_IMPORT); - return PARSER_OK; + try(parser_parse_import(self, &import)); + if(import) { + try(parser_queue_file(self, import->path, &import->file_scope)); + ast_init(rhs_expr, import, AST_IMPORT); + return PARSER_OK; + } + self->error_context = ERROR_CONTEXT_RHS_STANDALONE; } - self->error_context = ERROR_CONTEXT_RHS_START; try(parser_parse_rhs(self, rhs_expr)); self->error_context = ERROR_CONTEXT_NONE; return PARSER_OK; @@ -530,7 +532,10 @@ int parser_parse_body(Parser *self, Ast *ast) { return PARSER_OK; } - try(parser_parse_rhs_start(self, &rhs_expr)); + if(!lhs_expr) + self->error_context = ERROR_CONTEXT_NO_LHS; + + try(parser_parse_rhs_start(self, &rhs_expr, lhs_expr == NULL)); if(lhs_expr) { lhs_expr->rhs_expr = rhs_expr; ast_init(ast, lhs_expr, AST_LHS); @@ -576,9 +581,12 @@ int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_ case ERROR_CONTEXT_NONE: tokenizer_print_error_object(&self->tokenizer, &self->error); break; - case ERROR_CONTEXT_RHS_START: + case ERROR_CONTEXT_RHS_STANDALONE: tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected string, variable, closure, struct, function call or import"); break; + case ERROR_CONTEXT_NO_LHS: + tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected variable declaration, string, variable or function call"); + break; default: assert(bool_false && "Error context handling not implemented"); break; diff --git a/src/std/scoped_allocator.c b/src/std/scoped_allocator.c index c66639a..3104fd3 100644 --- a/src/std/scoped_allocator.c +++ b/src/std/scoped_allocator.c @@ -1,5 +1,6 @@ #include "../../include/std/scoped_allocator.h" #include "../../include/std/alloc.h" +#include "../../include/std/thread.h" #include "../../include/std/log.h" #include @@ -26,7 +27,8 @@ void scoped_allocator_node_deinit(ScopedAllocatorNode *self) { int scoped_allocator_init(ScopedAllocator *self) { return_if_error(scoped_allocator_node_init(&self->head)); self->current = &self->head; - return buffer_init(&self->buffers, NULL); + return_if_error(buffer_init(&self->buffers, NULL)); + return buffer_init(&self->mutexes, NULL); } static void buffer_deinit(Buffer *self) { @@ -36,11 +38,9 @@ static void buffer_deinit(Buffer *self) { self->capacity = 0; } -void scoped_allocator_deinit(ScopedAllocator *self) { +static void scoped_allocator_deinit_buffers(ScopedAllocator *self) { Buffer **buffer; Buffer **buffers_end; - - self->current = NULL; buffer = buffer_start(&self->buffers); buffers_end = buffer_end(&self->buffers); while(buffer != buffers_end) { @@ -48,6 +48,24 @@ void scoped_allocator_deinit(ScopedAllocator *self) { ++buffer; } buffer_deinit(&self->buffers); +} + +static void scoped_allocator_deinit_mutexes(ScopedAllocator *self) { + amal_mutex **mutex; + amal_mutex **mutexes_end; + mutex = buffer_start(&self->mutexes); + mutexes_end = buffer_end(&self->mutexes); + while(mutex != mutexes_end) { + amal_mutex_deinit(*mutex); + ++mutex; + } + buffer_deinit(&self->mutexes); +} + +void scoped_allocator_deinit(ScopedAllocator *self) { + self->current = NULL; + scoped_allocator_deinit_buffers(self); + scoped_allocator_deinit_mutexes(self); scoped_allocator_node_deinit(&self->head); } @@ -79,6 +97,8 @@ static usize align_ptr_ceil_offset(void *ptr, uintptr_t alignment) { return (uintptr_t)align_ptr_ceil(ptr, alignment) - (uintptr_t)ptr; } +#define SCOPED_ALLOC_ALIGNMENT 8 + int scoped_allocator_alloc(ScopedAllocator *self, usize size, void **mem) { ScopedAllocatorNode *current; usize alloc_size; @@ -90,14 +110,14 @@ int scoped_allocator_alloc(ScopedAllocator *self, usize size, void **mem) { return -1; } - alloc_size = size + align_ptr_ceil_offset(self->current->data + self->current->size, 16); + alloc_size = size + align_ptr_ceil_offset(self->current->data + self->current->size, SCOPED_ALLOC_ALIGNMENT); return_if_error(scoped_allocator_ensure_capacity_for(self, alloc_size)); /* Reallocated (new node created) */ if(self->current != current) { *mem = self->current->data; self->current->size += size; } else { - *mem = align_ptr_ceil(self->current->data + self->current->size, 16); + *mem = align_ptr_ceil(self->current->data + self->current->size, SCOPED_ALLOC_ALIGNMENT); self->current->size += alloc_size; } return 0; @@ -106,3 +126,10 @@ int scoped_allocator_alloc(ScopedAllocator *self, usize size, void **mem) { int scoped_allocator_add_buffer(ScopedAllocator *self, Buffer *buffer) { return buffer_append(&self->buffers, &buffer, sizeof(Buffer*)); } + +int scoped_allocator_create_mutex(ScopedAllocator *self, amal_mutex **mutex) { + *mutex = NULL; + return_if_error(scoped_allocator_alloc(self, sizeof(amal_mutex), (void**)mutex)); + amal_mutex_init(*mutex); + return buffer_append(&self->mutexes, mutex, sizeof(amal_mutex*)); +} diff --git a/src/std/thread.c b/src/std/thread.c index ba67463..350631a 100644 --- a/src/std/thread.c +++ b/src/std/thread.c @@ -109,7 +109,9 @@ int amal_thread_join(amal_thread *self, void **result) { void amal_mutex_init(amal_mutex *self) { pthread_mutex_init(&self->mutex, NULL); + #ifdef AMAL_MUTEX_DEBUG self->lock_identifier = NULL; + #endif self->locked = bool_false; self->owner_thread = 0; } @@ -129,10 +131,11 @@ static long amal_thread_get_id() { int amal_mutex_lock(amal_mutex *self, const char *lock_identifier) { int result; result = pthread_mutex_lock(&self->mutex); - self->lock_identifier = lock_identifier; self->owner_thread = pthread_self(); self->locked = bool_true; + (void)lock_identifier; #ifdef AMAL_MUTEX_DEBUG + self->lock_identifier = lock_identifier; if(result == 0 && self->lock_identifier) { amal_log_debug("amal_mutex_lock: mutex locked by thread %lu (%s), identification: %s", amal_thread_get_id(), @@ -149,6 +152,7 @@ int amal_mutex_unlock(amal_mutex *self) { const char *identifier; identifier = self->lock_identifier; #endif + /* No-op if mutex isn't locked or if another thread owns the mutex lock */ if(!self->locked || pthread_equal(self->owner_thread, pthread_self()) == 0) return 0; self->locked = bool_false; diff --git a/tests/main.amal b/tests/main.amal index 65ac43b..3af537b 100644 --- a/tests/main.amal +++ b/tests/main.amal @@ -10,6 +10,7 @@ const main = fn { } const value = "hello"; + // fn {} // error, function declaration can't be by itself. Needs left-hand side print(value, "world", 356, 13.37); var num1: i32; const num2 = 23232; @@ -20,7 +21,7 @@ const main = fn { /* episfjpseifipesf */ - //io.puts("lole"); + io.puts("lole"); } const print = fn { -- cgit v1.2.3