1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

all: coroutines (part 1)

This commit is contained in:
Alexander Medvednikov 2023-05-27 23:33:46 +02:00
parent 5812579d53
commit 45f16a2640
18 changed files with 216 additions and 24 deletions

View File

@ -1166,6 +1166,9 @@ fn (t Tree) expr(expr ast.Expr) &Node {
ast.GoExpr {
return t.go_expr(expr)
}
ast.SpawnExpr {
return t.spawn_expr(expr)
}
ast.OffsetOf {
return t.offset_of(expr)
}
@ -1862,6 +1865,15 @@ fn (t Tree) go_expr(expr ast.GoExpr) &Node {
return obj
}
fn (t Tree) spawn_expr(expr ast.SpawnExpr) &Node {
mut obj := new_object()
obj.add_terse('ast_type', t.string_node('SpawnExpr'))
obj.add_terse('call_expr', t.call_expr(expr.call_expr))
obj.add_terse('is_expr', t.bool_node(expr.is_expr))
obj.add('pos', t.pos(expr.pos))
return obj
}
fn (t Tree) offset_of(expr ast.OffsetOf) &Node {
mut obj := new_object()
obj.add_terse('ast_type', t.string_node('OffsetOf'))

44
thirdparty/photon/photonwrapper.h vendored Normal file
View File

@ -0,0 +1,44 @@
#ifndef C_PHOTONWRAPPER_H_
#define C_PHOTONWRAPPER_H_
#ifdef __cplusplus
#include <fcntl.h>
//#include <vector>
#include <photon/thread/std-compat.h>
#include <photon/common/alog.h>
#include <photon/common/iovector.h>
#include <photon/fs/localfs.h>
#include <photon/net/socket.h>
#include <iostream>
extern "C" {
#else
typedef struct CppClass CppClass;
#endif
//CppClass *cpp_class_create();
//void cpp_class_destroy(CppClass *c);
//void cpp_class_do_work(CppClass *c);
int photon_init_default();
void photon_thread_create11(void* (* f)(void*));
void photon_sleep_s(int n);
void photon_sleep_ms(int n);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,21 @@
// Copyright (c) 2019-2023 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module coroutines
import time
#flag -I @VEXEROOT/thirdparty/photon
#flag @VEXEROOT/thirdparty/photon/photonwrapper.so
#include "photonwrapper.h"
fn C.photon_init_default() int
fn C.photon_thread_create11(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())
}

View File

@ -54,6 +54,7 @@ pub type Expr = AnonFn
| SelectExpr
| SelectorExpr
| SizeOf
| SpawnExpr
| SqlExpr
| StringInterLiteral
| StringLiteral
@ -1386,6 +1387,15 @@ pub mut:
is_expr bool
}
[minify]
pub struct SpawnExpr {
pub:
pos token.Pos
pub mut:
call_expr CallExpr
is_expr bool
}
pub struct GotoLabel {
pub:
name string
@ -1948,10 +1958,11 @@ pub fn (expr Expr) pos() token.Pos {
}
NodeError, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr,
CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector,
EnumVal, DumpExpr, FloatLiteral, GoExpr, Ident, IfExpr, IntegerLiteral, IsRefType, Likely,
LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr,
RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral,
StructInit, TypeNode, TypeOf, UnsafeExpr, ComptimeType, Nil {
EnumVal, DumpExpr, FloatLiteral, GoExpr, SpawnExpr, Ident, IfExpr, IntegerLiteral,
IsRefType, Likely, LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr,
PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr,
StringInterLiteral, StringLiteral, StructInit, TypeNode, TypeOf, UnsafeExpr, ComptimeType,
Nil {
return expr.pos
}
IndexExpr {

View File

@ -403,6 +403,9 @@ pub fn (x Expr) str() string {
GoExpr {
return 'go ${x.call_expr}'
}
SpawnExpr {
return 'spawn ${x.call_expr}'
}
Ident {
return x.name.clone()
}

View File

@ -2548,6 +2548,9 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
ast.GoExpr {
return c.go_expr(mut node)
}
ast.SpawnExpr {
return c.spawn_expr(mut node)
}
ast.Ident {
return c.ident(mut node)
}

View File

@ -2100,7 +2100,7 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
return ast.void_type
}
fn (mut c Checker) go_expr(mut node ast.GoExpr) ast.Type {
fn (mut c Checker) spawn_expr(mut node ast.SpawnExpr) ast.Type {
ret_type := c.call_expr(mut node.call_expr)
if node.call_expr.or_block.kind != .absent {
c.error('option handling cannot be done in `spawn` call. Do it when calling `.wait()`',
@ -2126,6 +2126,33 @@ fn (mut c Checker) go_expr(mut node ast.GoExpr) ast.Type {
}
}
fn (mut c Checker) go_expr(mut node ast.GoExpr) ast.Type {
// TODO copypasta from spawn_expr
ret_type := c.call_expr(mut node.call_expr)
if node.call_expr.or_block.kind != .absent {
c.error('option handling cannot be done in `go` call. Do it when calling `.wait()`',
node.call_expr.or_block.pos)
}
// Make sure there are no mutable arguments
for arg in node.call_expr.args {
if arg.is_mut && !arg.typ.is_ptr() {
c.error('function in `go` statement cannot contain mutable non-reference arguments',
arg.expr.pos())
}
}
if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr()
&& !node.call_expr.left_type.is_ptr() {
c.error('method in `go` statement cannot have non-reference mutable receiver',
node.call_expr.left.pos())
}
if c.pref.backend.is_js() {
return c.table.find_or_register_promise(c.unwrap_generic(ret_type))
} else {
return c.table.find_or_register_thread(c.unwrap_generic(ret_type))
}
}
fn (mut c Checker) set_node_expected_arg_types(mut node ast.CallExpr, func &ast.Fn) {
if node.expected_arg_types.len == 0 {
start_idx := if func.is_method { 1 } else { 0 }

View File

@ -534,10 +534,10 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
}
ast.AnonFn, ast.ArrayDecompose, ast.AsCast, ast.Assoc, ast.AtExpr, ast.CTempVar,
ast.ChanInit, ast.Comment, ast.ComptimeCall, ast.ComptimeSelector, ast.ComptimeType,
ast.ConcatExpr, ast.DumpExpr, ast.EmptyExpr, ast.EnumVal, ast.GoExpr, ast.IfGuardExpr,
ast.IndexExpr, ast.IsRefType, ast.Likely, ast.LockExpr, ast.MapInit, ast.MatchExpr,
ast.Nil, ast.NodeError, ast.None, ast.OffsetOf, ast.OrExpr, ast.RangeExpr, ast.SelectExpr,
ast.SqlExpr, ast.TypeNode, ast.TypeOf {
ast.ConcatExpr, ast.DumpExpr, ast.EmptyExpr, ast.EnumVal, ast.GoExpr, ast.SpawnExpr,
ast.IfGuardExpr, ast.IndexExpr, ast.IsRefType, ast.Likely, ast.LockExpr, ast.MapInit,
ast.MatchExpr, ast.Nil, ast.NodeError, ast.None, ast.OffsetOf, ast.OrExpr, ast.RangeExpr,
ast.SelectExpr, ast.SqlExpr, ast.TypeNode, ast.TypeOf {
e.error('unhandled expression ${typeof(expr).name}')
}
}

View File

@ -640,6 +640,9 @@ pub fn (mut f Fmt) expr(node_ ast.Expr) {
ast.GoExpr {
f.go_expr(node)
}
ast.SpawnExpr {
f.spawn_expr(node)
}
ast.Ident {
f.ident(node)
}
@ -1206,11 +1209,16 @@ pub fn (mut f Fmt) global_decl(node ast.GlobalDecl) {
}
}
pub fn (mut f Fmt) go_expr(node ast.GoExpr) {
pub fn (mut f Fmt) spawn_expr(node ast.SpawnExpr) {
f.write('spawn ')
f.call_expr(node.call_expr)
}
pub fn (mut f Fmt) go_expr(node ast.GoExpr) {
f.write('go ')
f.call_expr(node.call_expr)
}
pub fn (mut f Fmt) goto_label(node ast.GotoLabel) {
f.writeln('${node.name}:')
}

View File

@ -3160,6 +3160,9 @@ fn (mut g Gen) expr(node_ ast.Expr) {
g.write(node.val)
}
}
ast.SpawnExpr {
g.spawn_expr(node)
}
ast.GoExpr {
g.go_expr(node)
}
@ -3260,7 +3263,7 @@ fn (mut g Gen) expr(node_ ast.Expr) {
mut expr_str := ''
if mut node.expr is ast.ComptimeSelector
&& (node.expr as ast.ComptimeSelector).left is ast.Ident {
// val.$(field.name)?
// val.$(field.name)?
expr_str = '${node.expr.left.str()}.${g.comptime_for_field_value.name}'
} else if mut node.expr is ast.Ident && g.is_comptime_var(node.expr) {
// val?

View File

@ -2090,6 +2090,11 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
}
fn (mut g Gen) go_expr(node ast.GoExpr) {
g.writeln('/*go (coroutine) */')
}
fn (mut g Gen) spawn_expr(node ast.SpawnExpr) {
g.writeln('/*spawn (thread) */')
line := g.go_before_stmt(0)
mut handle := ''
tmp := g.new_tmp_var()

View File

@ -935,6 +935,9 @@ fn (mut g JsGen) expr(node_ ast.Expr) {
ast.GoExpr {
g.gen_go_expr(node)
}
ast.SpawnExpr {
g.gen_spawn_expr(node)
}
ast.Ident {
g.gen_ident(node)
}
@ -1826,6 +1829,20 @@ fn (mut g JsGen) gen_go_expr(node ast.GoExpr) {
g.writeln('})});')
}
fn (mut g JsGen) gen_spawn_expr(node ast.SpawnExpr) {
if g.pref.output_es5 {
verror('No support for goroutines on ES5 output')
return
}
g.writeln('new _v_Promise({promise: new Promise(function(resolve){')
g.inc_indent()
g.write('resolve(')
g.expr(node.call_expr)
g.write(');')
g.dec_indent()
g.writeln('})});')
}
fn (mut g JsGen) gen_import_stmt(it ast.Import) {
g.ns.imports[it.mod] = it.alias
}

View File

@ -273,7 +273,7 @@ fn (mut w Walker) expr(node_ ast.Expr) {
w.fn_by_name('eprint')
w.fn_by_name('eprintln')
}
ast.GoExpr {
ast.SpawnExpr {
w.expr(node.call_expr)
if w.pref.os == .windows {
w.fn_by_name('panic_lasterr')
@ -283,6 +283,9 @@ fn (mut w Walker) expr(node_ ast.Expr) {
w.fn_by_name('panic_error_number')
}
}
ast.GoExpr {
w.expr(node.call_expr)
}
ast.IndexExpr {
w.expr(node.left)
w.expr(node.index)

View File

@ -112,9 +112,15 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr {
}
}
.key_go, .key_spawn {
mut go_expr := p.go_expr()
go_expr.is_expr = true
node = go_expr
if p.pref.use_coroutines && p.tok.kind == .key_go {
mut go_expr := p.go_expr()
go_expr.is_expr = true
node = go_expr
} else {
mut spawn_expr := p.spawn_expr()
spawn_expr.is_expr = true
node = spawn_expr
}
}
.key_true, .key_false {
node = ast.BoolLiteral{

View File

@ -1038,7 +1038,7 @@ fn (mut p Parser) fn_args() ([]ast.Param, bool, bool) {
return args, types_only, is_variadic
}
fn (mut p Parser) go_expr() ast.GoExpr {
fn (mut p Parser) spawn_expr() ast.SpawnExpr {
p.next()
spos := p.tok.pos()
expr := p.expr(0)
@ -1053,6 +1053,27 @@ fn (mut p Parser) go_expr() ast.GoExpr {
pos := spos.extend(p.prev_tok.pos())
p.register_auto_import('sync.threads')
p.table.gostmts++
return ast.SpawnExpr{
call_expr: call_expr
pos: pos
}
}
fn (mut p Parser) go_expr() ast.GoExpr {
p.next()
spos := p.tok.pos()
expr := p.expr(0)
call_expr := if expr is ast.CallExpr {
expr
} else {
p.error_with_pos('expression in `go` must be a function call', expr.pos())
ast.CallExpr{
scope: p.scope
}
}
pos := spos.extend(p.prev_tok.pos())
// p.register_auto_import('coroutines')
p.table.gostmts++
return ast.GoExpr{
call_expr: call_expr
pos: pos

View File

@ -1078,10 +1078,18 @@ fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
}
}
.key_go, .key_spawn {
go_expr := p.go_expr()
return ast.ExprStmt{
expr: go_expr
pos: go_expr.pos
if p.pref.use_coroutines && p.tok.kind == .key_go {
go_expr := p.go_expr()
return ast.ExprStmt{
expr: go_expr
pos: go_expr.pos
}
} else {
spawn_expr := p.spawn_expr()
return ast.ExprStmt{
expr: spawn_expr
pos: spawn_expr.pos
}
}
}
.key_goto {

View File

@ -218,10 +218,6 @@ pub fn default_tcc_compiler() string {
}
pub fn (mut p Preferences) default_c_compiler() {
// fast_clang := '/usr/local/Cellar/llvm/8.0.0/bin/clang'
// if os.exists(fast_clang) {
// return fast_clang
// }
// TODO fix $if after 'string'
$if windows {
p.ccompiler = 'gcc'

View File

@ -223,6 +223,7 @@ pub mut:
assert_failure_mode AssertFailureMode // whether to call abort() or print_backtrace() after an assertion failure
message_limit int = 150 // the maximum amount of warnings/errors/notices that will be accumulated
nofloat bool // for low level code, like kernels: replaces f32 with u32 and f64 with u64
use_coroutines bool // experimental coroutines
// checker settings:
checker_match_exhaustive_cutoff_limit int = 12
thread_stack_size int = 8388608 // Change with `-thread-stack-size 4194304`. Note: on macos it was 524288, which is too small for more complex programs with many nested callexprs.
@ -798,6 +799,9 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin
res.cmain = cmdline.option(current_args, '-cmain', '')
i++
}
'-use-coroutines' {
res.use_coroutines = true
}
else {
if command == 'build' && is_source_file(arg) {
eprintln('Use `v ${arg}` instead.')