diff --git a/doc/upcoming.md b/doc/upcoming.md index 38ec2b3823..12c2bed2bf 100644 --- a/doc/upcoming.md +++ b/doc/upcoming.md @@ -35,13 +35,20 @@ atomic d := ... - `c` can be passed to coroutines an accessed *concurrently*.2 In order to avoid data races it has to be locked before access can occur and unlocked to allow access to - other coroutines. This is done by the following block structure: + other coroutines. This is done by one the following block structures: ```v lock c { // read, modify, write c ... } ``` + + ```v + rlock c { + // read c + ... + } + ``` Several variables may be specified: `lock x, y, z { ... }`. They are unlocked in the opposite order. - `d` can be passed to coroutines and accessed *concurrently*, diff --git a/vlib/builtin/cfns.c.v b/vlib/builtin/cfns.c.v index 48da939167..993e5f212e 100644 --- a/vlib/builtin/cfns.c.v +++ b/vlib/builtin/cfns.c.v @@ -402,6 +402,7 @@ fn C.pthread_mutex_unlock(voidptr) int fn C.pthread_rwlockattr_init(voidptr) int fn C.pthread_rwlockattr_setkind_np(voidptr, int) int +fn C.pthread_rwlockattr_setpshared(voidptr, int) int fn C.pthread_rwlock_init(voidptr, voidptr) int fn C.pthread_rwlock_rdlock(voidptr) int fn C.pthread_rwlock_wrlock(voidptr) int diff --git a/vlib/sync/sync_nix.c.v b/vlib/sync/sync_nix.c.v index 1fc4475c2d..a11e9fdf25 100644 --- a/vlib/sync/sync_nix.c.v +++ b/vlib/sync/sync_nix.c.v @@ -33,6 +33,7 @@ pub fn new_rwmutex() &RwMutex { C.pthread_rwlockattr_init(&a.attr) // Give writer priority over readers C.pthread_rwlockattr_setkind_np(&a.attr, C.PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) + C.pthread_rwlockattr_setpshared(&a.attr, C.PTHREAD_PROCESS_PRIVATE) C.pthread_rwlock_init(&m.mutex, &a.attr) return m } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index fcc16e8352..aa33c3fb34 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -450,7 +450,6 @@ pub: pub mut: lockeds []Ident // `x`, `y` in `lock x, y {` is_expr bool - is_rw bool // `rwshared` needs special special handling even in `lock` case typ table.Type } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e9861bd233..de43e808be 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1158,9 +1158,6 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { if call_arg.share == .shared_t { words = 'shared' tok = 'shared' - } else if call_arg.share == .rwshared_t { - words = 'read/write shared' - tok = 'rwshared' } else if call_arg.share == .atomic_t { words = 'atomic' tok = 'atomic' @@ -1176,9 +1173,6 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { if arg.typ.share() == .shared_t { words = ' shared' tok = 'shared' - } else if arg.typ.share() == .rwshared_t { - words = ' read/write shared' - tok = 'rwshared' } else if arg.typ.share() == .atomic_t { words = 'n atomic' tok = 'atomic' @@ -1572,11 +1566,11 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { } mut scope := c.file.scope.innermost(assign_stmt.pos.pos) mut ident_var_info := left.var_info() - if ident_var_info.share in [.shared_t, .rwshared_t] { + if ident_var_info.share == .shared_t { left_type = left_type.set_flag(.shared_f) } - if ident_var_info.share in [.atomic_t, .rwshared_t] { - left_type = left_type.set_flag(.atomic_or_rw) + if ident_var_info.share == .atomic_t { + left_type = left_type.set_flag(.atomic_f) } assign_stmt.left_types[i] = left_type ident_var_info.typ = left_type diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 911c765a6e..6d207d7f0d 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -59,10 +59,8 @@ mut: is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc) is_shared bool // for initialization of hidden mutex in `[rw]shared` literals - is_rwshared bool optionals []string // to avoid duplicates TODO perf, use map shareds []int // types with hidden mutex for which decl has been emitted - rwshareds []int // same with hidden rwmutex inside_ternary int // ?: comma separated statements on a single line inside_map_postfix bool // inside map++/-- postfix expr inside_map_infix bool // inside map<' } else { '.' } - lock_prefix := if node.is_rlock { `r` } else { if typ.has_flag(.atomic_or_rw) { `w` } else { `m` } } + lock_prefix := if node.is_rlock { `r` } else { `w` } lock_prefixes << lock_prefix // keep for unlock - mut_prefix := if lock_prefix == `m` { '' } else { 'Rw' } - g.writeln('sync__${mut_prefix}Mutex_${lock_prefix:c}_lock(${name}${deref}mtx);') + g.writeln('sync__RwMutex_${lock_prefix:c}_lock(${name}${deref}mtx);') } g.stmts(node.stmts) // unlock in reverse order @@ -2092,9 +2075,7 @@ fn (mut g Gen) lock_expr(node ast.LockExpr) { lock_prefix := lock_prefixes[i] name := id.name deref := if id.is_mut { '->' } else { '.' } - mut_prefix := if lock_prefix == `m` { '' } else { 'Rw' } - unlock_type := if lock_prefix == `m` { '' } else { '${lock_prefix:c}_' } - g.writeln('sync__${mut_prefix}Mutex_${unlock_type}unlock(${name}${deref}mtx);') + g.writeln('sync__RwMutex_${lock_prefix:c}_unlock(${name}${deref}mtx);') } } @@ -2237,7 +2218,7 @@ fn (mut g Gen) ident(node ast.Ident) { g.write('(*($styp*)${name}.data)') return } - if !g.is_assign_lhs && (ident_var.share == .shared_t || ident_var.share == .rwshared_t) { + if !g.is_assign_lhs && ident_var.share == .shared_t { g.write('${name}.val') return } @@ -2763,7 +2744,6 @@ const ( fn (mut g Gen) struct_init(struct_init ast.StructInit) { styp := g.typ(struct_init.typ) mut shared_styp := '' // only needed for shared &St{... - mut share_prefix := '' // 'rw' for `rwshared` if styp in skip_struct_init { g.go_back_out(3) return @@ -2775,10 +2755,6 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { g.out.go_back(1) // delete the `&` already generated in `prefix_expr() if g.is_shared { mut shared_typ := struct_init.typ.set_flag(.shared_f) - if g.is_rwshared { - shared_typ = shared_typ.set_flag(.atomic_or_rw) - share_prefix = 'rw' - } shared_styp = g.typ(shared_typ) g.writeln('($shared_styp*)memdup(&($shared_styp){.val = ($styp){') } else { @@ -2883,7 +2859,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { } g.write('}') if g.is_shared { - g.write(', .mtx = sync__new_${share_prefix}mutex()}') + g.write(', .mtx = sync__new_rwmutex()}') if is_amp { g.write(', sizeof($shared_styp))') } diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 9b35c9c8a2..76f34fb840 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -100,16 +100,16 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp pub fn (mut p Parser) call_args() []ast.CallArg { mut args := []ast.CallArg{} for p.tok.kind != .rpar { - is_shared := p.tok.kind in [.key_shared, .key_rwshared] - is_atomic_or_rw := p.tok.kind in [.key_rwshared, .key_atomic] - is_mut := p.tok.kind == .key_mut || is_shared || is_atomic_or_rw + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + is_mut := p.tok.kind == .key_mut || is_shared || is_atomic if is_mut { p.next() } e := p.expr(0) args << ast.CallArg{ is_mut: is_mut - share: table.sharetype_from_flags(is_shared, is_atomic_or_rw) + share: table.sharetype_from_flags(is_shared, is_atomic) expr: e } if p.tok.kind != .rpar { @@ -151,9 +151,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl { if p.tok.kind == .lpar { p.next() // ( is_method = true - is_shared := p.tok.kind in [.key_shared, .key_rwshared] - is_atomic_or_rw := p.tok.kind in [.key_rwshared, .key_atomic] - rec_mut = p.tok.kind == .key_mut || is_shared || is_atomic_or_rw + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + rec_mut = p.tok.kind == .key_mut || is_shared || is_atomic if rec_mut { p.next() // `mut` } @@ -179,8 +179,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl { if is_shared { rec_type = rec_type.set_flag(.shared_f) } - if is_atomic_or_rw { - rec_type = rec_type.set_flag(.atomic_or_rw) + if is_atomic { + rec_type = rec_type.set_flag(.atomic_f) } args << table.Arg{ name: rec_name @@ -400,9 +400,9 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) { mut arg_no := 1 for p.tok.kind != .rpar { arg_name := 'arg_$arg_no' - is_shared := p.tok.kind in [.key_shared, .key_rwshared] - is_atomic_or_rw := p.tok.kind in [.key_rwshared, .key_atomic] - is_mut := p.tok.kind == .key_mut || is_shared || is_atomic_or_rw + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + is_mut := p.tok.kind == .key_mut || is_shared || is_atomic if is_mut { p.next() } @@ -416,13 +416,13 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) { if !arg_type.has_flag(.generic) { if is_shared { p.check_fn_shared_arguments(arg_type, pos) - } else if is_atomic_or_rw { + } else if is_atomic { p.check_fn_atomic_arguments(arg_type, pos) } else { p.check_fn_mutable_arguments(arg_type, pos) } - } else if is_shared || is_atomic_or_rw { - p.error_with_pos('generic object cannot be `atomic`, `shared` or `rwshared`', pos) + } else if is_shared || is_atomic { + p.error_with_pos('generic object cannot be `atomic`or `shared`', pos) } // if arg_type.is_ptr() { // p.error('cannot mut') @@ -432,8 +432,8 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) { if is_shared { arg_type = arg_type.set_flag(.shared_f) } - if is_atomic_or_rw { - arg_type = arg_type.set_flag(.atomic_or_rw) + if is_atomic { + arg_type = arg_type.set_flag(.atomic_f) } } if is_variadic { @@ -456,9 +456,9 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) { } } else { for p.tok.kind != .rpar { - is_shared := p.tok.kind in [.key_shared, .key_rwshared] - is_atomic_or_rw := p.tok.kind in [.key_rwshared, .key_atomic] - mut is_mut := p.tok.kind == .key_mut || is_shared || is_atomic_or_rw + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + mut is_mut := p.tok.kind == .key_mut || is_shared || is_atomic if is_mut { p.next() } @@ -485,20 +485,20 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) { if !typ.has_flag(.generic) { if is_shared { p.check_fn_shared_arguments(typ, pos) - } else if is_atomic_or_rw { + } else if is_atomic { p.check_fn_atomic_arguments(typ, pos) } else { p.check_fn_mutable_arguments(typ, pos) } - } else if is_shared || is_atomic_or_rw { - p.error_with_pos('generic object cannot be `atomic`, `shared` or `rwshared`', pos) + } else if is_shared || is_atomic { + p.error_with_pos('generic object cannot be `atomic` or `shared`', pos) } typ = typ.set_nr_muls(1) if is_shared { typ = typ.set_flag(.shared_f) } - if is_atomic_or_rw { - typ = typ.set_flag(.atomic_or_rw) + if is_atomic { + typ = typ.set_flag(.atomic_f) } } if is_variadic { diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index c9b9ede3e0..b7743da09e 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -133,11 +133,11 @@ pub fn (mut p Parser) parse_type() table.Type { return typ } } - is_shared := p.tok.kind in [.key_shared, .key_rwshared] - is_atomic_or_rw := p.tok.kind in [.key_rwshared, .key_atomic] + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic mut nr_muls := 0 - if p.tok.kind == .key_mut || is_shared || is_atomic_or_rw { + if p.tok.kind == .key_mut || is_shared || is_atomic { nr_muls++ p.next() } @@ -161,8 +161,8 @@ pub fn (mut p Parser) parse_type() table.Type { if is_shared { typ = typ.set_flag(.shared_f) } - if is_atomic_or_rw { - typ = typ.set_flag(.atomic_or_rw) + if is_atomic { + typ = typ.set_flag(.atomic_f) } if nr_muls > 0 { typ = typ.set_nr_muls(nr_muls) diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 1a93c68cca..d259cb1cdd 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -514,7 +514,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { .key_for { return p.for_stmt() } - .name, .key_mut, .key_shared, .key_atomic, .key_rwshared, .key_static, .mul { + .name, .key_mut, .key_shared, .key_atomic, .key_static, .mul { if p.tok.kind == .name { if p.tok.lit == 'sql' { return p.sql_stmt() @@ -783,9 +783,9 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt { pub fn (mut p Parser) parse_ident(language table.Language) ast.Ident { // p.warn('name ') - is_shared := p.tok.kind in [.key_shared, .key_rwshared] - is_atomic_or_rw := p.tok.kind in [.key_rwshared, .key_atomic] - is_mut := p.tok.kind == .key_mut || is_shared || is_atomic_or_rw + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + is_mut := p.tok.kind == .key_mut || is_shared || is_atomic if is_mut { p.next() } @@ -823,7 +823,7 @@ pub fn (mut p Parser) parse_ident(language table.Language) ast.Ident { info: ast.IdentVar{ is_mut: is_mut is_static: is_static - share: table.sharetype_from_flags(is_shared, is_atomic_or_rw) + share: table.sharetype_from_flags(is_shared, is_atomic) } } } else { diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index 0f8808e381..e5bdb1208d 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -16,7 +16,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { p.eat_comments() // Prefix match p.tok.kind { - .key_mut, .key_shared, .key_rwshared, .key_atomic, .key_static { + .key_mut, .key_shared, .key_atomic, .key_static { node = p.name_expr() p.is_stmt_ident = is_stmt_ident } diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index 00a615e593..adbbe745a7 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -42,7 +42,7 @@ pub enum TypeFlag { variadic generic shared_f - atomic_or_rw + atomic_f } /* @@ -54,7 +54,6 @@ pub enum ShareType { mut_t shared_t atomic_t - rwshared_t } pub fn (t ShareType) str() string { @@ -62,7 +61,6 @@ pub fn (t ShareType) str() string { .mut_t { return 'mut' } .shared_t { return 'shared' } .atomic_t { return 'atomic' } - .rwshared_t { return 'rwshared' } } } @@ -78,12 +76,12 @@ pub fn (t Type) atomic_typename() string { } } -pub fn sharetype_from_flags(is_shared, is_atomic_or_rw bool) ShareType { - return ShareType((int(is_atomic_or_rw) << 1) | int(is_shared)) +pub fn sharetype_from_flags(is_shared, is_atomic bool) ShareType { + return ShareType((int(is_atomic) << 1) | int(is_shared)) } pub fn (t Type) share() ShareType { - return sharetype_from_flags(t.has_flag(.shared_f), t.has_flag(.atomic_or_rw)) + return sharetype_from_flags(t.has_flag(.shared_f), t.has_flag(.atomic_f)) } pub fn (types []Type) contains(typ Type) bool { diff --git a/vlib/v/tests/shared_lock_3_test.v b/vlib/v/tests/shared_lock_3_test.v index 7300e8b702..bdae101403 100644 --- a/vlib/v/tests/shared_lock_3_test.v +++ b/vlib/v/tests/shared_lock_3_test.v @@ -6,7 +6,7 @@ mut: a int } -fn f(rwshared x St, shared z St) { +fn f(shared x St, shared z St) { for _ in 0..reads_per_thread { rlock x { // other instances may read at the same time time.sleep_ms(1) @@ -26,14 +26,14 @@ const ( fn test_shared_lock() { // object with separate read/write lock - rwshared x := &St{ + shared x := &St{ a: 5 } shared z := &St{ a: read_threads } for _ in 0..read_threads { - go f(rwshared x, shared z) + go f(shared x, shared z) } for i in 0..writes { lock x { // wait for ongoing reads to finish, don't start new ones diff --git a/vlib/v/tests/shared_lock_4_test.v b/vlib/v/tests/shared_lock_4_test.v index 61f7bba06b..f4ae7f98ba 100644 --- a/vlib/v/tests/shared_lock_4_test.v +++ b/vlib/v/tests/shared_lock_4_test.v @@ -6,7 +6,7 @@ mut: a int } -fn (rwshared x St) f(shared z St) { +fn (shared x St) f(shared z St) { for _ in 0..reads_per_thread { rlock x { // other instances may read at the same time time.sleep_ms(1) @@ -26,7 +26,7 @@ const ( fn test_shared_lock() { // object with separate read/write lock - rwshared x := &St{ + shared x := &St{ a: 5 } shared z := &St{ diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index 54324d6f0a..67112c7ff5 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -109,7 +109,6 @@ pub enum Kind { key_module key_mut key_shared - key_rwshared key_lock key_rlock key_none @@ -231,7 +230,6 @@ fn build_token_str() []string { s[Kind.key_const] = 'const' s[Kind.key_mut] = 'mut' s[Kind.key_shared] = 'shared' - s[Kind.key_rwshared] = 'rwshared' s[Kind.key_lock] = 'lock' s[Kind.key_rlock] = 'rlock' s[Kind.key_type] = 'type'