Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add try_lock to mutex/rwmutex #20381

Merged
merged 4 commits into from Jan 5, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 42 additions & 0 deletions vlib/sync/mutex_test.v
@@ -0,0 +1,42 @@
import sync

struct Counter {
pub mut:
i int
}

fn write_10000(mut co Counter, mut mx sync.Mutex) {
mx.@lock()
co.i = 10000
mx.unlock()
}

fn test_mutex() {
mut co := &Counter{10086}
mut mx := sync.new_mutex()
mx.@lock()
co.i = 888
th := spawn write_10000(mut co, mut mx)
mx.unlock() // after mx unlock, thread write_10000 can continue
th.wait()
mx.destroy()
assert co.i == 10000
}

fn test_try_lock_mutex() {
// In Windows, try_lock only avalible after Windows 7
$if windows {
$if !windows_7 ? {
return
}
}
mut mx := sync.new_mutex()
mx.@lock()
try_fail := mx.try_lock()
assert try_fail == false
mx.unlock()
try_sucess := mx.try_lock()
assert try_sucess == true
mx.unlock() // you must unlock it, after try_lock sucess
mx.destroy()
}
74 changes: 74 additions & 0 deletions vlib/sync/rwmutex_test.v
@@ -0,0 +1,74 @@
import sync
import time

struct Counter {
pub mut:
i int
}

fn write_10000(mut co Counter, mut mx sync.RwMutex) {
mx.@lock()
co.i = 10000
mx.unlock()
}

fn test_rwmutex() {
mut co := &Counter{10086}
mut mx := sync.new_rwmutex()
mx.@lock()
co.i = 888
th1 := spawn write_10000(mut co, mut mx)
mx.unlock() // after mx unlock, thread write_10000 can continue
th1.wait()
assert co.i == 10000

mx.@rlock()
th2 := spawn write_10000(mut co, mut mx) // write_10000 will be blocked
co.i = 999 // for demo purpose, don't modify data in rlock!
time.sleep(1 * time.millisecond)
assert co.i == 999
mx.runlock() // after rlock released, write_10000 can continue
th2.wait()
assert co.i == 10000
mx.destroy()
}

fn test_try_lock_rwmutex() {
// In Windows, try_lock only avalible after Windows 7
$if windows {
$if !windows_7 ? {
return
}
}
mut mx := sync.new_rwmutex()

// try_rlock will always fail when mx locked
mx.@lock()
try_fail_reading1 := mx.try_rlock()
try_fail_writing1 := mx.try_wlock()
assert try_fail_reading1 == false
assert try_fail_writing1 == false

mx.unlock()

// try_rlock will always sucess when mx unlocked,
// multiple try_rlock can apply to the same mx
try_sucess_reading2 := mx.try_rlock()
try_sucess_reading3 := mx.try_rlock()
assert try_sucess_reading2 == true
assert try_sucess_reading3 == true

// if mx is rlocked, then the try_wlock will fail
try_fail_writing2 := mx.try_wlock()
assert try_fail_writing2 == false

mx.runlock()
mx.runlock() // you must release rlock mutiple times, as it was rlocked multiple times

// after mx release all rlock, try_wlock will sucess
try_sucess_writing3 := mx.try_wlock()
assert try_sucess_writing3 == true

mx.unlock() // you must unlock it, after try_wlock sucess
mx.destroy()
}
24 changes: 24 additions & 0 deletions vlib/sync/sync_darwin.c.v
Expand Up @@ -12,6 +12,7 @@ import time
@[trusted]
fn C.pthread_mutex_init(voidptr, voidptr) int
fn C.pthread_mutex_lock(voidptr) int
fn C.pthread_mutex_trylock(voidptr) int
fn C.pthread_mutex_unlock(voidptr) int
fn C.pthread_mutex_destroy(voidptr) int
fn C.pthread_rwlockattr_init(voidptr) int
Expand All @@ -20,6 +21,8 @@ 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
fn C.pthread_rwlock_tryrdlock(voidptr) int
fn C.pthread_rwlock_trywrlock(voidptr) int
fn C.pthread_rwlock_unlock(voidptr) int
fn C.pthread_rwlock_destroy(voidptr) int
fn C.pthread_condattr_init(voidptr) int
Expand Down Expand Up @@ -119,6 +122,13 @@ pub fn (mut m Mutex) @lock() {
C.pthread_mutex_lock(&m.mutex)
}

// try_lock try to lock the mutex instance and return immediately.
// If the mutex was already locked, it will return false.
@[inline]
pub fn (mut m Mutex) try_lock() bool {
return C.pthread_mutex_trylock(&m.mutex) == 0
}

// unlock unlocks the mutex instance. The mutex is released, and one of
// the other threads, that were blocked, because they called @lock can continue.
@[inline]
Expand Down Expand Up @@ -158,6 +168,20 @@ pub fn (mut m RwMutex) @lock() {
C.pthread_rwlock_wrlock(&m.mutex)
}

// try_rlock try to lock the given RwMutex instance for reading and return immediately.
// If the mutex was already locked, it will return false.
@[inline]
pub fn (mut m RwMutex) try_rlock() bool {
return C.pthread_rwlock_tryrdlock(&m.mutex) == 0
}

