aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--ast.c2
-rw-r--r--ast.h8
-rw-r--r--compile.c76
-rw-r--r--docs/mutexed.md28
-rw-r--r--parse.c28
-rw-r--r--stdlib/arrays.h4
-rw-r--r--stdlib/datatypes.h5
-rw-r--r--stdlib/pointers.c6
-rw-r--r--stdlib/stdlib.c30
-rw-r--r--stdlib/stdlib.h2
-rw-r--r--stdlib/threads.c4
-rw-r--r--stdlib/tomo.h1
-rw-r--r--stdlib/types.h4
-rw-r--r--test/optionals.tm20
-rw-r--r--test/threads.tm56
-rw-r--r--typecheck.c22
-rw-r--r--types.c11
-rw-r--r--types.h3
19 files changed, 216 insertions, 96 deletions
diff --git a/Makefile b/Makefile
index 2342568d..a0194a90 100644
--- a/Makefile
+++ b/Makefile
@@ -34,7 +34,7 @@ BUILTIN_OBJS=stdlib/siphash.o stdlib/arrays.o stdlib/bools.o stdlib/bytes.o stdl
stdlib/pointers.o stdlib/memory.o stdlib/text.o stdlib/threads.o stdlib/c_strings.o stdlib/tables.o \
stdlib/types.o stdlib/util.o stdlib/files.o stdlib/ranges.o stdlib/shell.o stdlib/paths.o stdlib/rng.o \
stdlib/optionals.o stdlib/patterns.o stdlib/metamethods.o stdlib/functiontype.o stdlib/stdlib.o \
- stdlib/structs.o stdlib/enums.o stdlib/moments.o
+ stdlib/structs.o stdlib/enums.o stdlib/moments.o stdlib/mutexeddata.o
TESTS=$(patsubst %.tm,%.tm.testresult,$(wildcard test/*.tm))
all: libtomo.so tomo
diff --git a/ast.c b/ast.c
index c6d59966..23f1409d 100644
--- a/ast.c
+++ b/ast.c
@@ -117,6 +117,7 @@ CORD ast_to_xml(ast_t *ast)
T(HeapAllocate, "<HeapAllocate>%r</HeapAllocate>", ast_to_xml(data.value))
T(StackReference, "<StackReference>%r</StackReference>", ast_to_xml(data.value))
T(Mutexed, "<Mutexed>%r</Mutexed>", ast_to_xml(data.value))
+ T(Holding, "<Holding>%r%r</Holding>", ast_to_xml(data.mutexed), ast_to_xml(data.body))
T(Min, "<Min>%r%r%r</Min>", ast_to_xml(data.lhs), ast_to_xml(data.rhs), optional_tagged("key", data.key))
T(Max, "<Max>%r%r%r</Max>", ast_to_xml(data.lhs), ast_to_xml(data.rhs), optional_tagged("key", data.key))
T(Array, "<Array>%r%r</Array>", optional_tagged_type("item-type", data.item_type), ast_list_to_xml(data.items))
@@ -185,6 +186,7 @@ CORD type_ast_to_xml(type_ast_t *t)
T(TableTypeAST, "<TableType>%r %r</TableType>", type_ast_to_xml(data.key), type_ast_to_xml(data.value))
T(FunctionTypeAST, "<FunctionType>%r %r</FunctionType>", arg_list_to_xml(data.args), type_ast_to_xml(data.ret))
T(OptionalTypeAST, "<OptionalType>%r</OptionalType>", data.type)
+ T(MutexedTypeAST, "<MutexedType>%r</MutexedType>", data.type)
#undef T
default: return CORD_EMPTY;
}
diff --git a/ast.h b/ast.h
index 424eced2..173b3178 100644
--- a/ast.h
+++ b/ast.h
@@ -78,6 +78,7 @@ typedef enum {
TableTypeAST,
FunctionTypeAST,
OptionalTypeAST,
+ MutexedTypeAST,
} type_ast_e;
typedef struct tag_ast_s {
@@ -116,7 +117,7 @@ struct type_ast_s {
} FunctionTypeAST;
struct {
type_ast_t *type;
- } OptionalTypeAST;
+ } OptionalTypeAST, MutexedTypeAST;
} __data;
};
@@ -127,7 +128,7 @@ typedef enum {
TextLiteral, TextJoin, PrintStatement,
Declare, Assign,
BinaryOp, UpdateAssign,
- Not, Negative, HeapAllocate, StackReference, Mutexed,
+ Not, Negative, HeapAllocate, StackReference, Mutexed, Holding,
Min, Max,
Array, Set, Table, TableEntry, Comprehension,
FunctionDef, Lambda,
@@ -195,6 +196,9 @@ struct ast_s {
ast_t *value;
} Not, Negative, HeapAllocate, StackReference, Mutexed;
struct {
+ ast_t *mutexed, *body;
+ } Holding;
+ struct {
ast_t *lhs, *rhs, *key;
} Min, Max;
struct {
diff --git a/compile.c b/compile.c
index 07df7564..e6a22b09 100644
--- a/compile.c
+++ b/compile.c
@@ -249,6 +249,7 @@ CORD compile_type(type_t *t)
case AbortType: return "void";
case VoidType: return "void";
case MemoryType: return "void";
+ case MutexedType: return "MutexedData_t";
case BoolType: return "Bool_t";
case ByteType: return "Byte_t";
case CStringType: return "char*";
@@ -296,7 +297,7 @@ CORD compile_type(type_t *t)
case OptionalType: {
type_t *nonnull = Match(t, OptionalType)->type;
switch (nonnull->tag) {
- case CStringType: case FunctionType: case ClosureType:
+ case CStringType: case FunctionType: case ClosureType: case MutexedType:
case PointerType: case EnumType:
return compile_type(nonnull);
case TextType:
@@ -459,6 +460,8 @@ CORD check_none(type_t *t, CORD value)
return CORD_all("((", value, ").tag == 0)");
else if (t->tag == MomentType)
return CORD_all("((", value, ").tv_usec < 0)");
+ else if (t->tag == MutexedType)
+ return CORD_all("(", value, " == NULL)");
errx(1, "Optional check not implemented for: %T", t);
}
@@ -1038,7 +1041,10 @@ CORD compile_statement(env_t *env, ast_t *ast)
CORD code = CORD_EMPTY;
for (deferral_t *deferred = env->deferred; deferred && deferred != ctx->deferred; deferred = deferred->next)
code = CORD_all(code, compile_statement(deferred->defer_env, deferred->block));
- return CORD_all(code, "goto ", ctx->skip_label, ";");
+ if (code)
+ return CORD_all("{\n", code, "goto ", ctx->skip_label, ";\n}\n");
+ else
+ return CORD_all("goto ", ctx->skip_label, ";");
}
}
if (env->loop_ctx)
@@ -1064,7 +1070,10 @@ CORD compile_statement(env_t *env, ast_t *ast)
CORD code = CORD_EMPTY;
for (deferral_t *deferred = env->deferred; deferred && deferred != ctx->deferred; deferred = deferred->next)
code = CORD_all(code, compile_statement(deferred->defer_env, deferred->block));
- return CORD_all(code, "goto ", ctx->stop_label, ";");
+ if (code)
+ return CORD_all("{\n", code, "goto ", ctx->stop_label, ";\n}\n");
+ else
+ return CORD_all("goto ", ctx->stop_label, ";");
}
}
if (env->loop_ctx)
@@ -1181,6 +1190,30 @@ CORD compile_statement(env_t *env, ast_t *ast)
loop = CORD_all(loop, "\n", loop_ctx.stop_label, ":;");
return loop;
}
+ case Holding: {
+ ast_t *held = Match(ast, Holding)->mutexed;
+ type_t *held_type = get_type(env, held);
+ if (held_type->tag != MutexedType)
+ code_err(held, "This is a %t, not a mutexed value", held_type);
+ CORD code = CORD_all(
+ "{ // Holding\n",
+ "MutexedData_t mutexed = ", compile(env, held), ";\n",
+ "pthread_mutex_lock(&mutexed->mutex);\n");
+
+ env_t *body_scope = fresh_scope(env);
+ body_scope->deferred = new(deferral_t, .defer_env=env,
+ .block=FakeAST(InlineCCode, .code="pthread_mutex_unlock(&mutexed->mutex);"),
+ .next=body_scope->deferred);
+ if (held->tag == Var) {
+ CORD held_var = CORD_all(Match(held, Var)->name, "$held");
+ set_binding(body_scope, Match(held, Var)->name, new(binding_t, .type=Type(PointerType, .pointed=Match(held_type, MutexedType)->type, .is_stack=true),
+ .code=held_var));
+ code = CORD_all(code, compile_declaration(Type(PointerType, .pointed=Match(held_type, MutexedType)->type), held_var),
+ " = (", compile_type(Type(PointerType, .pointed=Match(held_type, MutexedType)->type)), ")mutexed->data;\n");
+ }
+ return CORD_all(code, compile_statement(body_scope, Match(ast, Holding)->body),
+ "pthread_mutex_unlock(&mutexed->mutex);\n}");
+ }
case For: {
auto for_ = Match(ast, For);
@@ -1585,7 +1618,8 @@ CORD expr_as_text(env_t *env, CORD expr, type_t *t, CORD color)
case FunctionType: case ClosureType: return CORD_asprintf("Func$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
case PointerType: return CORD_asprintf("Pointer$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
case OptionalType: return CORD_asprintf("Optional$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
- case StructType: case EnumType: return CORD_asprintf("generic_as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
+ case StructType: case EnumType: case MutexedType:
+ return CORD_asprintf("generic_as_text(stack(%r), %r, %r)", expr, color, compile_type_info(env, t));
default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for %T", t);
}
}
@@ -1997,6 +2031,7 @@ CORD compile_none(type_t *t)
env_t *enum_env = Match(t, EnumType)->env;
return CORD_all("((", compile_type(t), "){", namespace_prefix(enum_env, enum_env->namespace), "null})");
}
+ case MutexedType: return "NONE_MUTEXED_DATA";
default: compiler_err(NULL, NULL, NULL, "none isn't implemented for this type: %T", t);
}
}
@@ -2125,7 +2160,34 @@ CORD compile(env_t *env, ast_t *ast)
return CORD_all("stack(", compile(env, subject), ")");
}
case Mutexed: {
- return CORD_all("mutexed(", compile(env, Match(ast, Mutexed)->value), ")");
+ ast_t *mutexed = Match(ast, Mutexed)->value;
+ return CORD_all("new(struct MutexedData_s, .mutex=PTHREAD_MUTEX_INITIALIZER, .data=", compile(env, WrapAST(mutexed, HeapAllocate, mutexed)), ")");
+ }
+ case Holding: {
+ ast_t *held = Match(ast, Holding)->mutexed;
+ type_t *held_type = get_type(env, held);
+ if (held_type->tag != MutexedType)
+ code_err(held, "This is a %t, not a mutexed value", held_type);
+ CORD code = CORD_all(
+ "({ // Holding\n",
+ "MutexedData_t mutexed = ", compile(env, held), ";\n",
+ "pthread_mutex_lock(&mutexed->mutex);\n");
+
+ env_t *body_scope = fresh_scope(env);
+ body_scope->deferred = new(deferral_t, .defer_env=env,
+ .block=FakeAST(InlineCCode, .code="pthread_mutex_unlock(&mutexed->mutex);"),
+ .next=body_scope->deferred);
+ if (held->tag == Var) {
+ CORD held_var = CORD_all(Match(held, Var)->name, "$held");
+ set_binding(body_scope, Match(held, Var)->name, new(binding_t, .type=Type(PointerType, .pointed=Match(held_type, MutexedType)->type, .is_stack=true),
+ .code=held_var));
+ code = CORD_all(code, compile_declaration(Type(PointerType, .pointed=Match(held_type, MutexedType)->type), held_var),
+ " = (", compile_type(Type(PointerType, .pointed=Match(held_type, MutexedType)->type)), ")mutexed->data;\n");
+ }
+ type_t *body_type = get_type(env, ast);
+ return CORD_all(code, compile_declaration(body_type, "result"), " = ", compile(body_scope, Match(ast, Holding)->body), ";\n"
+ "pthread_mutex_unlock(&mutexed->mutex);\n"
+ "result;\n})");
}
case Optional: {
ast_t *value = Match(ast, Optional)->value;
@@ -3812,6 +3874,10 @@ CORD compile_type_info(env_t *env, type_t *t)
type_t *non_optional = Match(t, OptionalType)->type;
return CORD_asprintf("Optional$info(%zu, %zu, %r)", type_size(t), type_align(t), compile_type_info(env, non_optional));
}
+ case MutexedType: {
+ type_t *mutexed = Match(t, MutexedType)->type;
+ return CORD_all("MutexedData$info(", compile_type_info(env, mutexed), ")");
+ }
case TypeInfoType: return CORD_all("Type$info(", CORD_quoted(type_to_cord(Match(t, TypeInfoType)->type)), ")");
case MemoryType: return "&Memory$info";
case VoidType: return "&Void$info";
diff --git a/docs/mutexed.md b/docs/mutexed.md
index eebf9015..97357f39 100644
--- a/docs/mutexed.md
+++ b/docs/mutexed.md
@@ -1,24 +1,34 @@
# Mutexed Data
To serve the general case of synchronizing access to shared datastructures,
-Tomo uses the `mutexed` keyword, which returns a function which can be used
-to ensure that all access to a datastructure is guarded by a mutex:
+Tomo uses the `mutexed` keyword, which allocates a mutex and some heap memory
+for a value. Access to the heap-allocated value's memory can only be obtained
+by using a `holding` block. `holding` blocks ensure that the underlying mutex
+is locked before entering the block and unlocked before leaving it (even if a
+short-circuiting control flow statement like `return` or `stop` is used). Here
+is a simple example:
```tomo
-with_nums := mutexed [10, 20, 30]
+nums := mutexed [10, 20, 30]
+
+>> nums
+= mutexed [Int]<0x12345678> : mutexed([Int])
+
+holding nums:
+ # Inside this block, the type of `nums` is `&[Int]`
+ >> nums
+ = &[10, 20, 30] : &[Int]
thread := Thread.new(func():
- with_nums(func(nums:&[Int]):
+ holding nums:
nums:insert(30)
- )
)
-with_nums(func(nums:&[Int]):
+holding nums:
nums:insert(40)
-)
thread:join()
```
-Without having a mutex guard, the code above could run into concurrency issues
-leading to data corruption.
+Without using a mutex, the code above could run into concurrency issues leading
+to data corruption.
diff --git a/parse.c b/parse.c
index 774f8e7a..93dcf508 100644
--- a/parse.c
+++ b/parse.c
@@ -52,7 +52,7 @@ int op_tightness[] = {
static const char *keywords[] = {
"yes", "xor", "while", "when", "use", "unless", "struct", "stop", "skip", "return",
- "or", "not", "no", "mutexed", "mod1", "mod", "pass", "lang", "inline", "in", "if",
+ "or", "not", "no", "mutexed", "mod1", "mod", "pass", "lang", "inline", "in", "if", "holding",
"func", "for", "extern", "enum", "else", "do", "deserialize", "defer", "and", "none",
"_min_", "_max_", NULL,
};
@@ -85,6 +85,7 @@ static type_ast_t *parse_array_type(parse_ctx_t *ctx, const char *pos);
static type_ast_t *parse_func_type(parse_ctx_t *ctx, const char *pos);
static type_ast_t *parse_non_optional_type(parse_ctx_t *ctx, const char *pos);
static type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos);
+static type_ast_t *parse_mutexed_type(parse_ctx_t *ctx, const char *pos);
static type_ast_t *parse_set_type(parse_ctx_t *ctx, const char *pos);
static type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos);
static type_ast_t *parse_type(parse_ctx_t *ctx, const char *pos);
@@ -105,6 +106,7 @@ static PARSER(parse_file_body);
static PARSER(parse_for);
static PARSER(parse_func_def);
static PARSER(parse_heap_alloc);
+static PARSER(parse_holding);
static PARSER(parse_if);
static PARSER(parse_inline_c);
static PARSER(parse_int);
@@ -635,6 +637,19 @@ type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) {
return ptr_type;
}
+type_ast_t *parse_mutexed_type(parse_ctx_t *ctx, const char *pos) {
+ const char *start = pos;
+ if (!match_word(&pos, "mutexed")) return NULL;
+ spaces(&pos);
+ if (!match(&pos, "(")) return NULL;
+ spaces(&pos);
+ type_ast_t *mutexed = expect(ctx, start, &pos, parse_type,
+ "I couldn't parse a mutexed type after this point");
+ spaces(&pos);
+ if (!match(&pos, ")")) return NULL;
+ return NewTypeAST(ctx->file, start, pos, MutexedTypeAST, .type=mutexed);
+}
+
type_ast_t *parse_type_name(parse_ctx_t *ctx, const char *pos) {
const char *start = pos;
const char *id = get_id(&pos);
@@ -661,6 +676,7 @@ type_ast_t *parse_non_optional_type(parse_ctx_t *ctx, const char *pos) {
|| (type=parse_set_type(ctx, pos))
|| (type=parse_type_name(ctx, pos))
|| (type=parse_func_type(ctx, pos))
+ || (type=parse_mutexed_type(ctx, pos))
);
if (!success && match(&pos, "(")) {
whitespace(&pos);
@@ -1271,6 +1287,15 @@ PARSER(parse_mutexed) {
return NewAST(ctx->file, start, pos, Mutexed, .value=val);
}
+PARSER(parse_holding) {
+ const char *start = pos;
+ if (!match_word(&pos, "holding")) return NULL;
+ spaces(&pos);
+ ast_t *mutexed = expect(ctx, start, &pos, parse_expr, "I expected an expression for this 'holding'");
+ ast_t *body = expect(ctx, start, &pos, parse_block, "I expected a block to be here");
+ return NewAST(ctx->file, start, pos, Holding, .mutexed=mutexed, .body=body);
+}
+
PARSER(parse_not) {
const char *start = pos;
if (!match_word(&pos, "not")) return NULL;
@@ -1950,6 +1975,7 @@ PARSER(parse_extended_expr) {
|| (expr=optional(ctx, &pos, parse_when))
|| (expr=optional(ctx, &pos, parse_repeat))
|| (expr=optional(ctx, &pos, parse_do))
+ || (expr=optional(ctx, &pos, parse_holding))
)
return expr;
diff --git a/stdlib/arrays.h b/stdlib/arrays.h
index cf1f088b..53681dc7 100644
--- a/stdlib/arrays.h
+++ b/stdlib/arrays.h
@@ -59,12 +59,12 @@
#define ARRAY_DECREF(arr) (arr).data_refcount -= ((arr).data_refcount < ARRAY_MAX_DATA_REFCOUNT)
#define ARRAY_COPY(arr) ({ ARRAY_INCREF(arr); arr; })
-#define Array$insert_value(arr, item_expr, index, padded_item_size) ({ __typeof(item_expr) item = item_expr; Array$insert(arr, &item, index, padded_item_size); })
+#define Array$insert_value(arr, item_expr, index, padded_item_size) Array$insert(arr, (__typeof(item_expr)[1]){item_expr}, index, padded_item_size)
void Array$insert(Array_t *arr, const void *item, Int_t index, int64_t padded_item_size);
void Array$insert_all(Array_t *arr, Array_t to_insert, Int_t index, int64_t padded_item_size);
void Array$remove_at(Array_t *arr, Int_t index, Int_t count, int64_t padded_item_size);
void Array$remove_item(Array_t *arr, void *item, Int_t max_removals, const TypeInfo_t *type);
-#define Array$remove_item_value(arr, item_expr, max, type) ({ __typeof(item_expr) item = item_expr; Array$remove_item(arr, &item, max, type); })
+#define Array$remove_item_value(arr, item_expr, max, type) Array$remove_item(arr, (__typeof(item_expr)[1]){item_expr}, max, type)
#define Array$pop(arr_expr, index_expr, item_type, nonnone_var, nonnone_expr, none_expr, padded_item_size) ({ \
Array_t *arr = arr_expr; \
diff --git a/stdlib/datatypes.h b/stdlib/datatypes.h
index 9c3cabc2..11ae130e 100644
--- a/stdlib/datatypes.h
+++ b/stdlib/datatypes.h
@@ -89,4 +89,9 @@ typedef struct timeval Moment_t;
typedef struct RNGState_t* RNG_t;
+typedef struct MutexedData_s {
+ pthread_mutex_t mutex;
+ void *data;
+} *MutexedData_t;
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/pointers.c b/stdlib/pointers.c
index 40348965..76e882ec 100644
--- a/stdlib/pointers.c
+++ b/stdlib/pointers.c
@@ -18,12 +18,10 @@ public Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *ty
auto ptr_info = type->PointerInfo;
if (!x) {
Text_t typename = generic_as_text(NULL, false, ptr_info.pointed);
- Text_t text;
if (colorize)
- text = Text$concat(Text("\x1b[34;1m"), Text$from_str(ptr_info.sigil), typename, Text("\x1b[m"));
+ return Text$concat(Text("\x1b[34;1m"), Text$from_str(ptr_info.sigil), typename, Text("\x1b[m"));
else
- text = Text$concat(Text$from_str(ptr_info.sigil), typename);
- return text;
+ return Text$concat(Text$from_str(ptr_info.sigil), typename);
}
const void *ptr = *(const void**)x;
if (!ptr) {
diff --git a/stdlib/stdlib.c b/stdlib/stdlib.c
index 0266e788..cb9d2213 100644
--- a/stdlib/stdlib.c
+++ b/stdlib/stdlib.c
@@ -621,34 +621,4 @@ public void sleep_num(double seconds)
nanosleep(&ts, NULL);
}
-static void use_mutexed(Closure_t fn, void *userdata)
-{
- void (*call)(void*, void*) = fn.fn;
- struct data_t {
- pthread_mutex_t mutex;
- char item[0] __attribute__ ((aligned (8)));
- };
- pthread_mutex_t *mutex = &((struct data_t*)userdata)->mutex;
- pthread_mutex_lock(mutex);
- void *mutexed_item = (void*)((struct data_t*)userdata)->item;
- call(mutexed_item, fn.userdata);
- pthread_mutex_unlock(mutex);
-}
-
-public Closure_t _mutexed(const void *item, size_t size)
-{
- struct data_t {
- pthread_mutex_t mutex;
- char item[size] __attribute__ ((aligned (8)));
- };
- struct data_t *userdata = GC_MALLOC(sizeof(struct data_t));
-
- pthread_mutex_init(&userdata->mutex, NULL);
- memcpy(userdata->item, item, size);
- return (Closure_t){
- .fn=(void*)use_mutexed,
- .userdata=(void*)userdata,
- };
-}
-
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/stdlib.h b/stdlib/stdlib.h
index c68442a6..7c9bd7e1 100644
--- a/stdlib/stdlib.h
+++ b/stdlib/stdlib.h
@@ -42,7 +42,5 @@ Closure_t spawn(Closure_t fn);
bool pop_flag(char **argv, int *i, const char *flag, Text_t *result);
void print_stack_trace(FILE *out, int start, int stop);
void sleep_num(double seconds);
-public Closure_t _mutexed(const void *item, size_t size);
-#define mutexed(item) _mutexed((__typeof(item)[1]){item}, sizeof(item))
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/threads.c b/stdlib/threads.c
index c5690111..efd23546 100644
--- a/stdlib/threads.c
+++ b/stdlib/threads.c
@@ -34,8 +34,8 @@ static void *run_thread(Closure_t *closure)
public Thread_t Thread$new(Closure_t fn)
{
Thread_t thread = new(pthread_t);
- Closure_t *doop = new(Closure_t, .fn=fn.fn, .userdata=fn.userdata);
- pthread_create(thread, NULL, (void*)run_thread, doop);
+ Closure_t *closure = new(Closure_t, .fn=fn.fn, .userdata=fn.userdata);
+ pthread_create(thread, NULL, (void*)run_thread, closure);
return thread;
}
diff --git a/stdlib/tomo.h b/stdlib/tomo.h
index 10ac3668..7ed25412 100644
--- a/stdlib/tomo.h
+++ b/stdlib/tomo.h
@@ -18,6 +18,7 @@
#include "memory.h"
#include "metamethods.h"
#include "moments.h"
+#include "mutexeddata.h"
#include "nums.h"
#include "optionals.h"
#include "paths.h"
diff --git a/stdlib/types.h b/stdlib/types.h
index 36508393..2c9abcfc 100644
--- a/stdlib/types.h
+++ b/stdlib/types.h
@@ -30,7 +30,7 @@ struct TypeInfo_s {
metamethods_t metamethods;
struct { // Anonymous tagged union for convenience
enum { OpaqueInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, TableInfo, FunctionInfo,
- OptionalInfo, TypeInfoInfo } tag;
+ OptionalInfo, MutexedDataInfo, TypeInfoInfo } tag;
union {
struct {} OpaqueInfo;
struct {
@@ -54,7 +54,7 @@ struct TypeInfo_s {
} TypeInfoInfo;
struct {
const TypeInfo_t *type;
- } OptionalInfo;
+ } OptionalInfo, MutexedDataInfo;
struct {
const char *name;
int num_tags;
diff --git a/test/optionals.tm b/test/optionals.tm
index 13a8dcae..3ef7f28e 100644
--- a/test/optionals.tm
+++ b/test/optionals.tm
@@ -67,6 +67,12 @@ func maybe_thread(should_i:Bool->Thread?):
else:
return none
+func maybe_mutexed(should_i:Bool->mutexed(Bool)?):
+ if should_i:
+ return mutexed no
+ else:
+ return none
+
func main():
>> 5?
= 5 : Int?
@@ -251,6 +257,20 @@ func main():
fail("Truthy: $nope")
else: !! Falsey: $nope
+ do:
+ !! ...
+ !! Mutexed:
+ >> yep := maybe_mutexed(yes)
+ # No "=" test here because threads use addresses in the text version
+ >> nope := maybe_mutexed(no)
+ = none : mutexed(Bool)?
+ >> if yep: >> yep
+ else: fail("Falsey: $yep")
+ >> if nope:
+ fail("Truthy: $nope")
+ else: !! Falsey: $nope
+
+
if yep := maybe_int(yes):
>> yep
= 123 : Int
diff --git a/test/threads.tm b/test/threads.tm
index 772c866f..adf7784a 100644
--- a/test/threads.tm
+++ b/test/threads.tm
@@ -2,55 +2,47 @@ enum Job(Increment(x:Int), Decrement(x:Int))
func main():
do:
- >> with_nums := mutexed [10, 20, 30]
- with_nums(func(nums:&[Int]):
+ >> nums := mutexed [10, 20, 30]
+ holding nums:
>> nums[]
= [10, 20, 30]
nums:insert(40)
- )
- with_nums(func(nums:&[Int]):
+
+ holding nums:
>> nums[]
= [10, 20, 30, 40]
- )
- with_jobs := mutexed [Job.Increment(5)]
- enqueue_job := func(job:Job):
- with_jobs(func(jobs:&[Job]): jobs:insert(job))
- dequeue_job := func():
- job := @none:Job
- with_jobs(func(jobs:&[Job]): job[] = jobs:pop(1))
- job[]
+ jobs := mutexed [Job.Increment(5)]
- with_results := mutexed [:Int]
- enqueue_result := func(result:Int):
- with_results(func(results:&[Int]): results:insert(result))
- dequeue_result := func():
- result := @none:Int
- repeat:
- with_results(func(results:&[Int]): result[] = results:pop(1))
- stop if result[]
- sleep(0.00001)
- result[]!
+ results := mutexed [:Int]
>> thread := Thread.new(func():
!! In another thread!
repeat:
- job := dequeue_job() or stop
+ job := holding jobs: (jobs:pop(1) or stop)
when job is Increment(x):
- enqueue_result(x + 1)
+ holding results: results:insert(x + 1)
is Decrement(x):
- enqueue_result(x - 1)
+ holding results: results:insert(x - 1)
)
- enqueue_job(Decrement(100))
- enqueue_job(Decrement(200))
- enqueue_job(Decrement(300))
- enqueue_job(Decrement(400))
- enqueue_job(Decrement(500))
- enqueue_job(Decrement(600))
+ holding jobs:
+ jobs:insert(Decrement(100))
+ jobs:insert(Decrement(200))
+ jobs:insert(Decrement(300))
+ jobs:insert(Decrement(400))
+ jobs:insert(Decrement(500))
+ jobs:insert(Decrement(600))
+ jobs:insert(Increment(1000))
- >> enqueue_job(Increment(1000))
+ dequeue_result := func():
+ result := none:Int
+ repeat:
+ result = (holding results: results:pop(1))
+ stop if result
+ sleep(0.00001)
+ return result!
>> dequeue_result()
= 6
diff --git a/typecheck.c b/typecheck.c
index 5bc33181..fbee0d07 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -123,6 +123,13 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
code_err(ast, "Nested optional types are not currently supported");
return Type(OptionalType, .type=t);
}
+ case MutexedTypeAST: {
+ type_ast_t *mutexed = Match(ast, MutexedTypeAST)->type;
+ type_t *t = parse_type_ast(env, mutexed);
+ if (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType)
+ code_err(ast, "Mutexed %T types are not supported.", t);
+ return Type(MutexedType, .type=t);
+ }
case UnknownTypeAST: code_err(ast, "I don't know how to get this type");
}
#pragma GCC diagnostic pop
@@ -952,9 +959,18 @@ type_t *get_type(env_t *env, ast_t *ast)
}
case Mutexed: {
type_t *item_type = get_type(env, Match(ast, Mutexed)->value);
- type_t *user_thunk = Type(ClosureType, Type(FunctionType, .args=new(arg_t, .name="object", .type=Type(PointerType, .pointed=item_type, .is_stack=true)),
- .ret=Type(VoidType)));
- return Type(ClosureType, Type(FunctionType, .args=new(arg_t, .name="fn", .type=user_thunk), .ret=Type(VoidType)));
+ return Type(MutexedType, .type=item_type);
+ }
+ case Holding: {
+ ast_t *held = Match(ast, Holding)->mutexed;
+ type_t *held_type = get_type(env, held);
+ if (held_type->tag != MutexedType)
+ code_err(held, "This is a %t, not a mutexed value", held_type);
+ if (held->tag == Var) {
+ env = fresh_scope(env);
+ set_binding(env, Match(held, Var)->name, new(binding_t, .type=Type(PointerType, .pointed=Match(held_type, MutexedType)->type, .is_stack=true)));
+ }
+ return get_type(env, Match(ast, Holding)->body);
}
case BinaryOp: {
auto binop = Match(ast, BinaryOp);
diff --git a/types.c b/types.c
index 92a3c270..cee1db9b 100644
--- a/types.c
+++ b/types.c
@@ -85,6 +85,13 @@ CORD type_to_cord(type_t *t) {
else
return "(Unknown optional type)";
}
+ case MutexedType: {
+ type_t *opt = Match(t, MutexedType)->type;
+ if (opt)
+ return CORD_all("mutexed ", type_to_cord(opt));
+ else
+ return "(Unknown optional type)";
+ }
case TypeInfoType: {
return CORD_all("Type$info(", Match(t, TypeInfoType)->name, ")");
}
@@ -246,6 +253,7 @@ PUREFUNC bool has_heap_memory(type_t *t)
case SetType: return true;
case PointerType: return true;
case OptionalType: return has_heap_memory(Match(t, OptionalType)->type);
+ case MutexedType: return true;
case BigIntType: return true;
case StructType: {
for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
@@ -270,6 +278,7 @@ PUREFUNC bool has_stack_memory(type_t *t)
switch (t->tag) {
case PointerType: return Match(t, PointerType)->is_stack;
case OptionalType: return has_stack_memory(Match(t, OptionalType)->type);
+ case MutexedType: return has_stack_memory(Match(t, MutexedType)->type);
default: return false;
}
}
@@ -498,6 +507,7 @@ PUREFUNC size_t type_size(type_t *t)
case FunctionType: return sizeof(void*);
case ClosureType: return sizeof(struct {void *fn, *userdata;});
case PointerType: return sizeof(void*);
+ case MutexedType: return sizeof(MutexedData_t);
case OptionalType: {
type_t *nonnull = Match(t, OptionalType)->type;
switch (nonnull->tag) {
@@ -582,6 +592,7 @@ PUREFUNC size_t type_align(type_t *t)
case FunctionType: return __alignof__(void*);
case ClosureType: return __alignof__(struct {void *fn, *userdata;});
case PointerType: return __alignof__(void*);
+ case MutexedType: return __alignof__(MutexedData_t);
case OptionalType: {
type_t *nonnull = Match(t, OptionalType)->type;
switch (nonnull->tag) {
diff --git a/types.h b/types.h
index 1ddcc627..882e7e2c 100644
--- a/types.h
+++ b/types.h
@@ -60,6 +60,7 @@ struct type_s {
EnumType,
OptionalType,
TypeInfoType,
+ MutexedType,
ModuleType,
} tag;
@@ -117,7 +118,7 @@ struct type_s {
} EnumType;
struct {
type_t *type;
- } OptionalType;
+ } OptionalType, MutexedType;
struct {
const char *name;
type_t *type;