diff --git a/api/channels.md b/api/channels.md index ca7ce17..88cb46f 100644 --- a/api/channels.md +++ b/api/channels.md @@ -31,13 +31,15 @@ Adds an item to the channel. **Usage:** ```markdown -give(channel:|T|, item: T) -> Void +give(channel:|T|, item: T, where: Where = Where.End) -> Void ``` **Parameters:** - `channel`: The channel to which the item will be added. - `item`: The item to add to the channel. +- `where`: Where to put the item (at the `Start` or `End` of the queue). + `Anywhere` defaults to `End`. **Returns:** Nothing. @@ -56,13 +58,15 @@ Adds multiple items to the channel. **Usage:** ```markdown -give_all(channel:|T|, items: [T]) -> Void +give_all(channel:|T|, items: [T], where: Where = Where.End) -> Void ``` **Parameters:** - `channel`: The channel to which the items will be added. - `items`: The array of items to add to the channel. +- `where`: Where to put the items (at the `Start` or `End` of the queue). + `Anywhere` defaults to `End`. **Returns:** Nothing. @@ -81,12 +85,14 @@ Removes and returns an item from the channel. If the channel is empty, it waits **Usage:** ```markdown -get(channel:|T|) -> T +get(channel:|T|, where: Where = Where.Start) -> T ``` **Parameters:** - `channel`: The channel from which to remove an item. +- `where`: Where to get the item from (from the `Start` or `End` of the queue). + `Anywhere` defaults to `Start`. **Returns:** The item removed from the channel. @@ -107,12 +113,14 @@ it. If the channel is empty, it waits until an item is available. **Usage:** ```markdown -peek(channel:|T|) -> T +peek(channel:|T|, where: Where = Where.Start) -> T ``` **Parameters:** - `channel`: The channel from which to remove an item. +- `where`: Which end of the channel to peek from (the `Start` or `End`). + `Anywhere` defaults to `Start`. **Returns:** The item removed from the channel. diff --git a/builtins/channel.c b/builtins/channel.c index 9360bcf..c2e2cf8 100644 --- a/builtins/channel.c +++ b/builtins/channel.c @@ -17,6 +17,7 @@ #include "integers.h" #include "types.h" #include "util.h" +#include "where.h" public channel_t *Channel$new(Int_t max_size) { @@ -30,50 +31,54 @@ public channel_t *Channel$new(Int_t max_size) return channel; } -public void Channel$give(channel_t *channel, const void *item, int64_t padded_item_size) +public void Channel$give(channel_t *channel, const void *item, Where_t where, int64_t padded_item_size) { (void)pthread_mutex_lock(&channel->mutex); while (channel->items.length >= channel->max_size) pthread_cond_wait(&channel->cond, &channel->mutex); - Array$insert(&channel->items, item, I(0), padded_item_size); + Int_t index = (where.tag == $tag$Where$Start) ? I(1) : I(0); + Array$insert(&channel->items, item, index, padded_item_size); (void)pthread_mutex_unlock(&channel->mutex); (void)pthread_cond_signal(&channel->cond); } -public void Channel$give_all(channel_t *channel, array_t to_give, int64_t padded_item_size) +public void Channel$give_all(channel_t *channel, array_t to_give, Where_t where, int64_t padded_item_size) { if (to_give.length == 0) return; (void)pthread_mutex_lock(&channel->mutex); + Int_t index = (where.tag == $tag$Where$Start) ? I(1) : I(0); if (channel->items.length + to_give.length >= channel->max_size) { for (int64_t i = 0; i < to_give.length; i++) { while (channel->items.length >= channel->max_size) pthread_cond_wait(&channel->cond, &channel->mutex); - Array$insert(&channel->items, to_give.data + i*to_give.stride, I(0), padded_item_size); + Array$insert(&channel->items, to_give.data + i*to_give.stride, index, padded_item_size); } } else { - Array$insert_all(&channel->items, to_give, I(0), padded_item_size); + Array$insert_all(&channel->items, to_give, index, padded_item_size); } (void)pthread_mutex_unlock(&channel->mutex); (void)pthread_cond_signal(&channel->cond); } -public void Channel$get(channel_t *channel, void *out, int64_t item_size, int64_t padded_item_size) +public void Channel$get(channel_t *channel, void *out, Where_t where, int64_t item_size, int64_t padded_item_size) { (void)pthread_mutex_lock(&channel->mutex); while (channel->items.length == 0) pthread_cond_wait(&channel->cond, &channel->mutex); memcpy(out, channel->items.data, item_size); - Array$remove_at(&channel->items, I(1), I(1), padded_item_size); + Int_t index = (where.tag == $tag$Where$End) ? I(0) : I(1); + Array$remove_at(&channel->items, index, I(1), padded_item_size); (void)pthread_mutex_unlock(&channel->mutex); (void)pthread_cond_signal(&channel->cond); } -public void Channel$peek(channel_t *channel, void *out, int64_t item_size) +public void Channel$peek(channel_t *channel, void *out, Where_t where, int64_t item_size) { (void)pthread_mutex_lock(&channel->mutex); while (channel->items.length == 0) pthread_cond_wait(&channel->cond, &channel->mutex); - memcpy(out, channel->items.data, item_size); + int64_t index = (where.tag == $tag$Where$End) ? channel->items.length-1 : 0; + memcpy(out, channel->items.data + channel->items.stride*index, item_size); (void)pthread_mutex_unlock(&channel->mutex); (void)pthread_cond_signal(&channel->cond); } diff --git a/builtins/channel.h b/builtins/channel.h index b828237..241e7ec 100644 --- a/builtins/channel.h +++ b/builtins/channel.h @@ -8,15 +8,18 @@ #include "datatypes.h" #include "types.h" #include "util.h" +#include "where.h" channel_t *Channel$new(Int_t max_size); -void Channel$give(channel_t *channel, const void *item, int64_t padded_item_size); -#define Channel$give_value(channel, item, padded_item_size) ({ __typeof(item) _item = item; Channel$give(channel, &_item, padded_item_size); }) -void Channel$give_all(channel_t *channel, array_t to_give, int64_t padded_item_size); -void Channel$get(channel_t *channel, void *out, int64_t item_size, int64_t padded_item_size); -#define Channel$get_value(channel, t, padded_item_size) ({ t _val; Channel$get(channel, &_val, sizeof(t), padded_item_size); _val; }) -void Channel$peek(channel_t *channel, void *out, int64_t item_size); -#define Channel$peek_value(channel, t) ({ t _val; Channel$peek(channel, &_val, sizeof(t)); _val; }) +void Channel$give(channel_t *channel, const void *item, Where_t where, int64_t padded_item_size); +#define Channel$give_value(channel, item, where, padded_item_size) \ + ({ __typeof(item) _item = item; Channel$give(channel, &_item, where, padded_item_size); }) +void Channel$give_all(channel_t *channel, array_t to_give, Where_t where, int64_t padded_item_size); +void Channel$get(channel_t *channel, void *out, Where_t where, int64_t item_size, int64_t padded_item_size); +#define Channel$get_value(channel, where, t, padded_item_size) \ + ({ t _val; Channel$get(channel, &_val, where, sizeof(t), padded_item_size); _val; }) +void Channel$peek(channel_t *channel, void *out, Where_t where, int64_t item_size); +#define Channel$peek_value(channel, where, t) ({ t _val; Channel$peek(channel, &_val, where, sizeof(t)); _val; }) void Channel$clear(channel_t *channel); array_t Channel$view(channel_t *channel); uint32_t Channel$hash(const channel_t **channel, const TypeInfo *type); diff --git a/builtins/text.c b/builtins/text.c index 22784a3..d9e15d0 100644 --- a/builtins/text.c +++ b/builtins/text.c @@ -171,12 +171,12 @@ public bool Text$has(CORD str, CORD target, Where_t where) if (target_norm_len > str_norm_len) return false; bool ret; - if (where.$tag == $tag$Where$Start) { + if (where.tag == $tag$Where$Start) { ret = (u8_strncmp(str_normalized, target_normalized, target_norm_len-1) == 0); - } else if (where.$tag == $tag$Where$End) { + } else if (where.tag == $tag$Where$End) { ret = (u8_strcmp(str_normalized + str_norm_len - target_norm_len, target_normalized) == 0); } else { - assert(where.$tag == $tag$Where$Anywhere); + assert(where.tag == $tag$Where$Anywhere); ret = (u8_strstr(str_normalized, target_normalized) != NULL); } @@ -191,11 +191,11 @@ public CORD Text$without(CORD str, CORD target, Where_t where) size_t target_len = CORD_len(target); size_t str_len = CORD_len(str); - if (where.$tag == $tag$Where$Start) { + if (where.tag == $tag$Where$Start) { if (CORD_ncmp(str, 0, target, 0, target_len) == 0) return CORD_substr(str, target_len, str_len - target_len); return str; - } else if (where.$tag == $tag$Where$End) { + } else if (where.tag == $tag$Where$End) { if (CORD_ncmp(str, str_len-target_len, target, 0, target_len) == 0) return CORD_substr(str, 0, str_len - target_len); return str; @@ -222,10 +222,10 @@ public CORD Text$trimmed(CORD str, CORD skip, Where_t where) const uint8_t *ustr = (const uint8_t*)CORD_to_const_char_star(str); const uint8_t *uskip = (const uint8_t*)CORD_to_const_char_star(skip); // TODO: implement proper reverse iteration with u8_prev() - if (where.$tag == $tag$Where$Start) { + if (where.tag == $tag$Where$Start) { size_t span = u8_strspn(ustr, uskip); return (CORD)ustr + span; - } else if (where.$tag == $tag$Where$End) { + } else if (where.tag == $tag$Where$End) { size_t len = u8_strlen(ustr); const uint8_t *back = ustr + len; size_t back_span = 0; diff --git a/builtins/where.c b/builtins/where.c index 5447d8c..b69166e 100644 --- a/builtins/where.c +++ b/builtins/where.c @@ -31,7 +31,7 @@ static CORD Where$as_text(Where_t *obj, bool use_color) { if (!obj) return "Where"; - switch (obj->$tag) { + switch (obj->tag) { case $tag$Where$Anywhere: return use_color ? "\x1b[36;1mWhere.Anywhere\x1b[m" : "Where.Anywhere"; case $tag$Where$Start: diff --git a/builtins/where.h b/builtins/where.h index 75de22a..cfb45c6 100644 --- a/builtins/where.h +++ b/builtins/where.h @@ -22,7 +22,7 @@ struct Where$Anywhere_s {}; struct Where$Start_s {}; struct Where$End_s {}; struct Where_s { - enum { $tag$Where$Anywhere = 0, $tag$Where$Start = 1, $tag$Where$End = 2 } $tag; + enum { $tag$Where$Anywhere = 0, $tag$Where$Start = 1, $tag$Where$End = 2 } tag; union { Where$Anywhere_t Anywhere; Where$Start_t Start; diff --git a/compile.c b/compile.c index 477913c..e674d8f 100644 --- a/compile.c +++ b/compile.c @@ -2363,24 +2363,30 @@ CORD compile(env_t *env, ast_t *ast) case ChannelType: { type_t *item_t = Match(self_value_t, ChannelType)->item_type; CORD padded_item_size = CORD_asprintf("%ld", padded_type_size(item_t)); + arg_t *where_default_end = new(arg_t, .name="where", .type=WHERE_TYPE, + .default_val=FakeAST(FieldAccess, .fielded=FakeAST(Var, "Where"), .field="End")); + arg_t *where_default_start = new(arg_t, .name="where", .type=WHERE_TYPE, + .default_val=FakeAST(FieldAccess, .fielded=FakeAST(Var, "Where"), .field="Start")); if (streq(call->name, "give")) { CORD self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t); + arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, .next=where_default_end); return CORD_all("Channel$give_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "give_all")) { CORD self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="to_give", .type=Type(ArrayType, .item_type=item_t)); + arg_t *arg_spec = new(arg_t, .name="to_give", .type=Type(ArrayType, .item_type=item_t), .next=where_default_end); return CORD_all("Channel$give_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "get")) { CORD self = compile_to_pointer_depth(env, call->self, 0, false); - (void)compile_arguments(env, ast, NULL, call->args); - return CORD_all("Channel$get_value(", self, ", ", compile_type(item_t), ", ", padded_item_size, ")"); + arg_t *arg_spec = where_default_start; + return CORD_all("Channel$get_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), + ", ", compile_type(item_t), ", ", padded_item_size, ")"); } else if (streq(call->name, "peek")) { CORD self = compile_to_pointer_depth(env, call->self, 0, false); - (void)compile_arguments(env, ast, NULL, call->args); - return CORD_all("Channel$peek_value(", self, ", ", compile_type(item_t), ")"); + arg_t *arg_spec = where_default_start; + return CORD_all("Channel$peek_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), + ", ", compile_type(item_t), ")"); } else if (streq(call->name, "clear")) { CORD self = compile_to_pointer_depth(env, call->self, 0, false); (void)compile_arguments(env, ast, NULL, call->args); diff --git a/environment.c b/environment.c index a54cf5d..4345770 100644 --- a/environment.c +++ b/environment.c @@ -11,6 +11,7 @@ type_t *TEXT_TYPE = NULL; type_t *RANGE_TYPE = NULL; +type_t *WHERE_TYPE = NULL; public type_t *THREAD_TYPE = NULL; env_t *new_compilation_unit(CORD *libname) @@ -45,19 +46,18 @@ env_t *new_compilation_unit(CORD *libname) const char *name, *code, *type_str; } ns_entry_t; - type_t *where; { env_t *where_env = namespace_env(env, "Where"); type_t *anywhere = Type(StructType, .name="Anywhere"); type_t *start = Type(StructType, .name="Start"); type_t *end = Type(StructType, .name="End"); - where = Type(EnumType, .name="Where", .env=where_env, - .tags=new(tag_t, .name="Anywhere", .tag_value=0, .type=anywhere, - .next=new(tag_t, .name="Start", .tag_value=0, .type=start, - .next=new(tag_t, .name="End", .tag_value=0, .type=end)))); - set_binding(where_env, "Anywhere", new(binding_t, .type=where, .code="Where$tagged$Anywhere")); - set_binding(where_env, "Start", new(binding_t, .type=where, .code="Where$tagged$Start")); - set_binding(where_env, "End", new(binding_t, .type=where, .code="Where$tagged$End")); + WHERE_TYPE = Type(EnumType, .name="Where", .env=where_env, + .tags=new(tag_t, .name="Anywhere", .tag_value=0, .type=anywhere, + .next=new(tag_t, .name="Start", .tag_value=0, .type=start, + .next=new(tag_t, .name="End", .tag_value=0, .type=end)))); + set_binding(where_env, "Anywhere", new(binding_t, .type=WHERE_TYPE, .code="Where$tagged$Anywhere")); + set_binding(where_env, "Start", new(binding_t, .type=WHERE_TYPE, .code="Where$tagged$Start")); + set_binding(where_env, "End", new(binding_t, .type=WHERE_TYPE, .code="Where$tagged$End")); } { @@ -240,7 +240,7 @@ env_t *new_compilation_unit(CORD *libname) #undef F2 #undef F #undef C - {"Where", where, "Where_t", "Where", {}}, + {"Where", WHERE_TYPE, "Where_t", "Where", {}}, {"Range", RANGE_TYPE, "Range_t", "Range", TypedArray(ns_entry_t, {"reversed", "Range$reversed", "func(range:Range)->Range"}, {"by", "Range$by", "func(range:Range, step:Int)->Range"}, diff --git a/environment.h b/environment.h index edaff80..0f5d34e 100644 --- a/environment.h +++ b/environment.h @@ -77,5 +77,6 @@ binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name); extern type_t *TEXT_TYPE; extern type_t *RANGE_TYPE; extern type_t *THREAD_TYPE; +extern type_t *WHERE_TYPE; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/test/threads.tm b/test/threads.tm index f7acd01..09ec7dc 100644 --- a/test/threads.tm +++ b/test/threads.tm @@ -1,6 +1,24 @@ enum Job(Increment(x:Int), Decrement(x:Int)) func main(): + + do: + >> channel := |:Int| + >> channel:give(10) + >> channel:give(20) + >> channel:give(30) + >> channel:view() + = [10, 20, 30] + >> channel:peek() + = 10 + >> channel:peek(End) + = 30 + + >> channel:give(-10, Start) + >> channel:view() + = [-10, 10, 20, 30] + + jobs := |:Job; max_size=2| >> jobs:give(Increment(5)) >> jobs:peek()