all: extend `wait()` method to array of `thread` handles (#8840)

pull/8904/head
Uwe Krüger 2021-02-22 14:08:52 +01:00 committed by GitHub
parent ac1c4932e7
commit 5306b2788c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 177 additions and 48 deletions

View File

@ -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',

View File

@ -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)

View File

@ -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(')')
}

View File

@ -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 {

View File

@ -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 {}
}
}

View File

@ -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')
}
}
/*

View File

@ -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

View File

@ -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()
}

View File

@ -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

View File

@ -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 {

View File

@ -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
}

View File

@ -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]
}
}