all: experimental locked concurrency support, part 1 (#5637)

pull/5656/head
Uwe Krüger 2020-07-04 12:44:25 +02:00 committed by GitHub
parent 27149ba8bc
commit 3b067f5f85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 510 additions and 51 deletions

View File

@ -28,7 +28,7 @@ pub mut:
} }
pub fn (mut mh TestMessageHandler) append_message(msg string) { pub fn (mut mh TestMessageHandler) append_message(msg string) {
mh.mtx.lock() mh.mtx.m_lock()
mh.messages << msg mh.messages << msg
mh.mtx.unlock() mh.mtx.unlock()
} }
@ -102,7 +102,7 @@ pub fn (mut ts TestSession) test() {
} }
pub fn (mut m TestMessageHandler) display_message() { pub fn (mut m TestMessageHandler) display_message() {
m.mtx.lock() m.mtx.m_lock()
defer { defer {
m.messages.clear() m.messages.clear()
m.mtx.unlock() m.mtx.unlock()

View File

@ -2,8 +2,8 @@ module main
// This prelude is loaded in every v program compiled with -live, // This prelude is loaded in every v program compiled with -live,
// but only for the shared library. // but only for the shared library.
import live.shared import live.sharedlib
const ( const (
no_warning_live_shared_is_used = shared.is_used no_warning_live_shared_is_used = sharedlib.is_used
) )

View File

@ -1390,7 +1390,7 @@ mut:
fn (mut b St) g() { fn (mut b St) g() {
... ...
b.mtx.lock() b.mtx.m_lock()
// read/modify/write b.x // read/modify/write b.x
... ...
b.mtx.unlock() b.mtx.unlock()
@ -1404,7 +1404,7 @@ fn caller() {
} }
go a.g() go a.g()
... ...
a.mtx.lock() a.mtx.m_lock()
// read/modify/write a.x // read/modify/write a.x
... ...
a.mtx.unlock() a.mtx.unlock()

View File

@ -181,7 +181,7 @@ fn (mut cb Clipboard) free() {
} }
fn (mut cb Clipboard) clear(){ fn (mut cb Clipboard) clear(){
cb.mutex.lock() cb.mutex.m_lock()
C.XSetSelectionOwner(cb.display, cb.selection, C.Window(C.None), C.CurrentTime) C.XSetSelectionOwner(cb.display, cb.selection, C.Window(C.None), C.CurrentTime)
C.XFlush(cb.display) C.XFlush(cb.display)
cb.is_owner = false cb.is_owner = false
@ -200,7 +200,7 @@ fn (cb &Clipboard) take_ownership(){
fn (mut cb Clipboard) set_text(text string) bool { fn (mut cb Clipboard) set_text(text string) bool {
if cb.window == C.Window(C.None) {return false} if cb.window == C.Window(C.None) {return false}
cb.mutex.lock() cb.mutex.m_lock()
cb.text = text cb.text = text
cb.is_owner = true cb.is_owner = true
cb.take_ownership() cb.take_ownership()
@ -238,7 +238,7 @@ fn (mut cb Clipboard) transmit_selection(xse &C.XSelectionEvent) bool {
targets := cb.get_supported_targets() targets := cb.get_supported_targets()
C.XChangeProperty(xse.display, xse.requestor, xse.property, cb.get_atom(.xa_atom), 32, C.PropModeReplace, targets.data, targets.len) C.XChangeProperty(xse.display, xse.requestor, xse.property, cb.get_atom(.xa_atom), 32, C.PropModeReplace, targets.data, targets.len)
} else if cb.is_supported_target(xse.target) && cb.is_owner && cb.text != "" { } else if cb.is_supported_target(xse.target) && cb.is_owner && cb.text != "" {
cb.mutex.lock() cb.mutex.m_lock()
C.XChangeProperty(xse.display, xse.requestor, xse.property, xse.target, 8, C.PropModeReplace, cb.text.str, cb.text.len) C.XChangeProperty(xse.display, xse.requestor, xse.property, xse.target, 8, C.PropModeReplace, cb.text.str, cb.text.len)
cb.mutex.unlock() cb.mutex.unlock()
} else { } else {
@ -265,7 +265,7 @@ fn (mut cb Clipboard) start_listener(){
} }
C.SelectionClear { C.SelectionClear {
if event.xselectionclear.window == cb.window && event.xselectionclear.selection == cb.selection { if event.xselectionclear.window == cb.window && event.xselectionclear.selection == cb.selection {
cb.mutex.lock() cb.mutex.m_lock()
cb.is_owner = false cb.is_owner = false
cb.text = "" cb.text = ""
cb.mutex.unlock() cb.mutex.unlock()
@ -304,7 +304,7 @@ fn (mut cb Clipboard) start_listener(){
} else if event.xselection.target == to_be_requested { } else if event.xselection.target == to_be_requested {
sent_request = false sent_request = false
to_be_requested = C.Atom(0) to_be_requested = C.Atom(0)
cb.mutex.lock() cb.mutex.m_lock()
prop := read_property(event.xselection.display, event.xselection.requestor, event.xselection.property) prop := read_property(event.xselection.display, event.xselection.requestor, event.xselection.property)
C.XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property) C.XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property)
if cb.is_supported_target(prop.actual_type) { if cb.is_supported_target(prop.actual_type) {

View File

@ -1,4 +1,4 @@
module shared module sharedlib
import live import live

View File

@ -2,7 +2,7 @@ module websocket
fn (mut ws Client) write_to_server(buf voidptr, len int) int { fn (mut ws Client) write_to_server(buf voidptr, len int) int {
mut bytes_written := 0 mut bytes_written := 0
ws.write_lock.lock() ws.write_lock.m_lock()
bytes_written = if ws.is_ssl { bytes_written = if ws.is_ssl {
C.SSL_write(ws.ssl, buf, len) C.SSL_write(ws.ssl, buf, len)
} else { } else {

View File

@ -19,7 +19,7 @@ pub struct Client {
// cwebsocket_subprotocol *subprotocol; // cwebsocket_subprotocol *subprotocol;
// cwebsocket_subprotocol *subprotocols[]; // cwebsocket_subprotocol *subprotocols[];
mut: mut:
lock &sync.Mutex = sync.new_mutex() mtx &sync.Mutex = sync.new_mutex()
write_lock &sync.Mutex = sync.new_mutex() write_lock &sync.Mutex = sync.new_mutex()
state State state State
socket net.Socket socket net.Socket
@ -132,9 +132,9 @@ pub fn (mut ws Client) connect() int {
// do nothing // do nothing
} }
} }
ws.lock.lock() ws.mtx.m_lock()
ws.state = .connecting ws.state = .connecting
ws.lock.unlock() ws.mtx.unlock()
uri := ws.parse_uri() uri := ws.parse_uri()
nonce := get_nonce(ws.nonce_size) nonce := get_nonce(ws.nonce_size)
seckey := base64.encode(nonce) seckey := base64.encode(nonce)
@ -160,17 +160,17 @@ pub fn (mut ws Client) connect() int {
if ws.is_ssl { if ws.is_ssl {
ws.connect_ssl() ws.connect_ssl()
} }
ws.lock.lock() ws.mtx.m_lock()
ws.state = .connected ws.state = .connected
ws.lock.unlock() ws.mtx.unlock()
res := ws.write_to_server(handshake.str, handshake.len) res := ws.write_to_server(handshake.str, handshake.len)
if res <= 0 { if res <= 0 {
l.f('Handshake failed.') l.f('Handshake failed.')
} }
ws.read_handshake(seckey) ws.read_handshake(seckey)
ws.lock.lock() ws.mtx.m_lock()
ws.state = .open ws.state = .open
ws.lock.unlock() ws.mtx.unlock()
ws.send_open_event() ws.send_open_event()
unsafe { unsafe {
handshake.free() handshake.free()
@ -182,9 +182,9 @@ pub fn (mut ws Client) connect() int {
pub fn (mut ws Client) close(code int, message string) { pub fn (mut ws Client) close(code int, message string) {
if ws.state != .closed && ws.socket.sockfd > 1 { if ws.state != .closed && ws.socket.sockfd > 1 {
ws.lock.lock() ws.mtx.m_lock()
ws.state = .closing ws.state = .closing
ws.lock.unlock() ws.mtx.unlock()
mut code32 := 0 mut code32 := 0
if code > 0 { if code > 0 {
code_ := C.htons(code) code_ := C.htons(code)
@ -223,9 +223,9 @@ pub fn (mut ws Client) close(code int, message string) {
} }
ws.fragments = [] ws.fragments = []
ws.send_close_event() ws.send_close_event()
ws.lock.lock() ws.mtx.m_lock()
ws.state = .closed ws.state = .closed
ws.lock.unlock() ws.mtx.unlock()
unsafe { unsafe {
} }
// TODO impl autoreconnect // TODO impl autoreconnect

View File

@ -139,7 +139,7 @@ fn process_in_thread(mut pool PoolProcessor, task_id int) {
if pool.ntask >= ilen { if pool.ntask >= ilen {
break break
} }
pool.ntask_mtx.lock() pool.ntask_mtx.m_lock()
idx = pool.ntask idx = pool.ntask
pool.ntask++ pool.ntask++
pool.ntask_mtx.unlock() pool.ntask_mtx.unlock()

View File

@ -17,7 +17,8 @@ pub fn new_mutex() &Mutex {
return m return m
} }
pub fn (mut m Mutex) lock() { // m_lock(), for *manual* mutex handling, since `lock` is a keyword
pub fn (mut m Mutex) m_lock() {
C.pthread_mutex_lock(&m.mutex) C.pthread_mutex_lock(&m.mutex)
} }

View File

@ -43,7 +43,7 @@ pub fn new_mutex() &Mutex {
return sm return sm
} }
pub fn (mut m Mutex) lock() { pub fn (mut m Mutex) m_lock() {
// if mutex handle not initalized // if mutex handle not initalized
if isnil(m.mx) { if isnil(m.mx) {
m.mx = MHANDLE(C.CreateMutex(0, false, 0)) m.mx = MHANDLE(C.CreateMutex(0, false, 0))

View File

@ -10,7 +10,7 @@ mut:
} }
pub fn (mut w Waiter) wait() { pub fn (mut w Waiter) wait() {
w.mx.lock() w.mx.m_lock()
} }
pub fn (mut w Waiter) stop() { pub fn (mut w Waiter) stop() {

View File

@ -30,7 +30,7 @@ pub fn new_waitgroup() &WaitGroup {
// add panics if task count drops below zero. // add panics if task count drops below zero.
pub fn (mut wg WaitGroup) add(delta int) { pub fn (mut wg WaitGroup) add(delta int) {
// protect task_count // protect task_count
wg.task_count_mutex.lock() wg.task_count_mutex.m_lock()
defer { defer {
wg.task_count_mutex.unlock() wg.task_count_mutex.unlock()
} }

View File

@ -11,7 +11,7 @@ pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr | pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr |
CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr |
IndexExpr | InfixExpr | IntegerLiteral | Likely | MapInit | MatchExpr | None | OrExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr | None | OrExpr |
ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral |
StringLiteral | StructInit | Type | TypeOf StringLiteral | StructInit | Type | TypeOf
@ -232,6 +232,7 @@ pub:
receiver_pos token.Position receiver_pos token.Position
is_method bool is_method bool
rec_mut bool // is receiver mutable rec_mut bool // is receiver mutable
rec_share table.ShareType
language table.Language language table.Language
no_body bool // just a definition `fn C.malloc()` no_body bool // just a definition `fn C.malloc()`
is_builtin bool // this function is defined in builtin/strconv is_builtin bool // this function is defined in builtin/strconv
@ -271,6 +272,7 @@ pub mut:
pub struct CallArg { pub struct CallArg {
pub: pub:
is_mut bool is_mut bool
share table.ShareType
expr Expr expr Expr
pub mut: pub mut:
typ table.Type typ table.Type
@ -301,6 +303,7 @@ pub struct Var {
pub: pub:
name string name string
expr Expr expr Expr
share table.ShareType
is_mut bool is_mut bool
is_arg bool // fn args should not be autofreed is_arg bool // fn args should not be autofreed
pub mut: pub mut:
@ -344,6 +347,7 @@ pub mut:
is_mut bool is_mut bool
is_static bool is_static bool
is_optional bool is_optional bool
share table.ShareType
} }
pub type IdentInfo = IdentFn | IdentVar pub type IdentInfo = IdentFn | IdentVar
@ -438,6 +442,18 @@ pub:
comment Comment comment Comment
} }
pub struct LockExpr {
pub:
stmts []Stmt
is_rlock bool
pos token.Position
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
}
pub struct MatchExpr { pub struct MatchExpr {
pub: pub:
tok_kind token.Kind tok_kind token.Kind

View File

@ -1128,12 +1128,39 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
} }
if call_arg.is_mut { if call_arg.is_mut {
c.fail_if_immutable(call_arg.expr) c.fail_if_immutable(call_arg.expr)
if !arg.is_mut { if !arg.is_mut {
c.error('`$arg.name` argument is not mutable, `mut` is not needed`', call_arg.expr.position()) mut words := 'mutable'
mut tok := 'mut'
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'
}
c.error('`$arg.name` argument is not $words, `$tok` is not needed`', call_arg.expr.position())
} else if arg.typ.share() != call_arg.share {
c.error('wrong shared type', call_arg.expr.position())
} }
} else { } else {
if arg.is_mut { if arg.is_mut && (!call_arg.is_mut || arg.typ.share() != call_arg.share) {
c.error('`$arg.name` is a mutable argument, you need to provide `mut`: `${call_expr.name}(mut ...)`', mut words := ' mutable'
mut tok := 'mut'
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'
}
c.error('`$arg.name` is a$words argument, you need to provide `$tok`: `${call_expr.name}($tok ...)`',
call_arg.expr.position()) call_arg.expr.position())
} }
} }
@ -1497,6 +1524,13 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
} }
mut scope := c.file.scope.innermost(assign_stmt.pos.pos) mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
mut ident_var_info := left.var_info() mut ident_var_info := left.var_info()
if ident_var_info.share in [.shared_t, .rwshared_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)
}
assign_stmt.left_types[i] = left_type
ident_var_info.typ = left_type ident_var_info.typ = left_type
left.info = ident_var_info left.info = ident_var_info
scope.update_var_type(left.name, left_type) scope.update_var_type(left.name, left_type)
@ -2116,6 +2150,9 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
ast.IntegerLiteral { ast.IntegerLiteral {
return table.any_int_type return table.any_int_type
} }
ast.LockExpr {
return c.lock_expr(mut node)
}
ast.MapInit { ast.MapInit {
return c.map_init(mut node) return c.map_init(mut node)
} }
@ -2494,6 +2531,15 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol
c.error(err_details, node.pos) c.error(err_details, node.pos)
} }
pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type {
for id in node.lockeds {
c.ident(mut id)
}
c.stmts(node.stmts)
// void for now... maybe sometime `x := lock a { a.getval() }`
return table.void_type
}
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
mut expr_required := false mut expr_required := false
if c.expected_type != table.void_type { if c.expected_type != table.void_type {

View File

@ -835,6 +835,9 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
ast.IntegerLiteral { ast.IntegerLiteral {
f.write(node.val) f.write(node.val)
} }
ast.LockExpr {
f.lock_expr(node)
}
ast.MapInit { ast.MapInit {
if node.keys.len == 0 { if node.keys.len == 0 {
mut ktyp := node.key_type mut ktyp := node.key_type
@ -1164,6 +1167,20 @@ pub fn (mut f Fmt) short_module(name string) string {
return '${aname}.$symname' return '${aname}.$symname'
} }
pub fn (mut f Fmt) lock_expr(lex ast.LockExpr) {
f.write('lock ')
for i, v in lex.lockeds {
if i > 0 {
f.write(', ')
}
f.expr(v)
}
f.write(' {')
f.writeln('')
f.stmts(lex.stmts)
f.write('}')
}
pub fn (mut f Fmt) if_expr(it ast.IfExpr) { pub fn (mut f Fmt) if_expr(it ast.IfExpr) {
single_line := it.branches.len == 2 && it.has_else && single_line := it.branches.len == 2 && it.has_else &&
it.branches[0].stmts.len == 1 && it.branches[1].stmts.len == 1 && it.branches[0].stmts.len == 1 && it.branches[1].stmts.len == 1 &&

View File

@ -58,7 +58,11 @@ mut:
is_array_set bool is_array_set bool
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc 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_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 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_ternary int // ?: comma separated statements on a single line
inside_map_postfix bool // inside map++/-- postfix expr inside_map_postfix bool // inside map++/-- postfix expr
inside_map_infix bool // inside map<</+=/-= infix expr inside_map_infix bool // inside map<</+=/-= infix expr
@ -329,7 +333,11 @@ fn (g &Gen) typ(t table.Type) string {
} }
fn (g &Gen) base_type(t table.Type) string { fn (g &Gen) base_type(t table.Type) string {
mut styp := g.cc_type(t) share := t.share()
mut styp := if share == .atomic_t { t.atomic_typename() } else { g.cc_type(t) }
if t.has_flag(.shared_f) {
styp = g.find_or_register_shared(t, styp)
}
nr_muls := t.nr_muls() nr_muls := t.nr_muls()
if nr_muls > 0 { if nr_muls > 0 {
styp += strings.repeat(`*`, nr_muls) styp += strings.repeat(`*`, nr_muls)
@ -386,6 +394,27 @@ fn (mut g Gen) register_optional(t table.Type) string {
return styp return styp
} }
fn (mut g Gen) find_or_register_shared(t table.Type, base string) string {
is_rw := t.has_flag(.atomic_or_rw)
prefix := if is_rw { 'rw' } else { '' }
sh_typ := '__${prefix}shared__$base'
t_idx := t.idx()
if (is_rw && t_idx in g.rwshareds) || (!is_rw && t_idx in g.shareds) {
return sh_typ
}
// TODO: These two should become different...
mtx_typ := if is_rw { 'sync__Mutex' } else { 'sync__Mutex' }
g.hotcode_definitions.writeln('struct $sh_typ { $base val; $mtx_typ* mtx; };')
g.typedefs2.writeln('typedef struct $sh_typ $sh_typ;')
// println('registered shared type $sh_typ')
if is_rw {
g.rwshareds << t_idx
} else {
g.shareds << t_idx
}
return sh_typ
}
// cc_type returns the Cleaned Concrete Type name, *without ptr*, // cc_type returns the Cleaned Concrete Type name, *without ptr*,
// i.e. it's always just Cat, not Cat_ptr: // i.e. it's always just Cat, not Cat_ptr:
fn (g &Gen) cc_type(t table.Type) string { fn (g &Gen) cc_type(t table.Type) string {
@ -1141,7 +1170,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
// `a := 1` | `a,b := 1,2` // `a := 1` | `a,b := 1,2`
for i, left in assign_stmt.left { for i, left in assign_stmt.left {
mut var_type := assign_stmt.left_types[i] mut var_type := assign_stmt.left_types[i]
val_type := assign_stmt.right_types[i] mut val_type := assign_stmt.right_types[i]
val := assign_stmt.right[i] val := assign_stmt.right[i]
mut is_call := false mut is_call := false
mut blank_assign := false mut blank_assign := false
@ -1151,6 +1180,15 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
// id_info := ident.var_info() // id_info := ident.var_info()
// var_type = id_info.typ // var_type = id_info.typ
blank_assign = ident.kind == .blank_ident blank_assign = ident.kind == .blank_ident
if ident.info is ast.IdentVar {
share := (ident.info as ast.IdentVar).share
if share in [.shared_t, .rwshared_t] {
var_type = var_type.set_flag(.shared_f)
}
if share in [.atomic_t, .rwshared_t] {
var_type = var_type.set_flag(.atomic_or_rw)
}
}
} }
styp := g.typ(var_type) styp := g.typ(var_type)
mut is_fixed_array_init := false mut is_fixed_array_init := false
@ -1278,6 +1316,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
if unwrap_optional { if unwrap_optional {
g.write('*($styp*)') g.write('*($styp*)')
} }
g.is_shared = var_type.has_flag(.shared_f)
g.is_rwshared = var_type.has_flag(.atomic_or_rw)
if !cloned { if !cloned {
if is_decl { if is_decl {
if is_fixed_array_init && !has_val { if is_fixed_array_init && !has_val {
@ -1303,6 +1343,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
g.write(' })') g.write(' })')
g.is_array_set = false g.is_array_set = false
} }
g.is_rwshared = false
g.is_shared = false
} }
g.right_is_opt = false g.right_is_opt = false
g.is_assign_rhs = false g.is_assign_rhs = false
@ -1622,6 +1664,9 @@ fn (mut g Gen) expr(node ast.Expr) {
g.write(node.val) // .int().str()) g.write(node.val) // .int().str())
} }
} }
ast.LockExpr {
g.lock_expr(node)
}
ast.MatchExpr { ast.MatchExpr {
g.match_expr(node) g.match_expr(node)
} }
@ -1721,6 +1766,9 @@ fn (mut g Gen) expr(node ast.Expr) {
// g.write('. /*typ= $it.expr_type */') // ${g.typ(it.expr_type)} /') // g.write('. /*typ= $it.expr_type */') // ${g.typ(it.expr_type)} /')
g.write('.') g.write('.')
} }
if node.expr_type.has_flag(.shared_f) {
g.write('val.')
}
if node.expr_type == 0 { if node.expr_type == 0 {
verror('cgen: SelectorExpr | expr_type: 0 | it.expr: `$node.expr` | field: `$node.field_name` | file: $g.file.path | line: $node.pos.line_nr') verror('cgen: SelectorExpr | expr_type: 0 | it.expr: `$node.expr` | field: `$node.field_name` | file: $g.file.path | line: $node.pos.line_nr')
} }
@ -2011,6 +2059,36 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
} }
} }
fn (mut g Gen) lock_expr(node ast.LockExpr) {
for id in node.lockeds {
name := id.name
deref := if id.is_mut { '->' } else { '.' }
// TODO: use 3 different locking functions
if node.is_rlock {
g.writeln('sync__Mutex_m_lock(${name}${deref}mtx);')
} else if id.var_info().typ.has_flag(.atomic_or_rw) {
g.writeln('sync__Mutex_m_lock(${name}${deref}mtx);')
} else {
g.writeln('sync__Mutex_m_lock(${name}${deref}mtx);')
}
}
g.stmts(node.stmts)
// unlock in reverse order
for i := node.lockeds.len-1; i >= 0; i-- {
id := node.lockeds[i]
name := id.name
deref := if id.is_mut { '->' } else { '.' }
// TODO: use 3 different unlocking functions
if node.is_rlock {
g.writeln('sync__Mutex_unlock(${name}${deref}mtx);')
} else if id.var_info().typ.has_flag(.atomic_or_rw) {
g.writeln('sync__Mutex_unlock(${name}${deref}mtx);')
} else {
g.writeln('sync__Mutex_unlock(${name}${deref}mtx);')
}
}
}
fn (mut g Gen) match_expr(node ast.MatchExpr) { fn (mut g Gen) match_expr(node ast.MatchExpr) {
// println('match expr typ=$it.expr_type') // println('match expr typ=$it.expr_type')
// TODO // TODO
@ -2150,6 +2228,10 @@ fn (mut g Gen) ident(node ast.Ident) {
g.write('(*($styp*)${name}.data)') g.write('(*($styp*)${name}.data)')
return return
} }
if !g.is_assign_lhs && (ident_var.share == .shared_t || ident_var.share == .rwshared_t) {
g.write('${name}.val')
return
}
} }
g.write(g.get_ternary_name(name)) g.write(g.get_ternary_name(name))
} }
@ -2671,6 +2753,7 @@ const (
fn (mut g Gen) struct_init(struct_init ast.StructInit) { fn (mut g Gen) struct_init(struct_init ast.StructInit) {
styp := g.typ(struct_init.typ) styp := g.typ(struct_init.typ)
mut shared_styp := '' // only needed for shared &St{...
if styp in skip_struct_init { if styp in skip_struct_init {
g.go_back_out(3) g.go_back_out(3)
return return
@ -2680,10 +2763,23 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
g.is_amp = false // reset the flag immediately so that other struct inits in this expr are handled correctly g.is_amp = false // reset the flag immediately so that other struct inits in this expr are handled correctly
if is_amp { if is_amp {
g.out.go_back(1) // delete the `&` already generated in `prefix_expr() 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)
}
shared_styp = g.typ(shared_typ)
g.writeln('($shared_styp*)memdup(&($shared_styp){.val = ($styp){')
} else {
g.write('($styp*)memdup(&($styp){') g.write('($styp*)memdup(&($styp){')
}
} else {
if g.is_shared {
g.writeln('{.val = {')
} else { } else {
g.writeln('($styp){') g.writeln('($styp){')
} }
}
// mut fields := []string{} // mut fields := []string{}
mut inited_fields := map[string]int{} // TODO this is done in checker, move to ast node mut inited_fields := map[string]int{} // TODO this is done in checker, move to ast node
/* /*
@ -2771,7 +2867,12 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
g.write('\n#ifndef __cplusplus\n0\n#endif\n') g.write('\n#ifndef __cplusplus\n0\n#endif\n')
} }
g.write('}') g.write('}')
if g.is_shared {
g.write(', .mtx = sync__new_mutex()}')
if is_amp { if is_amp {
g.write(', sizeof($shared_styp))')
}
} else if is_amp {
g.write(', sizeof($styp))') g.write(', sizeof($styp))')
} }
} }

View File

@ -563,6 +563,9 @@ fn (mut g JsGen) expr(node ast.Expr) {
ast.IntegerLiteral { ast.IntegerLiteral {
g.write(it.val) g.write(it.val)
} }
ast.LockExpr {
g.gen_lock_expr(it)
}
ast.MapInit { ast.MapInit {
g.gen_map_init_expr(it) g.gen_map_init_expr(it)
} }
@ -1192,6 +1195,10 @@ fn (mut g JsGen) gen_ident(node ast.Ident) {
g.write(name) g.write(name)
} }
fn (mut g JsGen) gen_lock_expr(node ast.LockExpr) {
// TODO: implement this
}
fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { fn (mut g JsGen) gen_if_expr(node ast.IfExpr) {
type_sym := g.table.get_type_symbol(node.typ) type_sym := g.table.get_type_symbol(node.typ)

View File

@ -4,6 +4,7 @@
module parser module parser
import v.ast import v.ast
import v.table
fn (mut p Parser) assign_stmt() ast.Stmt { fn (mut p Parser) assign_stmt() ast.Stmt {
return p.partial_assign_stmt(p.expr_list()) return p.partial_assign_stmt(p.expr_list())
@ -106,16 +107,22 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr) ast.Stmt {
if p.scope.known_var(lx.name) { if p.scope.known_var(lx.name) {
p.error_with_pos('redefinition of `$lx.name`', lx.pos) p.error_with_pos('redefinition of `$lx.name`', lx.pos)
} }
mut share := table.ShareType(0)
if lx.info is ast.IdentVar {
share = (lx.info as ast.IdentVar).share
}
if left.len == right.len { if left.len == right.len {
p.scope.register(lx.name, ast.Var{ p.scope.register(lx.name, ast.Var{
name: lx.name name: lx.name
expr: right[i] expr: right[i]
share: share
is_mut: lx.is_mut || p.inside_for is_mut: lx.is_mut || p.inside_for
pos: lx.pos pos: lx.pos
}) })
} else { } else {
p.scope.register(lx.name, ast.Var{ p.scope.register(lx.name, ast.Var{
name: lx.name name: lx.name
share: share
is_mut: lx.is_mut || p.inside_for is_mut: lx.is_mut || p.inside_for
pos: lx.pos pos: lx.pos
}) })

View File

@ -100,14 +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 { pub fn (mut p Parser) call_args() []ast.CallArg {
mut args := []ast.CallArg{} mut args := []ast.CallArg{}
for p.tok.kind != .rpar { for p.tok.kind != .rpar {
mut is_mut := false is_shared := p.tok.kind in [.key_shared, .key_rwshared]
if p.tok.kind == .key_mut { 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
if is_mut {
p.next() p.next()
is_mut = true
} }
e := p.expr(0) e := p.expr(0)
args << ast.CallArg{ args << ast.CallArg{
is_mut: is_mut is_mut: is_mut
share: table.sharetype_from_flags(is_shared, is_atomic_or_rw)
expr: e expr: e
} }
if p.tok.kind != .rpar { if p.tok.kind != .rpar {
@ -149,7 +151,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
if p.tok.kind == .lpar { if p.tok.kind == .lpar {
p.next() // ( p.next() // (
is_method = true is_method = true
rec_mut = p.tok.kind == .key_mut 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
if rec_mut { if rec_mut {
p.next() // `mut` p.next() // `mut`
} }
@ -172,6 +176,12 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
if is_amp && rec_mut { if is_amp && rec_mut {
p.error('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`') p.error('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`')
} }
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)
}
args << table.Arg{ args << table.Arg{
name: rec_name name: rec_name
is_mut: rec_mut is_mut: rec_mut
@ -390,7 +400,9 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) {
mut arg_no := 1 mut arg_no := 1
for p.tok.kind != .rpar { for p.tok.kind != .rpar {
arg_name := 'arg_$arg_no' arg_name := 'arg_$arg_no'
is_mut := p.tok.kind == .key_mut 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
if is_mut { if is_mut {
p.next() p.next()
} }
@ -402,13 +414,27 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) {
mut arg_type := p.parse_type() mut arg_type := p.parse_type()
if is_mut { if is_mut {
if !arg_type.has_flag(.generic) { if !arg_type.has_flag(.generic) {
if is_shared {
p.check_fn_shared_arguments(arg_type, pos)
} else if is_atomic_or_rw {
p.check_fn_atomic_arguments(arg_type, pos)
} else {
p.check_fn_mutable_arguments(arg_type, pos) 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)
}
// if arg_type.is_ptr() { // if arg_type.is_ptr() {
// p.error('cannot mut') // p.error('cannot mut')
// } // }
// arg_type = arg_type.to_ptr() // arg_type = arg_type.to_ptr()
arg_type = arg_type.set_nr_muls(1) arg_type = arg_type.set_nr_muls(1)
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_variadic { if is_variadic {
arg_type = arg_type.set_flag(.variadic) arg_type = arg_type.set_flag(.variadic)
@ -430,7 +456,9 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) {
} }
} else { } else {
for p.tok.kind != .rpar { for p.tok.kind != .rpar {
mut is_mut := p.tok.kind == .key_mut 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
if is_mut { if is_mut {
p.next() p.next()
} }
@ -455,9 +483,23 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool, bool) {
mut typ := p.parse_type() mut typ := p.parse_type()
if is_mut { if is_mut {
if !typ.has_flag(.generic) { if !typ.has_flag(.generic) {
if is_shared {
p.check_fn_shared_arguments(typ, pos)
} else if is_atomic_or_rw {
p.check_fn_atomic_arguments(typ, pos)
} else {
p.check_fn_mutable_arguments(typ, pos) 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)
}
typ = typ.set_nr_muls(1) 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_variadic { if is_variadic {
typ = typ.set_flag(.variadic) typ = typ.set_flag(.variadic)
@ -497,6 +539,22 @@ fn (mut p Parser) check_fn_mutable_arguments(typ table.Type, pos token.Position)
} }
} }
fn (mut p Parser) check_fn_shared_arguments(typ table.Type, pos token.Position) {
sym := p.table.get_type_symbol(typ)
if sym.kind !in [.array, .struct_, .map, .placeholder] && !typ.is_ptr() {
p.error_with_pos('shared arguments are only allowed for arrays, maps, and structs\n', pos)
}
}
fn (mut p Parser) check_fn_atomic_arguments(typ table.Type, pos token.Position) {
sym := p.table.get_type_symbol(typ)
if sym.kind !in [.u32, .int, .u64] {
p.error_with_pos('atomic arguments are only allowed for 32/64 bit integers\n' +
'use shared arguments instead: `fn foo(atomic n $sym.name) {` => `fn foo(shared n $sym.name) {`',
pos)
}
}
fn (mut p Parser) fn_redefinition_error(name string) { fn (mut p Parser) fn_redefinition_error(name string) {
// Find where this function was already declared // Find where this function was already declared
// TODO // TODO

View File

@ -0,0 +1,35 @@
module parser
import v.ast
import v.table
fn (mut p Parser) lock_expr() ast.LockExpr {
pos := p.tok.position()
is_rlock := p.tok.kind == .key_rlock
p.next()
mut lockeds := []ast.Ident{}
for p.tok.kind == .name {
lockeds << ast.Ident{
language: table.Language.v
kind: .variable
pos: p.tok.position()
name: p.tok.lit
is_mut: true
info: ast.IdentVar{}
}
p.next()
if p.tok.kind == .lcbr {
break
}
p.check(.comma)
}
stmts := p.parse_block()
return ast.LockExpr {
lockeds: lockeds
stmts: stmts
is_rlock: is_rlock
pos: pos
}
}

View File

@ -105,8 +105,10 @@ pub fn (mut p Parser) parse_type() table.Type {
p.next() p.next()
is_optional = true is_optional = true
} }
is_shared := p.tok.kind in [.key_shared, .key_rwshared]
is_atomic_or_rw := p.tok.kind in [.key_rwshared, .key_atomic]
mut nr_muls := 0 mut nr_muls := 0
if p.tok.kind == .key_mut { if p.tok.kind == .key_mut || is_shared || is_atomic_or_rw {
nr_muls++ nr_muls++
p.next() p.next()
} }
@ -139,6 +141,12 @@ pub fn (mut p Parser) parse_type() table.Type {
if is_optional { if is_optional {
typ = typ.set_flag(.optional) typ = typ.set_flag(.optional)
} }
if is_shared {
typ = typ.set_flag(.shared_f)
}
if is_atomic_or_rw {
typ = typ.set_flag(.atomic_or_rw)
}
if nr_muls > 0 { if nr_muls > 0 {
typ = typ.set_nr_muls(nr_muls) typ = typ.set_nr_muls(nr_muls)
} }

View File

@ -506,7 +506,7 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
.key_for { .key_for {
return p.for_stmt() return p.for_stmt()
} }
.name, .key_mut, .key_static, .mul { .name, .key_mut, .key_shared, .key_atomic, .key_rwshared, .key_static, .mul {
if p.tok.kind == .name { if p.tok.kind == .name {
if p.tok.lit == 'sql' { if p.tok.lit == 'sql' {
return p.sql_stmt() return p.sql_stmt()
@ -750,7 +750,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
left0 := left[0] left0 := left[0]
if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() { if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() {
return p.partial_assign_stmt(left) return p.partial_assign_stmt(left)
} else if is_top_level && tok.kind !in [.key_if, .key_match] && } else if is_top_level && tok.kind !in [.key_if, .key_match, .key_lock, .key_rlock] &&
left0 !is ast.CallExpr && left0 !is ast.PostfixExpr && !(left0 is ast.InfixExpr && left0 !is ast.CallExpr && left0 !is ast.PostfixExpr && !(left0 is ast.InfixExpr &&
(left0 as ast.InfixExpr).op == .left_shift) && (left0 as ast.InfixExpr).op == .left_shift) &&
left0 !is ast.ComptimeCall { left0 !is ast.ComptimeCall {
@ -773,7 +773,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 { pub fn (mut p Parser) parse_ident(language table.Language) ast.Ident {
// p.warn('name ') // p.warn('name ')
is_mut := p.tok.kind == .key_mut 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
if is_mut { if is_mut {
p.next() p.next()
} }
@ -811,6 +813,7 @@ pub fn (mut p Parser) parse_ident(language table.Language) ast.Ident {
info: ast.IdentVar{ info: ast.IdentVar{
is_mut: is_mut is_mut: is_mut
is_static: is_static is_static: is_static
share: table.sharetype_from_flags(is_shared, is_atomic_or_rw)
} }
} }
} else { } else {

View File

@ -16,7 +16,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
p.eat_comments() p.eat_comments()
// Prefix // Prefix
match p.tok.kind { match p.tok.kind {
.key_mut, .key_static { .key_mut, .key_shared, .key_rwshared, .key_atomic, .key_static {
node = p.name_expr() node = p.name_expr()
p.is_stmt_ident = is_stmt_ident p.is_stmt_ident = is_stmt_ident
} }
@ -79,6 +79,9 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
.key_if { .key_if {
node = p.if_expr() node = p.if_expr()
} }
.key_lock, .key_rlock {
node = p.lock_expr()
}
.lsbr { .lsbr {
if p.expecting_type { if p.expecting_type {
// parse json.decode type (`json.decode([]User, s)`) // parse json.decode type (`json.decode([]User, s)`)

View File

@ -41,6 +41,49 @@ pub enum TypeFlag {
optional optional
variadic variadic
generic generic
shared_f
atomic_or_rw
}
/*
To save precious TypeFlag bits the 4 possible ShareTypes are coded in the two
bits `shared` and `atomic_or_rw` (see sharetype_from_flags() below).
*/
pub enum ShareType {
mut_t
shared_t
atomic_t
rwshared_t
}
pub fn (t ShareType) str() string {
match t {
.mut_t { return 'mut' }
.shared_t { return 'shared' }
.atomic_t { return 'atomic' }
.rwshared_t { return 'rwshared' }
}
}
// <atomic.h> defines special typenames
pub fn (t Type) atomic_typename() string {
idx := t.idx()
match idx {
u32_type_idx { return 'atomic_uint' }
int_type_idx { return 'atomic_int' }
u64_type_idx { return 'atomic_ullong' }
i64_type_idx { return 'atomic_llong' }
else { return 'unknown_atomic' }
}
}
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 (t Type) share() ShareType {
return sharetype_from_flags(t.has_flag(.shared_f), t.has_flag(.atomic_or_rw))
} }
pub fn (types []Type) contains(typ Type) bool { pub fn (types []Type) contains(typ Type) bool {

View File

@ -0,0 +1,53 @@
import sync
import time
struct St {
mut:
a int
}
fn (shared x St) f(shared y St, shared z St) {
for _ in 0..101 {
lock x, y {
tmp := y.a
y.a = x.a
x.a = tmp
}
}
lock z {
z.a--
}
}
fn test_shared_receiver_lock() {
shared x := &St{
a: 5
}
shared y := &St{
a: 7
}
shared z := &St{
a: 1
}
go x.f(shared y, shared z)
for _ in 0..100 {
lock x, y {
tmp := x.a
x.a = y.a
y.a = tmp
}
}
// the following would be a good application for a channel
for finished := false; ; {
lock z {
finished = z.a == 0
}
if finished {
break
}
time.sleep_ms(100)
}
lock x, y {
assert x.a == 7 && y.a == 5
}
}

View File

@ -0,0 +1,53 @@
import sync
import time
struct St {
mut:
a int
}
fn f(shared x St, shared y St, shared z St) {
for _ in 0..101 {
lock x, y {
tmp := y.a
y.a = x.a
x.a = tmp
}
}
lock z {
z.a--
}
}
fn test_shared_lock() {
shared x := &St{
a: 5
}
shared y := &St{
a: 7
}
shared z := &St{
a: 1
}
go f(shared x, shared y, shared z)
for _ in 0..100 {
lock x, y {
tmp := x.a
x.a = y.a
y.a = tmp
}
}
// the following would be a good application for a channel
for finished := false; ; {
lock z {
finished = z.a == 0
}
if finished {
break
}
time.sleep_ms(100)
}
lock x, y {
assert x.a == 7 && y.a == 5
}
}

View File

@ -108,6 +108,10 @@ pub enum Kind {
key_match key_match
key_module key_module
key_mut key_mut
key_shared
key_rwshared
key_lock
key_rlock
key_none key_none
key_return key_return
key_select key_select
@ -226,6 +230,10 @@ fn build_token_str() []string {
s[Kind.key_goto] = 'goto' s[Kind.key_goto] = 'goto'
s[Kind.key_const] = 'const' s[Kind.key_const] = 'const'
s[Kind.key_mut] = 'mut' 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' s[Kind.key_type] = 'type'
s[Kind.key_for] = 'for' s[Kind.key_for] = 'for'
s[Kind.key_switch] = 'switch' s[Kind.key_switch] = 'switch'