all: extend `wait()` method to array of `thread` handles (#8840)
parent
ac1c4932e7
commit
5306b2788c
cmd/tools
vlib/v
checker
tests
|
@ -96,6 +96,7 @@ const (
|
|||
'vlib/v/tests/generic_chan_test.v',
|
||||
'vlib/v/tests/generics_method_test.v',
|
||||
'vlib/v/tests/generics_test.v',
|
||||
'vlib/v/tests/go_array_wait_test.v',
|
||||
'vlib/v/tests/go_call_generic_fn_test.v',
|
||||
'vlib/v/tests/go_wait_2_test.v',
|
||||
'vlib/v/tests/interface_edge_cases/assign_to_interface_field_test.v',
|
||||
|
|
|
@ -29,7 +29,7 @@ const (
|
|||
valid_comp_if_platforms = ['amd64', 'aarch64', 'x64', 'x32', 'little_endian', 'big_endian']
|
||||
valid_comp_if_other = ['js', 'debug', 'test', 'glibc', 'prealloc', 'no_bounds_checking']
|
||||
array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort',
|
||||
'contains', 'index']
|
||||
'contains', 'index', 'wait']
|
||||
)
|
||||
|
||||
pub struct Checker {
|
||||
|
@ -1306,10 +1306,11 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
is_filter_map := method_name in ['filter', 'map']
|
||||
is_sort := method_name == 'sort'
|
||||
is_slice := method_name == 'slice'
|
||||
is_wait := method_name == 'wait'
|
||||
if is_slice && !c.is_builtin_mod {
|
||||
c.error('.slice() is a private method, use `x[start..end]` instead', call_expr.pos)
|
||||
}
|
||||
if is_filter_map || is_sort {
|
||||
if is_filter_map || is_sort || is_wait {
|
||||
array_info := left_type_sym.info as table.Array
|
||||
if is_filter_map {
|
||||
// position of `it` doesn't matter
|
||||
|
@ -1328,6 +1329,19 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
}
|
||||
}
|
||||
elem_typ = array_info.elem_type
|
||||
if is_wait {
|
||||
elem_sym := c.table.get_type_symbol(elem_typ)
|
||||
if elem_sym.kind == .thread {
|
||||
if call_expr.args.len != 0 {
|
||||
c.error('wait() does not have any arguments', call_expr.args[0].pos)
|
||||
}
|
||||
thread_ret_type := elem_sym.thread_info().return_type
|
||||
call_expr.return_type = c.table.find_or_register_array(thread_ret_type)
|
||||
} else {
|
||||
c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)',
|
||||
call_expr.left.position())
|
||||
}
|
||||
}
|
||||
}
|
||||
// map/filter are supposed to have 1 arg only
|
||||
mut arg_type := left_type
|
||||
|
@ -1409,8 +1423,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
if !c.check_types(arg_type, info.elem_type) && !c.check_types(left_type, arg_type) {
|
||||
c.error('cannot $method_name `$arg_sym.name` to `$left_type_sym.name`', arg_expr.position())
|
||||
}
|
||||
} else if left_type_sym.kind == .gohandle && method_name == 'wait' {
|
||||
info := left_type_sym.info as table.GoHandle
|
||||
} else if left_type_sym.kind == .thread && method_name == 'wait' {
|
||||
info := left_type_sym.info as table.Thread
|
||||
if call_expr.args.len > 0 {
|
||||
c.error('wait() does not have any arguments', call_expr.args[0].pos)
|
||||
}
|
||||
|
@ -3611,7 +3625,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
|||
}
|
||||
ast.GoExpr {
|
||||
ret_type := c.call_expr(mut node.go_stmt.call_expr)
|
||||
return c.table.find_or_register_gohandle(ret_type)
|
||||
return c.table.find_or_register_thread(ret_type)
|
||||
}
|
||||
ast.ChanInit {
|
||||
return c.chan_init(mut node)
|
||||
|
|
|
@ -556,3 +556,16 @@ fn (mut g Gen) gen_array_index(node ast.CallExpr) {
|
|||
g.expr(node.args[0].expr)
|
||||
g.write(')')
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_array_wait(node ast.CallExpr) {
|
||||
arr := g.table.get_type_symbol(node.receiver_type)
|
||||
thread_type := arr.array_info().elem_type
|
||||
thread_sym := g.table.get_type_symbol(thread_type)
|
||||
thread_ret_type := thread_sym.thread_info().return_type
|
||||
eltyp := g.table.get_type_symbol(thread_ret_type).cname
|
||||
fn_name := g.register_thread_array_wait_call(eltyp)
|
||||
g.write('${fn_name}(')
|
||||
g.expr(node.left)
|
||||
g.write(')')
|
||||
}
|
||||
|
||||
|
|
|
@ -619,6 +619,36 @@ fn (mut g Gen) find_or_register_shared(t table.Type, base string) string {
|
|||
return sh_typ
|
||||
}
|
||||
|
||||
fn (mut g Gen) register_thread_array_wait_call(eltyp string) string {
|
||||
thread_typ := '__v_thread_$eltyp'
|
||||
ret_typ := if eltyp == '' { 'void' } else { 'Array_$eltyp' }
|
||||
thread_arr_typ := 'Array_$thread_typ'
|
||||
fn_name := '${thread_arr_typ}_wait'
|
||||
if fn_name !in g.waiter_fns {
|
||||
g.waiter_fns << fn_name
|
||||
if eltyp == 'void' {
|
||||
g.gowrappers.writeln('
|
||||
void ${fn_name}($thread_arr_typ a) {
|
||||
for (int i = 0; i < a.len; ++i) {
|
||||
$thread_typ t = (($thread_typ*)a.data)[i];
|
||||
__v_thread_${eltyp}_wait(t);
|
||||
}
|
||||
}')
|
||||
} else {
|
||||
g.gowrappers.writeln('
|
||||
$ret_typ ${fn_name}($thread_arr_typ a) {
|
||||
$ret_typ res = __new_array_with_default(a.len, a.len, sizeof($eltyp), 0);
|
||||
for (int i = 0; i < a.len; ++i) {
|
||||
$thread_typ t = (($thread_typ*)a.data)[i];
|
||||
(($eltyp*)res.data)[i] = __v_thread_${eltyp}_wait(t);
|
||||
}
|
||||
return res;
|
||||
}')
|
||||
}
|
||||
}
|
||||
return fn_name
|
||||
}
|
||||
|
||||
fn (mut g Gen) register_chan_pop_optional_call(opt_el_type string, styp string) {
|
||||
if opt_el_type !in g.chan_pop_optionals {
|
||||
g.chan_pop_optionals << opt_el_type
|
||||
|
@ -3097,20 +3127,34 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||
g.write(')')
|
||||
}
|
||||
} else if op_is_eq_or_ne && left_sym.kind == .array && right_sym.kind == .array {
|
||||
ptr_typ := g.gen_array_equality_fn(left_type)
|
||||
ptr_typ := g.gen_array_equality_fn(left_type.clear_flag(.shared_f))
|
||||
if node.op == .ne {
|
||||
g.write('!')
|
||||
}
|
||||
g.write('${ptr_typ}_arr_eq(')
|
||||
if node.left_type.is_ptr() {
|
||||
if node.left_type.is_ptr() && !node.left_type.has_flag(.shared_f) {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
if node.left_type.has_flag(.shared_f) {
|
||||
if node.left_type.is_ptr() {
|
||||
g.write('->val')
|
||||
} else {
|
||||
g.write('.val')
|
||||
}
|
||||
}
|
||||
g.write(', ')
|
||||
if node.right_type.is_ptr() {
|
||||
if node.right_type.is_ptr() && !node.right_type.has_flag(.shared_f) {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.right)
|
||||
if node.right_type.has_flag(.shared_f) {
|
||||
if node.right_type.is_ptr() {
|
||||
g.write('->val')
|
||||
} else {
|
||||
g.write('.val')
|
||||
}
|
||||
}
|
||||
g.write(')')
|
||||
} else if op_is_eq_or_ne && left_sym.kind == .array_fixed && right_sym.kind == .array_fixed {
|
||||
ptr_typ := g.gen_fixed_array_equality_fn(left_type)
|
||||
|
@ -5330,15 +5374,15 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
|
|||
table.Alias {
|
||||
// table.Alias { TODO
|
||||
}
|
||||
table.GoHandle {
|
||||
table.Thread {
|
||||
if g.pref.os == .windows {
|
||||
if name == 'gohandle_void' {
|
||||
if name == '__v_thread_void' {
|
||||
g.type_definitions.writeln('typedef HANDLE $name;')
|
||||
} else {
|
||||
// Windows can only return `u32` (no void*) from a thread, so the
|
||||
// V gohandle must maintain a pointer to the return value
|
||||
g.type_definitions.writeln('typedef struct {')
|
||||
g.type_definitions.writeln('\tvoid* ret_ptr;')
|
||||
g.type_definitions.writeln('\tvoid* ret_ptr;')
|
||||
g.type_definitions.writeln('\tHANDLE handle;')
|
||||
g.type_definitions.writeln('} $name;')
|
||||
}
|
||||
|
@ -5825,7 +5869,7 @@ fn (mut g Gen) go_stmt(node ast.GoStmt, joinable bool) string {
|
|||
if g.pref.os == .windows && node.call_expr.return_type != table.void_type {
|
||||
g.writeln('$arg_tmp_var->ret_ptr = malloc(sizeof($s_ret_typ));')
|
||||
}
|
||||
gohandle_name := 'gohandle_' +
|
||||
gohandle_name := '__v_thread_' +
|
||||
g.table.get_type_symbol(g.unwrap_generic(node.call_expr.return_type)).cname
|
||||
if g.pref.os == .windows {
|
||||
simple_handle := if joinable && node.call_expr.return_type != table.void_type {
|
||||
|
|
|
@ -532,6 +532,10 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
g.gen_array_index(node)
|
||||
return
|
||||
}
|
||||
'wait' {
|
||||
g.gen_array_wait(node)
|
||||
return
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -165,8 +165,8 @@ pub fn (mut g JsGen) typ(t table.Type) string {
|
|||
.aggregate {
|
||||
panic('TODO: unhandled aggregate in JS')
|
||||
}
|
||||
.gohandle {
|
||||
panic('TODO: unhandled gohandle in JS')
|
||||
.thread {
|
||||
panic('TODO: unhandled thread in JS')
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -102,16 +102,7 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme
|
|||
comments << p.eat_comments({})
|
||||
mut right_comments := []ast.Comment{}
|
||||
mut right := []ast.Expr{cap: left.len}
|
||||
if p.tok.kind == .key_go {
|
||||
stmt := p.stmt(false)
|
||||
go_stmt := stmt as ast.GoStmt
|
||||
right << ast.GoExpr{
|
||||
go_stmt: go_stmt
|
||||
pos: go_stmt.pos
|
||||
}
|
||||
} else {
|
||||
right, right_comments = p.expr_list()
|
||||
}
|
||||
right, right_comments = p.expr_list()
|
||||
comments << right_comments
|
||||
end_comments := p.eat_comments(same_line: true)
|
||||
mut has_cross_var := false
|
||||
|
|
|
@ -106,7 +106,8 @@ pub fn (mut p Parser) parse_map_type() table.Type {
|
|||
}
|
||||
|
||||
pub fn (mut p Parser) parse_chan_type() table.Type {
|
||||
if p.peek_tok.kind != .name && p.peek_tok.kind != .key_mut && p.peek_tok.kind != .amp {
|
||||
if p.peek_tok.kind != .name && p.peek_tok.kind != .key_mut && p.peek_tok.kind != .amp
|
||||
&& p.peek_tok.kind != .lsbr {
|
||||
p.next()
|
||||
return table.chan_type
|
||||
}
|
||||
|
@ -118,6 +119,18 @@ pub fn (mut p Parser) parse_chan_type() table.Type {
|
|||
return table.new_type(idx)
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) parse_thread_type() table.Type {
|
||||
if p.peek_tok.kind != .name && p.peek_tok.kind != .key_mut && p.peek_tok.kind != .amp
|
||||
&& p.peek_tok.kind != .lsbr {
|
||||
p.next()
|
||||
return table.thread_type
|
||||
}
|
||||
p.next()
|
||||
elem_type := p.parse_type()
|
||||
idx := p.table.find_or_register_thread(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{}
|
||||
|
@ -319,6 +332,9 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool, check
|
|||
if name == 'chan' {
|
||||
return p.parse_chan_type()
|
||||
}
|
||||
if name == 'thread' {
|
||||
return p.parse_thread_type()
|
||||
}
|
||||
defer {
|
||||
p.next()
|
||||
}
|
||||
|
|
|
@ -85,6 +85,14 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
|||
node = p.prefix_expr()
|
||||
}
|
||||
}
|
||||
.key_go {
|
||||
stmt := p.stmt(false)
|
||||
go_stmt := stmt as ast.GoStmt
|
||||
node = ast.GoExpr{
|
||||
go_stmt: go_stmt
|
||||
pos: go_stmt.pos
|
||||
}
|
||||
}
|
||||
.key_true, .key_false {
|
||||
node = ast.BoolLiteral{
|
||||
val: p.tok.kind == .key_true
|
||||
|
|
|
@ -484,17 +484,17 @@ pub fn (t &Table) chan_cname(elem_type Type, is_mut bool) string {
|
|||
}
|
||||
|
||||
[inline]
|
||||
pub fn (t &Table) gohandle_name(return_type Type) string {
|
||||
pub fn (t &Table) thread_name(return_type Type) string {
|
||||
return_type_sym := t.get_type_symbol(return_type)
|
||||
ptr := if return_type.is_ptr() { '&' } else { '' }
|
||||
return 'gohandle[$ptr$return_type_sym.name]'
|
||||
return 'thread $ptr$return_type_sym.name'
|
||||
}
|
||||
|
||||
[inline]
|
||||
pub fn (t &Table) gohandle_cname(return_type Type) string {
|
||||
pub fn (t &Table) thread_cname(return_type Type) string {
|
||||
return_type_sym := t.get_type_symbol(return_type)
|
||||
suffix := if return_type.is_ptr() { '_ptr' } else { '' }
|
||||
return 'gohandle_$return_type_sym.cname$suffix'
|
||||
return '__v_thread_$return_type_sym.cname$suffix'
|
||||
}
|
||||
|
||||
// map_source_name generates the original name for the v source.
|
||||
|
@ -560,25 +560,25 @@ pub fn (mut t Table) find_or_register_map(key_type Type, value_type Type) int {
|
|||
return t.register_type_symbol(map_typ)
|
||||
}
|
||||
|
||||
pub fn (mut t Table) find_or_register_gohandle(return_type Type) int {
|
||||
name := t.gohandle_name(return_type)
|
||||
cname := t.gohandle_cname(return_type)
|
||||
pub fn (mut t Table) find_or_register_thread(return_type Type) int {
|
||||
name := t.thread_name(return_type)
|
||||
cname := t.thread_cname(return_type)
|
||||
// existing
|
||||
existing_idx := t.type_idxs[name]
|
||||
if existing_idx > 0 {
|
||||
return existing_idx
|
||||
}
|
||||
// register
|
||||
gohandle_typ := TypeSymbol{
|
||||
parent_idx: gohandle_type_idx
|
||||
kind: .gohandle
|
||||
thread_typ := TypeSymbol{
|
||||
parent_idx: thread_type_idx
|
||||
kind: .thread
|
||||
name: name
|
||||
cname: cname
|
||||
info: GoHandle{
|
||||
info: Thread{
|
||||
return_type: return_type
|
||||
}
|
||||
}
|
||||
return t.register_type_symbol(gohandle_typ)
|
||||
return t.register_type_symbol(thread_typ)
|
||||
}
|
||||
|
||||
pub fn (mut t Table) find_or_register_array(elem_type Type) int {
|
||||
|
|
|
@ -16,7 +16,7 @@ import strings
|
|||
pub type Type = int
|
||||
|
||||
pub type TypeInfo = Aggregate | Alias | Array | ArrayFixed | Chan | Enum | FnType | GenericStructInst |
|
||||
GoHandle | Interface | Map | MultiReturn | Struct | SumType
|
||||
Interface | Map | MultiReturn | Struct | SumType | Thread
|
||||
|
||||
pub enum Language {
|
||||
v
|
||||
|
@ -306,7 +306,7 @@ pub const (
|
|||
any_type_idx = 25
|
||||
float_literal_type_idx = 26
|
||||
int_literal_type_idx = 27
|
||||
gohandle_type_idx = 28
|
||||
thread_type_idx = 28
|
||||
)
|
||||
|
||||
pub const (
|
||||
|
@ -350,14 +350,14 @@ pub const (
|
|||
any_type = new_type(any_type_idx)
|
||||
float_literal_type = new_type(float_literal_type_idx)
|
||||
int_literal_type = new_type(int_literal_type_idx)
|
||||
gohandle_type = new_type(gohandle_type_idx)
|
||||
thread_type = new_type(thread_type_idx)
|
||||
)
|
||||
|
||||
pub const (
|
||||
builtin_type_names = ['void', 'voidptr', 'charptr', 'byteptr', 'i8', 'i16', 'int', 'i64', 'u16',
|
||||
'u32', 'u64', 'int_literal', 'f32', 'f64', 'float_literal', 'string', 'ustring', 'char',
|
||||
'byte', 'bool', 'none', 'array', 'array_fixed', 'map', 'chan', 'any', 'struct', 'mapnode',
|
||||
'size_t', 'rune', 'gohandle']
|
||||
'size_t', 'rune', 'thread']
|
||||
)
|
||||
|
||||
pub struct MultiReturn {
|
||||
|
@ -419,7 +419,7 @@ pub enum Kind {
|
|||
float_literal
|
||||
int_literal
|
||||
aggregate
|
||||
gohandle
|
||||
thread
|
||||
}
|
||||
|
||||
pub fn (t &TypeSymbol) str() string {
|
||||
|
@ -471,10 +471,10 @@ pub fn (t &TypeSymbol) chan_info() Chan {
|
|||
}
|
||||
|
||||
[inline]
|
||||
pub fn (t &TypeSymbol) gohandle_info() GoHandle {
|
||||
pub fn (t &TypeSymbol) thread_info() Thread {
|
||||
match mut t.info {
|
||||
GoHandle { return t.info }
|
||||
else { panic('TypeSymbol.gohandle_info(): no gohandle info for type: $t.name') }
|
||||
Thread { return t.info }
|
||||
else { panic('TypeSymbol.thread_info(): no thread info for type: $t.name') }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -540,7 +540,7 @@ pub fn (mut t Table) register_builtin_type_symbols() {
|
|||
cname: 'int_literal'
|
||||
mod: 'builtin'
|
||||
)
|
||||
t.register_type_symbol(kind: .gohandle, name: 'gohandle', cname: 'gohandle', mod: 'builtin')
|
||||
t.register_type_symbol(kind: .thread, name: 'thread', cname: '__v_thread', mod: 'builtin')
|
||||
}
|
||||
|
||||
[inline]
|
||||
|
@ -619,7 +619,7 @@ pub fn (k Kind) str() string {
|
|||
.generic_struct_inst { 'generic_struct_inst' }
|
||||
.rune { 'rune' }
|
||||
.aggregate { 'aggregate' }
|
||||
.gohandle { 'gohandle' }
|
||||
.thread { 'thread' }
|
||||
}
|
||||
return k_str
|
||||
}
|
||||
|
@ -730,7 +730,7 @@ pub mut:
|
|||
is_mut bool
|
||||
}
|
||||
|
||||
pub struct GoHandle {
|
||||
pub struct Thread {
|
||||
pub mut:
|
||||
return_type Type
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
fn f(x f64) f64 {
|
||||
y := x * x
|
||||
return y
|
||||
}
|
||||
|
||||
fn test_array_thread_f64_wait() {
|
||||
mut r := []thread f64{cap: 10}
|
||||
for i in 0 .. 10 {
|
||||
r << go f(f64(i) + 0.5)
|
||||
}
|
||||
x := r.wait()
|
||||
assert x == [0.25, 2.25, 6.25, 12.25, 20.25, 30.25, 42.25, 56.25, 72.25, 90.25]
|
||||
}
|
||||
|
||||
fn g(shared a []int, i int) {
|
||||
lock a {
|
||||
a[i] *= a[i] + 1
|
||||
}
|
||||
}
|
||||
|
||||
fn test_array_thread_void_wait() {
|
||||
shared a := [2 3 5 7 11 13 17]
|
||||
t := [
|
||||
go g(shared a, 0)
|
||||
go g(shared a, 3)
|
||||
go g(shared a, 6)
|
||||
go g(shared a, 2)
|
||||
go g(shared a, 1)
|
||||
go g(shared a, 5)
|
||||
go g(shared a, 4)
|
||||
]
|
||||
println('threads started')
|
||||
t.wait()
|
||||
rlock a {
|
||||
assert a == [6, 12, 30, 56, 132, 182, 306]
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue