-
Notifications
You must be signed in to change notification settings - Fork 67
Mutex for Kotlin/Common #508
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
Open
bbrockbernd
wants to merge
12
commits into
develop
Choose a base branch
from
bbrockbernd/common-mutex
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
e1bd019
Introduce mutex
bbrockbernd a4d1a3b
Minor improvements
bbrockbernd 00187e5
Revert lincheck version
bbrockbernd 2c11bc5
Fix inconsistent package defs
bbrockbernd 464a124
Lazy init native mutex in lincheck tests
bbrockbernd 7a35ef7
Add comment
bbrockbernd 2ace4c7
Exclude lincheck tests from the `transformed` test jobs
bbrockbernd 536b81c
Fixed node not being dequed properly after timeout. Also added valida…
bbrockbernd 6c745aa
Fix jsAndWasm comments, remove redundant return, apply terminology su…
bbrockbernd 9301433
Turn redundant ifs into checks, assert for native is still experimental.
bbrockbernd 43d0892
Exclude all tests from transformation that end with "LincheckTest"
bbrockbernd 5768a08
Bugfix mutex
bbrockbernd File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,6 +45,15 @@ final class <#A: kotlin/Any?> kotlinx.atomicfu/AtomicRef { // kotlinx.atomicfu/A | |
final fun compareAndSet(#A, #A): kotlin/Boolean // kotlinx.atomicfu/AtomicRef.compareAndSet|compareAndSet(1:0;1:0){}[0] | ||
} | ||
|
||
final class kotlinx.atomicfu.locks/SynchronousMutex { // kotlinx.atomicfu.locks/SynchronousMutex|null[0] | ||
constructor <init>() // kotlinx.atomicfu.locks/SynchronousMutex.<init>|<init>(){}[0] | ||
|
||
final fun lock() // kotlinx.atomicfu.locks/SynchronousMutex.lock|lock(){}[0] | ||
final fun tryLock(): kotlin/Boolean // kotlinx.atomicfu.locks/SynchronousMutex.tryLock|tryLock(){}[0] | ||
final fun tryLock(kotlin.time/Duration): kotlin/Boolean // kotlinx.atomicfu.locks/SynchronousMutex.tryLock|tryLock(kotlin.time.Duration){}[0] | ||
final fun unlock() // kotlinx.atomicfu.locks/SynchronousMutex.unlock|unlock(){}[0] | ||
} | ||
|
||
final class kotlinx.atomicfu/AtomicBoolean { // kotlinx.atomicfu/AtomicBoolean|null[0] | ||
final var value // kotlinx.atomicfu/AtomicBoolean.value|{}value[0] | ||
// Targets: [native] | ||
|
@@ -278,6 +287,7 @@ final inline fun (kotlinx.atomicfu/AtomicLong).kotlinx.atomicfu/getAndUpdate(kot | |
final inline fun (kotlinx.atomicfu/AtomicLong).kotlinx.atomicfu/loop(kotlin/Function1<kotlin/Long, kotlin/Unit>): kotlin/Nothing // kotlinx.atomicfu/loop|[email protected](kotlin.Function1<kotlin.Long,kotlin.Unit>){}[0] | ||
final inline fun (kotlinx.atomicfu/AtomicLong).kotlinx.atomicfu/update(kotlin/Function1<kotlin/Long, kotlin/Long>) // kotlinx.atomicfu/update|[email protected](kotlin.Function1<kotlin.Long,kotlin.Long>){}[0] | ||
final inline fun (kotlinx.atomicfu/AtomicLong).kotlinx.atomicfu/updateAndGet(kotlin/Function1<kotlin/Long, kotlin/Long>): kotlin/Long // kotlinx.atomicfu/updateAndGet|[email protected](kotlin.Function1<kotlin.Long,kotlin.Long>){}[0] | ||
final inline fun <#A: kotlin/Any?> (kotlinx.atomicfu.locks/SynchronousMutex).kotlinx.atomicfu.locks/withLock(kotlin/Function0<#A>): #A // kotlinx.atomicfu.locks/withLock|[email protected](kotlin.Function0<0:0>){0§<kotlin.Any?>}[0] | ||
final inline fun <#A: kotlin/Any?> (kotlinx.atomicfu/AtomicRef<#A>).kotlinx.atomicfu/getAndUpdate(kotlin/Function1<#A, #A>): #A // kotlinx.atomicfu/getAndUpdate|[email protected]<0:0>(kotlin.Function1<0:0,0:0>){0§<kotlin.Any?>}[0] | ||
final inline fun <#A: kotlin/Any?> (kotlinx.atomicfu/AtomicRef<#A>).kotlinx.atomicfu/loop(kotlin/Function1<#A, kotlin/Unit>): kotlin/Nothing // kotlinx.atomicfu/loop|[email protected]<0:0>(kotlin.Function1<0:0,kotlin.Unit>){0§<kotlin.Any?>}[0] | ||
final inline fun <#A: kotlin/Any?> (kotlinx.atomicfu/AtomicRef<#A>).kotlinx.atomicfu/update(kotlin/Function1<#A, #A>) // kotlinx.atomicfu/update|[email protected]<0:0>(kotlin.Function1<0:0,0:0>){0§<kotlin.Any?>}[0] | ||
|
@@ -297,14 +307,6 @@ open annotation class kotlinx.atomicfu.locks/ExperimentalThreadBlockingApi : kot | |
constructor <init>() // kotlinx.atomicfu.locks/ExperimentalThreadBlockingApi.<init>|<init>(){}[0] | ||
} | ||
|
||
// Targets: [native] | ||
final class kotlinx.atomicfu.locks/NativeMutexNode { // kotlinx.atomicfu.locks/NativeMutexNode|null[0] | ||
constructor <init>() // kotlinx.atomicfu.locks/NativeMutexNode.<init>|<init>(){}[0] | ||
|
||
final fun lock() // kotlinx.atomicfu.locks/NativeMutexNode.lock|lock(){}[0] | ||
final fun unlock() // kotlinx.atomicfu.locks/NativeMutexNode.unlock|unlock(){}[0] | ||
} | ||
|
||
// Targets: [native] | ||
final class kotlinx.atomicfu.locks/ParkingHandle // kotlinx.atomicfu.locks/ParkingHandle|null[0] | ||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,6 +127,7 @@ kotlin { | |
|
||
jvmTest { | ||
dependencies { | ||
implementation("org.jetbrains.kotlinx:lincheck:2.39") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please declare dependencies in |
||
implementation("org.jetbrains.kotlin:kotlin-reflect") | ||
implementation("org.jetbrains.kotlin:kotlin-test") | ||
implementation("org.jetbrains.kotlin:kotlin-test-junit") | ||
|
@@ -206,7 +207,12 @@ val transformedTestFU_current by tasks.registering(Test::class) { | |
dependsOn(transformFU) | ||
classpath = files(configurations.getByName("jvmTestRuntimeClasspath"), classesPostTransformFU) | ||
testClassesDirs = project.files(classesPostTransformFU) | ||
exclude("**/*LFTest.*", "**/TraceToStringTest.*", "**/AtomicfuReferenceJsTest.*") | ||
exclude( | ||
"**/*LFTest.*", | ||
"**/TraceToStringTest.*", | ||
"**/AtomicfuReferenceJsTest.*", | ||
"**/*LincheckTest.*", | ||
) | ||
dkhalanskyjb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
filter { isFailOnNoMatchingTests = false } | ||
launcherForJdk(LAUNCHER_JDK_VERSION) | ||
} | ||
|
@@ -220,7 +226,8 @@ val transformedTestBOTH_current by tasks.registering(Test::class) { | |
"**/TraceToStringTest.*", | ||
"**/TopLevelGeneratedDeclarationsReflectionTest.*", | ||
"**/SyntheticFUFieldsTest.*", | ||
"**/AtomicfuReferenceJsTest.*" | ||
"**/AtomicfuReferenceJsTest.*", | ||
"**/*LincheckTest.*", | ||
) | ||
filter { isFailOnNoMatchingTests = false } | ||
launcherForJdk(LAUNCHER_JDK_VERSION) | ||
|
@@ -235,7 +242,8 @@ val transformedTestVH by tasks.registering(Test::class) { | |
"**/TraceToStringTest.*", | ||
"**/TopLevelGeneratedDeclarationsReflectionTest.*", | ||
"**/SyntheticFUFieldsTest.*", | ||
"**/AtomicfuReferenceJsTest.*" | ||
"**/AtomicfuReferenceJsTest.*", | ||
"**/*LincheckTest.*", | ||
) | ||
filter { isFailOnNoMatchingTests = false } | ||
|
||
|
57 changes: 0 additions & 57 deletions
57
atomicfu/src/androidNativeMain/kotlin/kotlinx/atomicfu/locks/NativeMutexNode.kt
This file was deleted.
Oops, something went wrong.
112 changes: 0 additions & 112 deletions
112
atomicfu/src/appleMain/kotlin/kotlinx/atomicfu/locks/NativeMutexNode.kt
This file was deleted.
Oops, something went wrong.
10 changes: 0 additions & 10 deletions
10
atomicfu/src/appleMain/kotlin/kotlinx/atomicfu/locks/ThreadId.kt
This file was deleted.
Oops, something went wrong.
89 changes: 89 additions & 0 deletions
89
atomicfu/src/commonMain/kotlin/kotlinx/atomicfu/locks/SynchronousMutex.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package kotlinx.atomicfu.locks | ||
|
||
import kotlin.contracts.ExperimentalContracts | ||
import kotlin.contracts.InvocationKind | ||
import kotlin.contracts.contract | ||
import kotlin.time.Duration | ||
|
||
/** | ||
* Mutual exclusion for Kotlin Multiplatform. | ||
* | ||
* It can protect a shared resource or critical section from multiple thread accesses. | ||
* Threads can acquire the lock by calling [lock] and release the lock by calling [unlock]. | ||
* | ||
* When a thread calls [lock] while another thread is locked, it will suspend until the lock is released. | ||
bbrockbernd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* When multiple threads are waiting for the lock, they will acquire it in a fair order (first in first out). | ||
* On JVM, a [lock] call can skip the queue if it happens in between a thread releasing and the first in queue acquiring. | ||
* | ||
* It is reentrant, meaning the lock holding thread can call [lock] multiple times without suspending. | ||
* To release the lock (after multiple [lock] calls) an equal number of [unlock] calls are required. | ||
* | ||
* This Mutex should not be used in combination with coroutines and `suspend` functions | ||
* as it blocks the waiting thread. | ||
* Use the `Mutex` from the coroutines library instead. | ||
* | ||
* ```Kotlin | ||
* mutex.withLock { | ||
* // Critical section only executed by | ||
* // one thread at a time. | ||
* } | ||
* ``` | ||
*/ | ||
public expect class SynchronousMutex() { | ||
/** | ||
* Tries to lock this mutex, returning `false` if this mutex is already locked. | ||
* | ||
* It is recommended to use [withLock] for safety reasons, so that the acquired lock is always | ||
* released at the end of your critical section, and [unlock] is never invoked before a successful | ||
* lock acquisition. | ||
* | ||
* (JVM only) this call can potentially skip line. | ||
*/ | ||
public fun tryLock(): Boolean | ||
|
||
/** | ||
* Tries to lock this mutex within the given [timeout] period, | ||
* returning `false` if the duration passed without locking. | ||
* | ||
* Note: when [tryLock] succeeds the lock needs to be released by [unlock]. | ||
* When [tryLock] does not succeed the lock does not have to be released. | ||
* | ||
* (JVM only) throws Interrupted exception when thread is interrupted while waiting for lock. | ||
*/ | ||
public fun tryLock(timeout: Duration): Boolean | ||
|
||
/** | ||
* Locks the mutex, blocks the thread until the lock is acquired. | ||
* | ||
* It is recommended to use [withLock] for safety reasons, so that the acquired lock is always | ||
* released at the end of your critical section, and [unlock] is never invoked before a successful | ||
* lock acquisition. | ||
*/ | ||
public fun lock() | ||
|
||
/** | ||
* Releases the lock. | ||
* Throws [IllegalStateException] when the current thread is not holding the lock. | ||
* | ||
* It is recommended to use [withLock] for safety reasons, so that the acquired lock is always | ||
* released at the end of the critical section, and [unlock] is never invoked before a successful | ||
* lock acquisition. | ||
*/ | ||
public fun unlock() | ||
} | ||
|
||
/** | ||
* Executes the given code [block] under this mutex's lock. | ||
* | ||
* @return result of [block] | ||
*/ | ||
@OptIn(ExperimentalContracts::class) | ||
public inline fun <T> SynchronousMutex.withLock(block: () -> T): T { | ||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } | ||
lock() | ||
return try { | ||
block() | ||
} finally { | ||
unlock() | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume removing this implementation detail is safe, but cc @fzhinkin: he probably knows if this was already used by someone else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, there's a lot of API entries that were unintentionally left public.
Last time I checked, nobody actually use them, but it might be worth explicitly announcing the removal prior the release.