Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
469 changes: 424 additions & 45 deletions vlib/builtin/closure/closure.c.v

Large diffs are not rendered by default.

38 changes: 31 additions & 7 deletions vlib/builtin/closure/closure_nix.c.v
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ module closure

$if !freestanding && !vinix {
#include <sys/mman.h>
}
#insert "@VEXEROOT/vlib/builtin/closure/closure_once_nix.h"

@[typedef]
pub struct C.pthread_mutex_t {}
fn C.v_closure_init_once(ClosureInitFn)
}

struct ClosureMutex {
closure_mtx C.pthread_mutex_t
closure_mtx [128]u8
}

@[inline]
fn closure_mtx_ptr_platform() voidptr {
return unsafe { voidptr(&g_closure.closure_mtx[0]) }
}

@[inline]
Expand Down Expand Up @@ -65,20 +70,39 @@ fn get_page_size_platform() int {
@[inline]
fn closure_mtx_lock_init_platform() {
$if !freestanding || vinix {
C.pthread_mutex_init(&g_closure.closure_mtx, 0)
C.pthread_mutex_init(closure_mtx_ptr_platform(), 0)
}
}

@[inline]
fn closure_mtx_lock_platform() {
$if !freestanding || vinix {
C.pthread_mutex_lock(&g_closure.closure_mtx)
C.pthread_mutex_lock(closure_mtx_ptr_platform())
}
}

@[inline]
fn closure_mtx_unlock_platform() {
$if !freestanding || vinix {
C.pthread_mutex_unlock(&g_closure.closure_mtx)
C.pthread_mutex_unlock(closure_mtx_ptr_platform())
}
}

@[inline]
fn closure_current_thread_id_platform() u64 {
$if !freestanding {
return u64(C.pthread_self())
}
return u64(0)
}

@[inline]
fn closure_init_once_platform() {
$if freestanding || vinix {
if isnil(g_closure.closure_ptr) {
closure_init_body()
}
} $else {
C.v_closure_init_once(closure_init_body)
}
}
28 changes: 28 additions & 0 deletions vlib/builtin/closure/closure_once_nix.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef V_CLOSURE_ONCE_NIX_H
#define V_CLOSURE_ONCE_NIX_H

#include <pthread.h>

typedef void (*v_closure_init_fn)(void);

#ifndef V_CLOSURE_STATIC_INLINE
# ifdef _MSC_VER
# define V_CLOSURE_STATIC_INLINE static __inline
# else
# define V_CLOSURE_STATIC_INLINE static inline
# endif
#endif

static pthread_mutex_t v_closure_once_mutex = PTHREAD_MUTEX_INITIALIZER;
static int v_closure_once_done = 0;

V_CLOSURE_STATIC_INLINE void v_closure_init_once(v_closure_init_fn init_fn) {
pthread_mutex_lock(&v_closure_once_mutex);
if (!v_closure_once_done) {
init_fn();
v_closure_once_done = 1;
}
pthread_mutex_unlock(&v_closure_once_mutex);
}

#endif
28 changes: 28 additions & 0 deletions vlib/builtin/closure/closure_once_windows.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef V_CLOSURE_ONCE_WINDOWS_H
#define V_CLOSURE_ONCE_WINDOWS_H

#include <windows.h>

typedef void (*v_closure_init_fn)(void);

#ifndef V_CLOSURE_STATIC_INLINE
# ifdef _MSC_VER
# define V_CLOSURE_STATIC_INLINE static __inline
# else
# define V_CLOSURE_STATIC_INLINE static inline
# endif
#endif

static SRWLOCK v_closure_once_lock;
static int v_closure_once_done = 0;

V_CLOSURE_STATIC_INLINE void v_closure_init_once(v_closure_init_fn init_fn) {
AcquireSRWLockExclusive(&v_closure_once_lock);
if (!v_closure_once_done) {
init_fn();
v_closure_once_done = 1;
}
ReleaseSRWLockExclusive(&v_closure_once_lock);
}

#endif
13 changes: 13 additions & 0 deletions vlib/builtin/closure/closure_windows.c.v
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
module closure

#include <synchapi.h>
#insert "@VEXEROOT/vlib/builtin/closure/closure_once_windows.h"

fn C.v_closure_init_once(ClosureInitFn)

struct ClosureMutex {
closure_mtx C.SRWLOCK
Expand Down Expand Up @@ -51,3 +54,13 @@ fn closure_mtx_lock_platform() {
fn closure_mtx_unlock_platform() {
C.ReleaseSRWLockExclusive(&g_closure.closure_mtx)
}

@[inline]
fn closure_current_thread_id_platform() u64 {
return u64(C.GetCurrentThreadId())
}

@[inline]
fn closure_init_once_platform() {
C.v_closure_init_once(closure_init_body)
}
24 changes: 17 additions & 7 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ mut:
right_is_opt bool // right hand side on assignment is an option
assign_ct_type map[int]ast.Type // left hand side resolved comptime type
expected_rhs_type_by_pos map[int]ast.Type // expected value type for local RHS expressions
closure_frame_arg_tmps map[int]string
indent int
empty_line bool
assign_op token.Kind // *=, =, etc (for array_set)
Expand Down Expand Up @@ -431,6 +432,7 @@ pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) GenO
boehm_keep_decl: map[string]bool{}
boehm_keep_gen: map[string]bool{}
boehm_keep_busy: map[string]bool{}
closure_frame_arg_tmps: map[int]string{}
generic_parts_cache: []i8{len: table.type_symbols.len}
unwrap_generic_cache: map[u64]ast.Type{}
resolved_scope_var_type_cache: map[u64]ast.Type{}
Expand Down Expand Up @@ -1089,6 +1091,7 @@ fn cgen_process_one_file_cb(mut p pool.PoolProcessor, idx int, wid int) voidptr
boehm_keep_decl: map[string]bool{}
boehm_keep_gen: map[string]bool{}
boehm_keep_busy: map[string]bool{}
closure_frame_arg_tmps: map[int]string{}
generic_parts_cache: []i8{len: global_g.table.type_symbols.len}
unwrap_generic_cache: map[u64]ast.Type{}
resolved_scope_var_type_cache: map[u64]ast.Type{}
Expand Down Expand Up @@ -11138,6 +11141,11 @@ fn (mut g Gen) return_stmt(node ast.Return) {
}
}
return_needs_local_closure_cleanup := g.return_needs_local_closure_cleanup(node)
return_expr0 := unwrap_paren_call_expr(expr0)
return_call_needs_closure_lifetime_arg_tmp := match return_expr0 {
ast.CallExpr { g.call_needs_closure_lifetime_arg_tmp(return_expr0) }
else { false }
}

if exprs_len > 0 {
// `$veb.html()` expands to statements, so the Result return
Expand Down Expand Up @@ -11229,16 +11237,17 @@ fn (mut g Gen) return_stmt(node ast.Return) {
ret_typ := g.ret_styp(fn_ret_type)

// `return fn_call_opt()`
if exprs_len == 1 && (fn_return_is_option || fn_return_is_result) && expr0 is ast.CallExpr
&& expr0.or_block.kind == .absent {
mut resolved_call_return_type := g.resolve_return_type(expr0)
if exprs_len == 1 && (fn_return_is_option || fn_return_is_result)
&& return_expr0 is ast.CallExpr && return_expr0.or_block.kind == .absent {
mut resolved_call_return_type := g.resolve_return_type(return_expr0)
if resolved_call_return_type == ast.void_type {
resolved_call_return_type = expr0.return_type
resolved_call_return_type = return_expr0.return_type
}
if g.unwrap_generic(g.recheck_concrete_type(resolved_call_return_type)) == ret_type {
if g.defer_stmts.len > 0 || return_needs_local_closure_cleanup {
if g.defer_stmts.len > 0 || return_needs_local_closure_cleanup
|| return_call_needs_closure_lifetime_arg_tmp {
g.write('${ret_typ} ${tmpvar} = ')
g.expr(expr0)
g.expr(return_expr0)
g.writeln(';')
g.write_defer_stmts_when_needed(node.scope, true, node.pos)
if return_needs_local_closure_cleanup {
Expand All @@ -11248,14 +11257,15 @@ fn (mut g Gen) return_stmt(node ast.Return) {
} else {
g.write_defer_stmts_when_needed(node.scope, true, node.pos)
g.write('return ')
g.expr(expr0)
g.expr(return_expr0)
g.writeln(';')
}
return
}
}
mut use_tmp_var := g.defer_stmts.len > 0 || g.defer_profile_code.len > 0
|| g.cur_lock.lockeds.len > 0 || return_needs_local_closure_cleanup
|| return_call_needs_closure_lifetime_arg_tmp
|| (fn_return_is_multi && exprs_len >= 1 && fn_return_is_option)
|| fn_return_is_fixed_array_non_result
|| (fn_return_is_multi && ret_expr_types.any(g.table.final_sym(it).kind == .array_fixed))
Expand Down
119 changes: 119 additions & 0 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -2312,6 +2312,12 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
return
}
}
closure_frame_arg_tmps := g.prepare_closure_lifetime_arg_tmps(node)
defer {
for pos in closure_frame_arg_tmps.keys() {
g.closure_frame_arg_tmps.delete(pos)
}
}
old_inside_call := g.inside_call
g.inside_call = true
// Reset inside_selector_lhs so that receiver expressions inside method
Expand Down Expand Up @@ -2444,6 +2450,13 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
} else {
g.fn_call(node)
}
if closure_frame_arg_tmps.len > 0 {
g.writeln(';')
for _, tmp in closure_frame_arg_tmps {
g.writeln('builtin__closure__closure_try_destroy((voidptr)${tmp});')
Comment thread
GGRei marked this conversation as resolved.
}
g.set_current_pos_as_last_stmt_pos()
}
if needs_tmp_fn_result_cleanup && !gen_or && !gen_keep_alive && !g.inside_curry_call {
if node.return_type == ast.void_type {
g.writeln(';')
Expand Down Expand Up @@ -2527,6 +2540,105 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
}
}

fn (mut g Gen) is_closure_lifetime_callback_call(node ast.CallExpr) bool {
method_name := node.name.all_after_last('.')
if !node.is_method || method_name !in ['frame', 'suspend', 'untracked'] || node.args.len != 1 {
return false
}
mut receiver_type := g.unwrap_generic(node.receiver_type)
if receiver_type == 0 {
receiver_type = g.unwrap_generic(node.left_type)
}
if receiver_type == 0 {
return false
}
if receiver_type.is_ptr() {
receiver_type = receiver_type.deref()
}
return g.table.sym(receiver_type).name == 'builtin.closure.Lifetime'
}

fn (mut g Gen) closure_lifetime_arg_needs_tmp(expr ast.Expr) bool {
unwrapped_expr := expr.remove_par()
return match unwrapped_expr {
ast.AnonFn {
unwrapped_expr.inherited_vars.len > 0
}
Comment thread
GGRei marked this conversation as resolved.
ast.SelectorExpr {
unwrapped_expr.has_hidden_receiver
}
else {
false
Comment thread
GGRei marked this conversation as resolved.
}
Comment thread
GGRei marked this conversation as resolved.
}
}

fn (mut g Gen) call_needs_closure_lifetime_arg_tmp(node ast.CallExpr) bool {
return g.is_closure_lifetime_callback_call(node)
&& g.closure_lifetime_arg_needs_tmp(node.args[0].expr)
}

fn (mut g Gen) closure_lifetime_fn_type_tmp_signature(fn_types []ast.Type, tmp string) ?string {
for raw_fn_typ in fn_types {
fn_typ := g.unwrap_generic(g.recheck_concrete_type(raw_fn_typ))
if fn_typ == 0 {
continue
}
fn_sym := g.table.final_sym(fn_typ)
if fn_sym.info is ast.FnType {
func := fn_sym.info.func
return g.fn_var_signature(ast.void_type, func.return_type, func.params.map(it.typ), tmp)
}
}
return none
}

fn (mut g Gen) closure_lifetime_arg_tmp_signature(node ast.CallExpr, arg ast.CallArg, tmp string) ?string {
unwrapped_expr := arg.expr.remove_par()
match unwrapped_expr {
ast.AnonFn {
return g.fn_var_signature(ast.void_type, unwrapped_expr.decl.return_type,
unwrapped_expr.decl.params.map(it.typ), tmp)
}
ast.SelectorExpr {
mut fn_types := []ast.Type{}
if arg.typ != 0 {
fn_types << arg.typ
}
if unwrapped_expr.typ != 0 {
fn_types << unwrapped_expr.typ
}
if node.expected_arg_types.len > 0 && node.expected_arg_types[0] != 0 {
fn_types << node.expected_arg_types[0]
}
return g.closure_lifetime_fn_type_tmp_signature(fn_types, tmp)
}
else {}
}

return none
}

fn (mut g Gen) prepare_closure_lifetime_arg_tmps(node ast.CallExpr) map[int]string {
mut tmps := map[int]string{}
if !g.call_needs_closure_lifetime_arg_tmp(node) {
return tmps
}
arg := node.args[0]
tmp := g.new_tmp_var()
fn_type := g.closure_lifetime_arg_tmp_signature(node, arg, tmp) or { return tmps }
line := g.go_before_last_stmt().trim_space()
g.empty_line = true
g.write('${fn_type} = ')
g.expr(ast.Expr(arg.expr))
g.writeln(';')
g.set_current_pos_as_last_stmt_pos()
g.write(line)
tmps[arg.pos.pos] = tmp
g.closure_frame_arg_tmps[arg.pos.pos] = tmp
return tmps
}

fn (mut g Gen) conversion_function_call(prefix string, postfix string, node ast.CallExpr) {
g.write('${prefix}( (')
g.expr(node.left)
Expand Down Expand Up @@ -6410,6 +6522,13 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
if is_variadic && i == expected_types.len - 1 {
break
}
if tmp := g.closure_frame_arg_tmps[arg.pos.pos] {
g.write(tmp)
if i < args.len - 1 || is_variadic {
g.write(', ')
}
continue
}
mut is_smartcast := false
if arg.expr is ast.Ident {
if arg.expr.obj is ast.Var {
Expand Down
Loading
Loading