// try_wlock try to lock the given RwMutex instance for writing and return immediately.
// If the mutex was already locked, it will return false.
@[inline]
pub fn (mut m RwMutex) try_wlock() bool {
return C.pthread_rwlock_trywrlock(&m.mutex) == 0
}

// destroy frees the resources associated with the rwmutex instance.
// Note: the mutex itself is not freed.
@[inline]
Expand Down
24 changes: 24 additions & 0 deletions vlib/sync/sync_default.c.v
Expand Up @@ -16,6 +16,7 @@ $if !android {
@[trusted]
fn C.pthread_mutex_init(voidptr, voidptr) int
fn C.pthread_mutex_lock(voidptr) int
fn C.pthread_mutex_trylock(voidptr) int
fn C.pthread_mutex_unlock(voidptr) int
fn C.pthread_mutex_destroy(voidptr) int
fn C.pthread_rwlockattr_init(voidptr) int
Expand All @@ -25,6 +26,8 @@ fn C.pthread_rwlockattr_destroy(voidptr) int
fn C.pthread_rwlock_init(voidptr, voidptr) int
fn C.pthread_rwlock_rdlock(voidptr) int
fn C.pthread_rwlock_wrlock(voidptr) int
fn C.pthread_rwlock_tryrdlock(voidptr) int
fn C.pthread_rwlock_trywrlock(voidptr) int
fn C.pthread_rwlock_unlock(voidptr) int
fn C.pthread_rwlock_destroy(voidptr) int
fn C.sem_init(voidptr, int, u32) int
Expand Down Expand Up @@ -106,6 +109,13 @@ pub fn (mut m Mutex) @lock() {
C.pthread_mutex_lock(&m.mutex)
}

// try_lock try to lock the mutex instance and return immediately.
// If the mutex was already locked, it will return false.
@[inline]
pub fn (mut m Mutex) try_lock() bool {
return C.pthread_mutex_trylock(&m.mutex) == 0
}

// unlock unlocks the mutex instance. The mutex is released, and one of
// the other threads, that were blocked, because they called @lock can continue.
@[inline]
Expand Down Expand Up @@ -144,6 +154,20 @@ pub fn (mut m RwMutex) @lock() {
C.pthread_rwlock_wrlock(&m.mutex)
}

// try_rlock try to lock the given RwMutex instance for reading and return immediately.
// If the mutex was already locked, it will return false.
@[inline]
pub fn (mut m RwMutex) try_rlock() bool {
return C.pthread_rwlock_tryrdlock(&m.mutex) == 0
}

// try_wlock try to lock the given RwMutex instance for writing and return immediately.
// If the mutex was already locked, it will return false.
@[inline]
pub fn (mut m RwMutex) try_wlock() bool {
return C.pthread_rwlock_trywrlock(&m.mutex) == 0
}

// destroy frees the resources associated with the rwmutex instance.
// Note: the mutex itself is not freed.
pub fn (mut m RwMutex) destroy() {
Expand Down
39 changes: 39 additions & 0 deletions vlib/sync/sync_windows.c.v
Expand Up @@ -76,6 +76,19 @@ pub fn (mut m Mutex) @lock() {
C.AcquireSRWLockExclusive(&m.mx)
}

// try_lock try to lock the mutex instance and return immediately.
// If the mutex was already locked, it will return false.
// NOTE: try_lock require Windows 7 or later. Before Windows 7, it will always return false.
// NOTE: To enable try_lock , you should compile your project with `-d windows_7`, like `v . -d windows_7`
// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-tryacquiresrwlockexclusive
pub fn (mut m Mutex) try_lock() bool {
$if windows_7 ? {
return C.TryAcquireSRWLockExclusive(&m.mx) != 0
} $else {
return false
}
}

pub fn (mut m Mutex) unlock() {
C.ReleaseSRWLockExclusive(&m.mx)
}
Expand All @@ -89,6 +102,32 @@ pub fn (mut m RwMutex) @lock() {
C.AcquireSRWLockExclusive(&m.mx)
}

// try_rlock try to lock the given RwMutex instance for reading and return immediately.
// If the mutex was already locked, it will return false.
// NOTE: try_rlock require Windows 7 or later. Before Windows 7, it will always return false.
// NOTE: To enable try_rlock , you should compile your project with `-d windows_7`, like `v . -d windows_7`
// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-tryacquiresrwlockshared
pub fn (mut m RwMutex) try_rlock() bool {
$if windows_7 ? {
return C.TryAcquireSRWLockShared(&m.mx) != 0
} $else {
return false
}
}

// try_wlock try to lock the given RwMutex instance for writing and return immediately.
// If the mutex was already locked, it will return false.
// NOTE: try_wlock require Windows 7 or later. Before Windows 7, it will always return false.
// NOTE: To enable try_wlock , you should compile your project with `-d windows_7`, like `v . -d windows_7`
// https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-tryacquiresrwlockexclusive
pub fn (mut m RwMutex) try_wlock() bool {
$if windows_7 ? {
return C.TryAcquireSRWLockExclusive(&m.mx) != 0
} $else {
return false
}
}

// Windows SRWLocks have different function to unlock
// So provide two functions here, too, to have a common interface
pub fn (mut m RwMutex) runlock() {
Expand Down