Skip to content

Commit

Permalink
all: coroutines (part 2)
Browse files Browse the repository at this point in the history
  • Loading branch information
medvednikov committed May 28, 2023
1 parent 2162230 commit 9db10c8
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 48 deletions.
2 changes: 1 addition & 1 deletion thirdparty/photon/photonwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extern "C" {
#endif

int photon_init_default();
void photon_thread_create11(void* (* f)(void*));
void photon_thread_create(void* (* f)(void*), void* arg);
void photon_sleep_s(int n);
void photon_sleep_ms(int n);

Expand Down
7 changes: 6 additions & 1 deletion vlib/coroutines/coroutines.v
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ import time
#include "photonwrapper.h"

fn C.photon_init_default() int
fn C.photon_thread_create11(f voidptr)
fn C.photon_thread_create(f voidptr)
fn C.photon_sleep_s(n int)
fn C.photon_sleep_ms(n int)

// sleep is coroutine-safe version of time.sleep()
pub fn sleep(duration time.Duration) {
C.photon_sleep_ms(duration.milliseconds())
}

// init needs to be run
pub fn initialize() int {
return C.photon_init_default()
}
8 changes: 7 additions & 1 deletion vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -4199,7 +4199,13 @@ fn (mut c Checker) enum_val(mut node ast.EnumVal) ast.Type {
fsym := c.table.final_sym(typ)
if fsym.kind != .enum_ && !c.pref.translated && !c.file.is_translated {
// TODO in C int fields can be compared to enums, need to handle that in C2V
c.error('expected type is not an enum (`${typ_sym.name}`)', node.pos)
if typ_sym.kind == .placeholder {
// If it's a placeholder, the type doesn't exist, print
// an error that makes sense here.
c.error('unknown type `${typ_sym.name}`', node.pos)
} else {
c.error('expected type is not an enum (`${typ_sym.name}`)', node.pos)
}
return ast.void_type
}
if fsym.info !is ast.Enum {
Expand Down
10 changes: 8 additions & 2 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -3161,10 +3161,16 @@ fn (mut g Gen) expr(node_ ast.Expr) {
}
}
ast.SpawnExpr {
g.spawn_expr(node)
g.spawn_and_go_expr(node, .spawn_)
}
ast.GoExpr {
g.go_expr(node)
// XTODO this results in a cgen bug, order of fields is broken
// g.spawn_and_go_expr(ast.SpawnExpr{node.pos, node.call_expr, node.is_expr},
g.spawn_and_go_expr(ast.SpawnExpr{
pos: node.pos
call_expr: node.call_expr
is_expr: node.is_expr
}, .go_)
}
ast.Ident {
g.ident(node)
Expand Down
105 changes: 62 additions & 43 deletions vlib/v/gen/c/spawn_and_go.v
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,19 @@ module c
import v.ast
import v.util

fn (mut g Gen) spawn_expr(node ast.SpawnExpr) {
g.writeln('/*spawn (thread) */')
enum SpawnGoMode {
spawn_
go_
}

fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) {
is_spawn := mode == .spawn_
is_go := mode == .go_
if is_spawn {
g.writeln('/*spawn (thread) */')
} else {
g.writeln('/*go (coroutine) */')
}
line := g.go_before_stmt(0)
mut handle := ''
tmp := g.new_tmp_var()
Expand Down Expand Up @@ -67,7 +78,12 @@ fn (mut g Gen) spawn_expr(node ast.SpawnExpr) {
wrapper_struct_name := 'thread_arg_' + name
wrapper_fn_name := name + '_thread_wrapper'
arg_tmp_var := 'arg_' + tmp
g.writeln('${wrapper_struct_name} *${arg_tmp_var} = malloc(sizeof(thread_arg_${name}));')
if is_spawn {
g.writeln('${wrapper_struct_name} *${arg_tmp_var} = malloc(sizeof(thread_arg_${name}));')
} else if is_go {
g.writeln('${wrapper_struct_name} ${arg_tmp_var};')
}
dot := if is_spawn { '->' } else { '.' }
fn_name := if use_tmp_fn_var {
tmp_fn
} else if expr.is_fn_var {
Expand All @@ -76,15 +92,15 @@ fn (mut g Gen) spawn_expr(node ast.SpawnExpr) {
name
}
if !(expr.is_method && g.table.sym(expr.receiver_type).kind == .interface_) {
g.writeln('${arg_tmp_var}->fn = ${fn_name};')
g.writeln('${arg_tmp_var}${dot}fn = ${fn_name};')
}
if expr.is_method {
g.write('${arg_tmp_var}->arg0 = ')
g.write('${arg_tmp_var}${dot}arg0 = ')
g.expr(expr.left)
g.writeln(';')
}
for i, arg in expr.args {
g.write('${arg_tmp_var}->arg${i + 1} = ')
g.write('${arg_tmp_var}${dot}arg${i + 1} = ')
g.expr(arg.expr)
g.writeln(';')
}
Expand All @@ -108,39 +124,43 @@ fn (mut g Gen) spawn_expr(node ast.SpawnExpr) {
res := if is_res { '${result_name}_' } else { '' }
gohandle_name = '__v_thread_${opt}${res}${g.table.sym(g.unwrap_generic(node.call_expr.return_type)).cname}'
}
if g.pref.os == .windows {
simple_handle := if node.is_expr && node.call_expr.return_type != ast.void_type {
'thread_handle_${tmp}'
if is_spawn {
if g.pref.os == .windows {
simple_handle := if node.is_expr && node.call_expr.return_type != ast.void_type {
'thread_handle_${tmp}'
} else {
'thread_${tmp}'
}
stack_size := g.get_cur_thread_stack_size(expr.name)
g.writeln('HANDLE ${simple_handle} = CreateThread(0, ${stack_size}, (LPTHREAD_START_ROUTINE)${wrapper_fn_name}, ${arg_tmp_var}, 0, 0); // fn: ${expr.name}')
g.writeln('if (!${simple_handle}) panic_lasterr(tos3("`go ${name}()`: "));')
if node.is_expr && node.call_expr.return_type != ast.void_type {
g.writeln('${gohandle_name} thread_${tmp} = {')
g.writeln('\t.ret_ptr = ${arg_tmp_var}->ret_ptr,')
g.writeln('\t.handle = thread_handle_${tmp}')
g.writeln('};')
}
if !node.is_expr {
g.writeln('CloseHandle(thread_${tmp});')
}
} else {
'thread_${tmp}'
}
stack_size := g.get_cur_thread_stack_size(expr.name)
g.writeln('HANDLE ${simple_handle} = CreateThread(0, ${stack_size}, (LPTHREAD_START_ROUTINE)${wrapper_fn_name}, ${arg_tmp_var}, 0, 0); // fn: ${expr.name}')
g.writeln('if (!${simple_handle}) panic_lasterr(tos3("`go ${name}()`: "));')
if node.is_expr && node.call_expr.return_type != ast.void_type {
g.writeln('${gohandle_name} thread_${tmp} = {')
g.writeln('\t.ret_ptr = ${arg_tmp_var}->ret_ptr,')
g.writeln('\t.handle = thread_handle_${tmp}')
g.writeln('};')
}
if !node.is_expr {
g.writeln('CloseHandle(thread_${tmp});')
}
} else {
g.writeln('pthread_t thread_${tmp};')
mut sthread_attributes := 'NULL'
if g.pref.os != .vinix {
g.writeln('pthread_attr_t thread_${tmp}_attributes;')
g.writeln('pthread_attr_init(&thread_${tmp}_attributes);')
size := g.get_cur_thread_stack_size(expr.name)
g.writeln('pthread_attr_setstacksize(&thread_${tmp}_attributes, ${size}); // fn: ${expr.name}')
sthread_attributes = '&thread_${tmp}_attributes'
}
g.writeln('int ${tmp}_thr_res = pthread_create(&thread_${tmp}, ${sthread_attributes}, (void*)${wrapper_fn_name}, ${arg_tmp_var});')
g.writeln('if (${tmp}_thr_res) panic_error_number(tos3("`go ${name}()`: "), ${tmp}_thr_res);')
if !node.is_expr {
g.writeln('pthread_detach(thread_${tmp});')
g.writeln('pthread_t thread_${tmp};')
mut sthread_attributes := 'NULL'
if g.pref.os != .vinix {
g.writeln('pthread_attr_t thread_${tmp}_attributes;')
g.writeln('pthread_attr_init(&thread_${tmp}_attributes);')
size := g.get_cur_thread_stack_size(expr.name)
g.writeln('pthread_attr_setstacksize(&thread_${tmp}_attributes, ${size}); // fn: ${expr.name}')
sthread_attributes = '&thread_${tmp}_attributes'
}
g.writeln('int ${tmp}_thr_res = pthread_create(&thread_${tmp}, ${sthread_attributes}, (void*)${wrapper_fn_name}, ${arg_tmp_var});')
g.writeln('if (${tmp}_thr_res) panic_error_number(tos3("`go ${name}()`: "), ${tmp}_thr_res);')
if !node.is_expr {
g.writeln('pthread_detach(thread_${tmp});')
}
}
} else if is_go {
g.writeln('photon_thread_create((void*)${wrapper_fn_name}, &${arg_tmp_var});')
}
g.writeln('// end go')
if node.is_expr {
Expand Down Expand Up @@ -273,11 +293,11 @@ fn (mut g Gen) spawn_expr(node ast.SpawnExpr) {
receiver_type_name := util.no_dots(rec_cc_type)
g.gowrappers.write_string('${c_name(receiver_type_name)}_name_table[')
g.gowrappers.write_string('arg->arg0')
dot := if expr.left_type.is_ptr() { '->' } else { '.' }
idot := if expr.left_type.is_ptr() { '->' } else { '.' }
mname := c_name(expr.name)
g.gowrappers.write_string('${dot}_typ]._method_${mname}(')
g.gowrappers.write_string('${idot}_typ]._method_${mname}(')
g.gowrappers.write_string('arg->arg0')
g.gowrappers.write_string('${dot}_object')
g.gowrappers.write_string('${idot}_object')
} else {
g.gowrappers.write_string('arg->fn(')
g.gowrappers.write_string('arg->arg0')
Expand Down Expand Up @@ -354,9 +374,8 @@ fn (mut g Gen) spawn_expr(node ast.SpawnExpr) {
}
}

fn (mut g Gen) go_expr(node ast.GoExpr) {
g.writeln('/*go (coroutine) */')
}
// fn (mut g Gen) go_expr(node ast.GoExpr) {
//}

// get current thread size, if fn hasn't defined return default
[inline]
Expand Down
15 changes: 15 additions & 0 deletions vlib/v/pref/pref.v
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import os.cmdline
import os
import v.vcache
import rand
// import net.http // TODO can't use net.http on arm maccs: bignum.c arm asm error

pub enum BuildMode {
// `v program.v'
Expand Down Expand Up @@ -801,6 +802,20 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
}
'-use-coroutines' {
res.use_coroutines = true
$if macos && arm64 {
vexe := vexe_path()
vroot := os.dir(vexe)
so_path := os.join_path(vroot, 'thirdparty', 'photon', 'photonwrapper.so')
so_url := 'https://github.com/vlang/photonbin/raw/master/photonwrapper_macos_arm64.so'
if !os.exists(so_path) {
println('coroutines .so not found, downloading...')
// http.download_file(so_url, so_path) or { panic(err) }
os.system('wget -O "${so_path}" "${so_url}"')
println('done!')
}
} $else {
println('coroutines only work on arm64 macos for now')
}
}
else {
if command == 'build' && is_source_file(arg) {
Expand Down

0 comments on commit 9db10c8

Please sign in to comment.