diff --git a/vlib/sync/channel_array_mut_test.v b/vlib/sync/channel_array_mut_test.v new file mode 100644 index 0000000000..0527e61a66 --- /dev/null +++ b/vlib/sync/channel_array_mut_test.v @@ -0,0 +1,36 @@ +import sync + +const ( + num_iterations = 10000 +) + +struct St { +mut: + n int +} + +// this function gets an array of channels for `St` references +fn do_rec_calc_send(chs []chan mut St) { + mut s := St{} + for { + if !(&sync.Channel(chs[0])).pop(&s) { + break + } + s.n++ + (&sync.Channel(chs[1])).push(&s) + } +} + +fn test_channel_array_mut() { + mut chs := [chan mut St{cap: 1}, chan mut St{}] + go do_rec_calc_send(chs) + mut t := St{ + n: 100 + } + for _ in 0 .. num_iterations { + (&sync.Channel(chs[0])).push(&t) + (&sync.Channel(chs[1])).pop(&t) + } + (&sync.Channel(chs[0])).close() + assert t.n == 100 + num_iterations +} diff --git a/vlib/sync/channel_select_2_test.v b/vlib/sync/channel_select_2_test.v new file mode 100644 index 0000000000..abea5f081f --- /dev/null +++ b/vlib/sync/channel_select_2_test.v @@ -0,0 +1,76 @@ +import sync + +fn do_rec_i64(ch chan i64) { + mut sum := i64(0) + for _ in 0 .. 300 { + mut a := i64(0) + (&sync.Channel(ch)).pop(&a) + sum += a + } + assert sum == 300 * (300 - 1) / 2 +} + +fn do_send_int(ch chan int) { + for i in 0 .. 300 { + (&sync.Channel(ch)).push(&i) + } +} + +fn do_send_byte(ch chan byte) { + for i in 0 .. 300 { + ii := byte(i) + (&sync.Channel(ch)).push(&ii) + } +} + +fn do_send_i64(mut ch sync.Channel) { + for i in 0 .. 300 { + ii := i64(i) + ch.push(&ii) + } +} + +fn test_select() { + chi := chan int{} + mut chl := sync.new_channel(1) + chb := chan byte{cap: 10} + recch := chan i64{cap: 0} + go do_rec_i64(recch) + go do_send_int(chi) + go do_send_byte(chb) + go do_send_i64(mut chl) + mut channels := [&sync.Channel(chi), &sync.Channel(recch), chl, &sync.Channel(chb)] + directions := [sync.Direction.pop, .push, .pop, .pop] + mut sum := i64(0) + mut rl := i64(0) + mut ri := int(0) + mut rb := byte(0) + mut sl := i64(0) + mut objs := [voidptr(&ri), &sl, &rl, &rb] + for _ in 0 .. 1200 { + idx := sync.channel_select(mut channels, directions, mut objs, -1) + match idx { + 0 { + sum += ri + } + 1 { + sl++ + } + 2 { + sum += rl + } + 3 { + sum += rb + } + else { + println('got $idx (timeout)') + } + } + } + // Use Gauß' formula for the first 2 contributions + expected_sum := 2 * (300 * (300 - 1) / 2) + + // the 3rd contribution is `byte` and must be seen modulo 256 + 256 * (256 - 1) / 2 + + 44 * (44 - 1) / 2 + assert sum == expected_sum +} diff --git a/vlib/sync/channels.v b/vlib/sync/channels.v index 9ca291ccb1..755b0f7093 100644 --- a/vlib/sync/channels.v +++ b/vlib/sync/channels.v @@ -111,6 +111,10 @@ pub: pub fn new_channel(n u32) &Channel { st := sizeof(T) + return new_channel_st(n, st) +} + +fn new_channel_st(n u32, st u32) &Channel { return &Channel{ writesem: new_semaphore_init(if n > 0 { n + 1 } else { 1 }) readsem: new_semaphore_init(if n > 0 { u32(0) } else { 1 }) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index dec7c15982..4d30b7e5b2 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -10,7 +10,7 @@ import v.errors pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr | - CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | + CharLiteral | ChanInit | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | TypeOf | UnsafeExpr @@ -751,6 +751,16 @@ pub mut: typ table.Type } +pub struct ChanInit { +pub: + pos token.Position + cap_expr Expr + has_cap bool +pub mut: + typ table.Type + elem_type table.Type +} + pub struct MapInit { pub: pos token.Position diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e1e33ac1da..7d8a8eea89 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2345,6 +2345,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { ast.CallExpr { return c.call_expr(mut node) } + ast.ChanInit { + return c.chan_init(mut node) + } ast.CharLiteral { return table.byte_type } @@ -3198,6 +3201,17 @@ pub fn (mut c Checker) enum_val(mut node ast.EnumVal) table.Type { return typ } +pub fn (mut c Checker) chan_init(mut node ast.ChanInit) table.Type { + if node.typ != 0 { + info := c.table.get_type_symbol(node.typ).chan_info() + node.elem_type = info.elem_type + return node.typ + } else { + c.error('`chan` of unknown type', node.pos) + return node.typ + } +} + pub fn (mut c Checker) map_init(mut node ast.MapInit) table.Type { // `x ;= map[string]string` - set in parser if node.typ != 0 { diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 07d6c20f22..f91d868e49 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -799,6 +799,16 @@ pub fn (mut f Fmt) expr(node ast.Expr) { ast.CallExpr { f.call_expr(node) } + ast.ChanInit { + f.write('chan[') + f.write(f.type_to_str(node.elem_type)) + f.write(']{') + if node.has_cap { + f.write('cap: ') + f.expr(node.cap_expr) + } + f.write('}') + } ast.CharLiteral { f.write('`$node.val`') } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index e727049d3a..1f91206b7a 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -497,6 +497,10 @@ typedef struct { .interface_ { g.type_definitions.writeln('typedef _Interface ${c_name(typ.name)};') } + .chan { + styp := util.no_dots(typ.name) + g.type_definitions.writeln('typedef chan $styp;') + } .map { styp := util.no_dots(typ.name) g.type_definitions.writeln('typedef map $styp;') @@ -1787,6 +1791,18 @@ fn (mut g Gen) expr(node ast.Expr) { g.write('))') } } + ast.ChanInit { + elem_typ_str := g.typ(node.elem_type) + g.write('sync__new_channel_st(') + if node.has_cap { + g.expr(node.cap_expr) + } else { + g.write('0') + } + g.write(', sizeof(') + g.write(elem_typ_str) + g.write('))') + } ast.CharLiteral { g.write("'$node.val'") } diff --git a/vlib/v/gen/cheaders.v b/vlib/v/gen/cheaders.v index ab2204beb7..be88c8b0b7 100644 --- a/vlib/v/gen/cheaders.v +++ b/vlib/v/gen/cheaders.v @@ -415,6 +415,8 @@ typedef map map_string; typedef byte array_fixed_byte_300 [300]; typedef byte array_fixed_byte_400 [400]; +typedef struct sync__Channel* chan; + #ifndef __cplusplus #ifndef bool typedef int bool; diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index d8e3511109..78571f12a2 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -232,6 +232,9 @@ pub fn (mut g JsGen) typ(t table.Type) string { info := sym.info as table.ArrayFixed styp = g.typ(info.elem_type) + '[]' } + .chan { + styp = 'chan' + } // 'map[string]int' => 'Map' .map { info := sym.info as table.Map @@ -533,6 +536,9 @@ fn (mut g JsGen) expr(node ast.Expr) { ast.CallExpr { g.gen_call_expr(node) } + ast.ChanInit { + // TODO + } ast.CastExpr { // JS has no types, so no need to cast // Just write the expression inside diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 539c4d93e7..37c8a4b4f3 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -51,6 +51,13 @@ pub fn (mut p Parser) parse_map_type() table.Type { return table.new_type(idx) } +pub fn (mut p Parser) parse_chan_type() table.Type { + p.next() + elem_type := p.parse_type() + idx := p.table.find_or_register_chan(elem_type) + return table.new_type(idx) +} + pub fn (mut p Parser) parse_multi_return_type() table.Type { p.check(.lpar) mut mr_types := []table.Type{} @@ -211,6 +218,9 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr, check_dot if name == 'map' { return p.parse_map_type() } + if name == 'chan' { + return p.parse_chan_type() + } defer { p.next() } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index cd3da27559..413d335a97 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -958,6 +958,47 @@ pub fn (mut p Parser) name_expr() ast.Expr { typ: map_type } } + // `chan typ{...}` + if p.tok.lit == 'chan' { + first_pos := p.tok.position() + mut last_pos := p.tok.position() + chan_type := p.parse_chan_type() + mut has_cap := false + mut cap_expr := ast.Expr{} + p.check(.lcbr) + if p.tok.kind == .rcbr { + last_pos = p.tok.position() + p.next() + } else { + key := p.check_name() + p.check(.colon) + match key { + 'cap' { + has_cap = true + cap_expr = p.expr(0) + } + 'len', 'init' { + p.error('`$key` cannot be initialized for `chan`. Did you mean `cap`?') + } + else { + p.error('wrong field `$key`, expecting `cap`') + } + } + last_pos = p.tok.position() + p.check(.rcbr) + } + pos := token.Position{ + line_nr: first_pos.line_nr + pos: first_pos.pos + len: last_pos.pos - first_pos.pos + last_pos.len + } + return ast.ChanInit{ + pos: pos + has_cap: has_cap + cap_expr: cap_expr + typ: chan_type + } + } // Raw string (`s := r'hello \n ') if p.tok.lit in ['r', 'c', 'js'] && p.peek_tok.kind == .string && !p.inside_str_interp { return p.string_expr() diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index 608ae4e1c0..2bcb233ab2 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -15,7 +15,7 @@ import strings pub type Type int -pub type TypeInfo = Alias | Array | ArrayFixed | Enum | FnType | GenericStructInst | Interface | +pub type TypeInfo = Alias | Array | ArrayFixed | Chan | Enum | FnType | GenericStructInst | Interface | Map | MultiReturn | Struct | SumType pub enum Language { @@ -253,10 +253,11 @@ pub const ( ustring_type_idx = 19 array_type_idx = 20 map_type_idx = 21 - any_type_idx = 22 + chan_type_idx = 22 + any_type_idx = 23 // t_type_idx = 23 - any_flt_type_idx = 23 - any_int_type_idx = 24 + any_flt_type_idx = 24 + any_int_type_idx = 25 ) pub const ( @@ -293,6 +294,7 @@ pub const ( ustring_type = new_type(ustring_type_idx) array_type = new_type(array_type_idx) map_type = new_type(map_type_idx) + chan_type = new_type(chan_type_idx) any_type = new_type(any_type_idx) // t_type = new_type(t_type_idx) any_flt_type = new_type(any_flt_type_idx) @@ -302,7 +304,7 @@ pub const ( pub const ( builtin_type_names = ['void', 'voidptr', 'charptr', 'byteptr', 'i8', 'i16', 'int', 'i64', 'u16', 'u32', 'u64', 'any_int', 'f32', 'f64', 'any_float', 'string', 'ustring', 'char', 'byte', 'bool', - 'none', 'array', 'array_fixed', 'map', 'any', 'struct', 'mapnode', 'size_t'] + 'none', 'array', 'array_fixed', 'map', 'chan', 'any', 'struct', 'mapnode', 'size_t'] ) pub struct MultiReturn { @@ -342,6 +344,7 @@ pub enum Kind { array array_fixed map + chan any struct_ generic_struct_inst @@ -391,6 +394,14 @@ pub fn (t &TypeSymbol) array_fixed_info() ArrayFixed { } } +[inline] +pub fn (t &TypeSymbol) chan_info() Chan { + match t.info { + Chan { return *it } + else { panic('TypeSymbol.chan_info(): no chan info for type: $t.name') } + } +} + [inline] pub fn (t &TypeSymbol) map_info() Map { match t.info { @@ -524,6 +535,11 @@ pub fn (mut t Table) register_builtin_type_symbols() { name: 'map' mod: 'builtin' }) + t.register_type_symbol({ + kind: .chan + name: 'chan' + mod: 'builtin' + }) t.register_type_symbol({ kind: .any name: 'any' @@ -616,6 +632,7 @@ pub fn (k Kind) str() string { .array { 'array' } .array_fixed { 'array_fixed' } .map { 'map' } + .chan { 'chan' } .multi_return { 'multi_return' } .sum_type { 'sum_type' } .alias { 'alias' } @@ -708,6 +725,11 @@ pub mut: elem_type Type } +pub struct Chan { +pub mut: + elem_type Type +} + pub struct Map { pub mut: key_type Type diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index f4d785774d..03f3c5d2a9 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -320,6 +320,13 @@ pub fn (t &Table) array_fixed_name(elem_type Type, size, nr_dims int) string { } } +[inline] +pub fn (t &Table) chan_name(elem_type Type) string { + elem_type_sym := t.get_type_symbol(elem_type) + suffix := if elem_type.is_ptr() { '_ptr' } else { '' } + return 'chan_$elem_type_sym.name' + suffix +} + [inline] pub fn (t &Table) map_name(key_type, value_type Type) string { key_type_sym := t.get_type_symbol(key_type) @@ -329,6 +336,25 @@ pub fn (t &Table) map_name(key_type, value_type Type) string { // return 'map_${value_type_sym.name}' + suffix } +pub fn (mut t Table) find_or_register_chan(elem_type Type) int { + name := t.chan_name(elem_type) + // existing + existing_idx := t.type_idxs[name] + if existing_idx > 0 { + return existing_idx + } + // register + chan_typ := TypeSymbol{ + parent_idx: chan_type_idx + kind: .chan + name: name + info: Chan{ + elem_type: elem_type + } + } + return t.register_type_symbol(chan_typ) +} + pub fn (mut t Table) find_or_register_map(key_type, value_type Type) int { name := t.map_name(key_type, value_type) // existing