-
-
Notifications
You must be signed in to change notification settings - Fork 1
WIP Add Kotlin coroutine examples #172
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
base: main
Are you sure you want to change the base?
Changes from all commits
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,61 @@ | ||
package dev.shtanko.concurrency.coroutines.tasks | ||
|
||
import kotlin.coroutines.cancellation.CancellationException | ||
import kotlinx.coroutines.CoroutineDispatcher | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.Job | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.StateFlow | ||
import kotlinx.coroutines.flow.asStateFlow | ||
import kotlinx.coroutines.withContext | ||
|
||
@Suppress("TooGenericExceptionCaught") | ||
abstract class BaseTask<R>( | ||
override val name: String, | ||
override val description: String, | ||
private val dispatcher: CoroutineDispatcher = Dispatchers.Default, | ||
) : Task<R> { | ||
private val _progress = MutableStateFlow(0f) | ||
override val progress: StateFlow<Float> = _progress.asStateFlow() | ||
|
||
private val _status = MutableStateFlow(TaskStatus.IDLE) | ||
override val status: StateFlow<TaskStatus> = _status.asStateFlow() | ||
|
||
private var job: Job? = null | ||
|
||
protected fun updateProgress(value: Float) { | ||
_progress.value = value.coerceIn(0f, 1f) | ||
} | ||
|
||
protected fun updateStatus(status: TaskStatus) { | ||
_status.value = status | ||
} | ||
|
||
override fun cancel() { | ||
job?.cancel() | ||
updateStatus(TaskStatus.CANCELLED) | ||
} | ||
|
||
override suspend fun run(): R { | ||
return withContext(dispatcher) { | ||
job = coroutineContext[Job] | ||
updateStatus(TaskStatus.RUNNING) | ||
updateProgress(0f) | ||
|
||
try { | ||
val result = execute() | ||
updateStatus(TaskStatus.COMPLETED) | ||
updateProgress(1f) | ||
result | ||
} catch (e: CancellationException) { | ||
updateStatus(TaskStatus.CANCELLED) | ||
throw e | ||
} catch (e: Exception) { | ||
updateStatus(TaskStatus.ERROR) | ||
throw e | ||
} | ||
} | ||
} | ||
|
||
protected abstract suspend fun execute(): R | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
@file:Suppress("MagicNumber") | ||
|
||
package dev.shtanko.concurrency.coroutines.tasks | ||
|
||
import kotlin.random.Random | ||
import kotlinx.coroutines.yield | ||
|
||
class BinaryTreeTask( | ||
private val nodeCount: Int = 50_000, | ||
) : BaseTask<String>( | ||
name = "Binary Tree Operations", | ||
description = "Building and traversing $nodeCount nodes", | ||
) { | ||
class TreeNode(val value: Int) { | ||
var left: TreeNode? = null | ||
var right: TreeNode? = null | ||
} | ||
|
||
override suspend fun execute(): String { | ||
var root: TreeNode? = null | ||
val values = List(nodeCount) { Random.nextInt(1000000) } | ||
|
||
// Build tree | ||
for ((index, value) in values.withIndex()) { | ||
if (index % 500 == 0) { | ||
yield() | ||
updateProgress(index.toFloat() / (nodeCount * 2)) | ||
} | ||
root = insert(root, value) | ||
} | ||
|
||
// Calculate tree properties | ||
val height = calculateHeight(root) | ||
val nodeSum = sumNodes(root) | ||
|
||
return "Height: $height, Sum: $nodeSum" | ||
} | ||
|
||
private fun insert(root: TreeNode?, value: Int): TreeNode { | ||
if (root == null) return TreeNode(value) | ||
|
||
if (value < root.value) { | ||
root.left = insert(root.left, value) | ||
} else { | ||
root.right = insert(root.right, value) | ||
} | ||
return root | ||
} | ||
|
||
private suspend fun calculateHeight(node: TreeNode?): Int { | ||
if (node == null) return 0 | ||
return 1 + maxOf(calculateHeight(node.left), calculateHeight(node.right)) | ||
} | ||
|
||
private suspend fun sumNodes(node: TreeNode?): Long { | ||
if (node == null) return 0 | ||
return node.value + sumNodes(node.left) + sumNodes(node.right) | ||
} | ||
Comment on lines
+55
to
+58
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. Compilation error: returning Int where Long is required sumNodes is declared to return Long but returns 0 (Int) for the null case. Use 0L to match the return type. Apply this diff: - private suspend fun sumNodes(node: TreeNode?): Long {
- if (node == null) return 0
- return node.value + sumNodes(node.left) + sumNodes(node.right)
- }
+ private suspend fun sumNodes(node: TreeNode?): Long {
+ if (node == null) return 0L
+ return node.value + sumNodes(node.left) + sumNodes(node.right)
+ } 🤖 Prompt for AI Agents
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
@file:Suppress("MagicNumber") | ||
|
||
package dev.shtanko.concurrency.coroutines.tasks | ||
|
||
import kotlin.random.Random | ||
import kotlinx.coroutines.yield | ||
|
||
class CompressionTask( | ||
private val dataSize: Int = 100_000, | ||
) : BaseTask<Double>( | ||
name = "Data Compression", | ||
description = "LZ77 compression of $dataSize bytes", | ||
) { | ||
override suspend fun execute(): Double { | ||
val data = ByteArray(dataSize) { (Random.nextInt(26) + 'a'.code).toByte() } | ||
val compressed = mutableListOf<Triple<Int, Int, Byte>>() // (offset, length, next char) | ||
|
||
var i = 0 | ||
while (i < data.size) { | ||
if (i % 1000 == 0) { | ||
yield() | ||
updateProgress(i.toFloat() / dataSize) | ||
} | ||
|
||
var maxLength = 0 | ||
var maxOffset = 0 | ||
val searchStart = maxOf(0, i - 4096) // Search window | ||
|
||
for (j in searchStart until i) { | ||
var length = 0 | ||
while (i + length < data.size && | ||
length < 255 && | ||
data[j + length] == data[i + length] | ||
) { | ||
length++ | ||
} | ||
|
||
if (length > maxLength) { | ||
maxLength = length | ||
maxOffset = i - j | ||
} | ||
} | ||
|
||
val nextChar = if (i + maxLength < data.size) data[i + maxLength] else 0 | ||
compressed.add(Triple(maxOffset, maxLength, nextChar)) | ||
i += maxLength + 1 | ||
Comment on lines
+44
to
+46
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. Fix type mismatch for The else branch uses an Apply this diff: - val nextChar = if (i + maxLength < data.size) data[i + maxLength] else 0
+ val nextChar = if (i + maxLength < data.size) data[i + maxLength] else 0.toByte() 🤖 Prompt for AI Agents
|
||
} | ||
|
||
val compressionRatio = compressed.size * 3.0 / dataSize | ||
return compressionRatio | ||
} | ||
} |
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.
💡 Verification agent
🧩 Analysis chain
Avoid committing backup files; also verify Kotlin badge matches build toolchain
.bak
files are typically transient and shouldn’t be versioned. Consider removing this file or moving it under docs with a proper name. While you’re here, confirm the2.2.10
badge aligns with the Kotlin toolchain used by Gradle and libs.versions.toml.Run this script to surface Kotlin versions used in the repo and the badge versions referenced in docs:
🏁 Script executed:
Length of output: 12494
🏁 Script executed:
Length of output: 705
Remove the backup file config/main.md.bak
kotlin = "2.2.10"
entry in gradle/libs.versions.toml.🤖 Prompt for AI Agents