-
Notifications
You must be signed in to change notification settings - Fork 127
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
Introduce korlibs-concurrent (Lock + NativeThread) + add logger, datastructure and platform to K/N desktop targets #2132
Changes from 5 commits
a0e5c09
b474c53
1a663c9
1d2e7df
918bc3e
924ba8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import korlibs.* | ||
|
||
description = "Korlibs Concurrent" | ||
|
||
project.extensions.extraProperties.properties.apply { | ||
applyProjectProperties( | ||
"https://raw.githubusercontent.com/korlibs/korge/main/korlibs-concurrent", | ||
"Public Domain", | ||
"https://raw.githubusercontent.com/korlibs/korge/main/korlibs-concurrent/LICENSE" | ||
) | ||
} | ||
|
||
dependencies { | ||
commonMainApi(libs.kotlinx.atomicfu) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
@file:Suppress("PackageDirectoryMismatch") | ||
|
||
package korlibs.concurrent.lock | ||
|
||
import korlibs.concurrent.thread.* | ||
import kotlin.time.* | ||
import kotlin.time.Duration.Companion.milliseconds | ||
import kotlin.time.Duration.Companion.seconds | ||
|
||
interface BaseLock { | ||
fun notify(unit: Unit = Unit) | ||
fun wait(time: Duration): Boolean | ||
//fun lock() | ||
//fun unlock() | ||
} | ||
|
||
//typealias Lock = BaseLock | ||
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. We should not leave in comment code in PR. YAGNI: You aren't gonna need it. If you do, you can readd it from earlier commit. 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. This is old code that was like that, I'm okay with removing those lines. So feel free to do so. |
||
//typealias NonRecursiveLock = BaseLock | ||
|
||
//inline operator fun <T> BaseLock.invoke(callback: () -> T): T { | ||
// lock() | ||
// try { | ||
// return callback() | ||
// } finally { | ||
// unlock() | ||
// } | ||
//} | ||
|
||
/** | ||
* Reentrant typical lock. | ||
*/ | ||
expect class Lock() : BaseLock { | ||
override fun notify(unit: Unit) | ||
override fun wait(time: Duration): Boolean | ||
inline operator fun <T> invoke(callback: () -> T): T | ||
} | ||
|
||
/** | ||
* Optimized lock that cannot be called inside another lock, | ||
* don't keep the current thread id, or a list of threads to awake | ||
* It is lightweight and just requires an atomic. | ||
* Does busy-waiting instead of sleeping the thread. | ||
*/ | ||
expect class NonRecursiveLock() : BaseLock { | ||
override fun notify(unit: Unit) | ||
override fun wait(time: Duration): Boolean | ||
inline operator fun <T> invoke(callback: () -> T): T | ||
} | ||
|
||
fun BaseLock.waitPrecise(time: Duration): Boolean { | ||
val startTime = TimeSource.Monotonic.markNow() | ||
val doWait = time - 10.milliseconds | ||
val signaled = if (doWait > 0.seconds) wait(doWait) else false | ||
if (!signaled && doWait > 0.seconds) { | ||
val elapsed = startTime.elapsedNow() | ||
//println(" !!!!! SLEEP EXACT: ${elapsed - time}") | ||
NativeThread.sleepExact(time - elapsed) | ||
} | ||
return signaled | ||
} | ||
|
||
fun BaseLock.wait(time: Duration, precise: Boolean): Boolean { | ||
return if (precise) waitPrecise(time) else wait(time) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package korlibs.concurrent.thread | ||
|
||
import kotlin.time.* | ||
import kotlin.time.Duration.Companion.milliseconds | ||
import kotlin.time.Duration.Companion.seconds | ||
|
||
// @TODO: Mark this as experimental or something so people know this is not fully supported in all the targets. | ||
// @TODO: isSupported is required to be used. | ||
expect class NativeThread(code: (NativeThread) -> Unit) { | ||
companion object { | ||
val isSupported: Boolean | ||
val currentThreadId: Long | ||
val currentThreadName: String? | ||
|
||
fun gc(full: Boolean): Unit | ||
fun sleep(time: Duration): Unit | ||
inline fun spinWhile(cond: () -> Boolean): Unit | ||
} | ||
var userData: Any? | ||
var threadSuggestRunning: Boolean | ||
var priority: Int | ||
var name: String? | ||
var isDaemon: Boolean | ||
fun start(): Unit | ||
fun interrupt(): Unit | ||
} | ||
|
||
public fun nativeThread( | ||
start: Boolean = true, | ||
isDaemon: Boolean = false, | ||
name: String? = null, | ||
priority: Int = -1, | ||
block: (NativeThread) -> Unit | ||
): NativeThread { | ||
val thread = NativeThread(block) | ||
if (isDaemon) thread.isDaemon = true | ||
if (priority > 0) thread.priority = priority | ||
if (name != null) thread.name = name | ||
// if (contextClassLoader != null) thread.contextClassLoader = contextClassLoader | ||
if (start) thread.start() | ||
return thread | ||
} | ||
|
||
fun NativeThread.Companion.sleep(time: Duration, exact: Boolean) { | ||
if (exact) sleepExact(time) else sleep(time) | ||
} | ||
|
||
// https://stackoverflow.com/questions/13397571/precise-thread-sleep-needed-max-1ms-error#:~:text=Scheduling%20Fundamentals | ||
// https://www.softprayog.in/tutorials/alarm-sleep-and-high-resolution-timers | ||
fun NativeThread.Companion.sleepExact(time: Duration) { | ||
val start = TimeSource.Monotonic.markNow() | ||
//val imprecision = 10.milliseconds | ||
//val imprecision = 1.milliseconds | ||
val imprecision = 4.milliseconds | ||
val javaSleep = time - imprecision | ||
if (javaSleep >= 0.seconds) { | ||
NativeThread.sleep(javaSleep) | ||
} | ||
NativeThread.spinWhile { start.elapsedNow() < time } | ||
} | ||
|
||
//fun NativeThread.Companion.sleepUntil(date: DateTime, exact: Boolean = true) { | ||
// sleep(date - DateTime.now(), exact) | ||
//} | ||
|
||
inline fun NativeThread.Companion.sleepWhile(cond: () -> Boolean) { | ||
while (cond()) { | ||
NativeThread.sleep(1.milliseconds) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package korlibs.concurrent.thread | ||
|
||
import kotlinx.cinterop.* | ||
import platform.Foundation.* | ||
|
||
actual val __currentThreadId: Long get() = NSThread.currentThread.objcPtr().toLong() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package korlibs.concurrent.thread | ||
|
||
import platform.posix.* | ||
|
||
actual val __currentThreadId: Long get() = pthread_self().toLong() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package korlibs.concurrent.thread | ||
|
||
import kotlinx.cinterop.* | ||
import platform.posix.* | ||
|
||
actual val __currentThreadId: Long get() = pthread_self().toLong() |
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.
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.
Rationale behind the star imports:
KorGE uses star imports because it relies a lot on extension members. It was a pain in the ass adding all those members manually for people starting. Users typically don't care where classes come from as long as the code works.
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.
Yes, but it makes it pain in the ass to refactor. For our current task forexample it is important to see which methods are used from which packages, when we want to reduce the dependency towards a package.
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.
Why it is a pain in the ass? Doesn't
@Deprecated
has a parameter to specify replacement to do it automatically inside the IDE?