Kotlin synchronized doesn't work properly - kotlin

I'm trying to create a simple program, which is model of Brownian motion using concurrency (impurities randomly move left and right in cells). I have Impurity and Cells classes. Cell class contains cell array which mean how many impurities in each cell at the moment. Each Impurity object changes cell array in Cells in own thread. I'm starting threads and they are running in infinite loop for 1 seconds. But before and after this I print sum of impurities in cells and these values not equal, which means I do something wrong with synchronisation. Here is code:
Cells class:
object Cells {
var cell = Array(N) { 0 }
fun addImpurity(impurity: Impurity) {
cell[impurity.currentCell]++
}
#Synchronized
fun move(impurity: Impurity, direction: Direction) {
if (direction == Direction.LEFT && impurity.currentCell > 0) {
cell[impurity.currentCell]--
cell[impurity.currentCell - 1]++
impurity.currentCell--
} else if (direction == Direction.RIGHT && impurity.currentCell < N - 1) {
cell[impurity.currentCell]--
cell[impurity.currentCell + 1]++
impurity.currentCell++
}
Unit
}
fun printCells() {
for (c in cell)
print("$c ")
}
}
enum class Direction {
LEFT, RIGHT
}
Impurity class:
class Impurity(var currentCell: Int) {
private lateinit var thread: Thread
init {
Cells.addImpurity(this)
}
fun startMoving() {
thread = Thread {
while (true) {
if (random() > P)
Cells.move(this, Direction.RIGHT)
else
Cells.move(this, Direction.LEFT)
}
}
thread.start()
}
fun stopMoving() = thread.interrupt()
}
and Main:
const val N = 10
const val K = 15
const val P = 0.5
fun main(args: Array<String>) {
val impurities = ArrayList<Impurity>()
for (i in 1..K)
impurities.add(Impurity(0))
println(Cells.cell.sum())
startMoving(impurities)
Thread.sleep(1000)
stopMoving(impurities)
Cells.printCells()
println(Cells.cell.sum())
}
private fun startMoving(impurities: ArrayList<Impurity>) {
for (impurity in impurities)
impurity.startMoving()
}
private fun stopMoving(impurities: ArrayList<Impurity>) {
for (impurity in impurities)
impurity.stopMoving()
}
Thanks in advance!

I think it might be better to manually signal to the thread that it should finish its work by having it contain some flag that it refers to in order to know when to quit the loop. For example:
class Impurity(var currentCell: Int) {
...
private var _continue = true
fun startMoving() {
thread = Thread {
while (_continue) {
}
}
...
fun stopMoving() {
_continue = false
}
}
Additionally, you might also want to wait till the actual thread itself dies as part of the call to stopMoving. This will ensure that all the threads have definitely received the signal and quit their loops, before you call Cells.printCells. For example you could add this method to the Impurity class:
fun waitForEnded() = thread.join()
And you could update stopMoving in the main class to call this method after signaling to each thread to stop:
private fun stopMoving(impurities: ArrayList<Impurity>) {
for (impurity in impurities)
impurity.stopMoving()
impurities.forEach(Impurity::waitForEnded)
}

Related

Can't call a function from CountDownTimer object. What am I missing?

I'm going through Googles Kotlin Compose tutorials. One of the tasks is to build a game where you unscramble words. After completing it, I tried to improve the game on my own, and just can't figure out how to add a countdown timer to it. I want the program to skip a word when time runs out.
I'm a programming noob, it's not quite clear to me yet how classes and objects work and how they differ from functions.
The code for the timer at the moment:
object Timer: CountDownTimer(60000, 1000) {
override fun onTick(millisUntilFinished: Long) {
TODO("Not yet implemented")
}
override fun onFinish() {
skipWord() // <<----------- **Unresolved reference: skipWord**
}
}
Elsewhere in my code I have:
class GameViewModel : ViewModel() {
//....
fun skipWord() { // <<---------------- Function that skips to the next word
updateGameState(_uiState.value.score)
updateUserGuess("")
}
//.....
private fun pickRandomWordAndShuffle(): String {
// Continue picking up a new random word until you get one that hasn't been used before
if (currentLanguage == "English") {
currentWord = engWords.random()
} else {
currentWord = finWords.random()
}
setPointAmount()
Timer.start() // <<---------------Start a new countdown for a new word.
if (usedWords.contains(currentWord)) {
return pickRandomWordAndShuffle()
} else {
usedWords.add(currentWord)
return shuffleCurrentWord(currentWord)
}
}
}
Also, a separate problem: the .random() always uses the same seed and picks the same words to unscramble.
Change
object Timer: CountDownTimer(60000, 1000) {
...
to
val timer = object: CountDownTimer(60000, 1000) {
...
and put in into your GameViewModel class.
To solve random issue provide some seed to Random object, like:
var myRandom = Random(System.currentTimeMillis())
and then
currentWord = engWords[myRandon.nextInt(engwords.lastIndex)]

CoroutineScope is skipped

For some reasom my CoroutineScope just stopped working, though, nothing crucial has been changed and errors don't show up. The program skips it and my progress bar remains unchanged. Do you have any idea what could have possibly caused this issue?
class HomeFragment : BaseFragment(R.layout.fragment_home) {
private var todayEventsCount: Int = 0
private var todayCheckedEventsCount: Int = 0
private val mHandler = Handler()
private var mAdapter: FirebaseRecyclerAdapter<EventModel, EventsHolder>? = null
private lateinit var mRunnable: Runnable
private lateinit var barList: ArrayList<BarEntry>
private lateinit var barDataSet: BarDataSet
private lateinit var barData: BarData
override fun onResume() {
super.onResume()
initFields()
}
override fun onPause() {
super.onPause()
home_events_list.removeAllViewsInLayout()
todayCheckedEventsCount = 0
}
#SuppressLint("SetTextI18n")
private fun initFields() {
val progress: ProgressBar = ev_progress_bar
val text: TextView = count
getTodayEvents()
CoroutineScope(Dispatchers.IO).launch {
mRunnable = Runnable {
if (mAdapter?.itemCount != 0) {
todayEventsCount = mAdapter?.itemCount!!
for (i in 0 until todayEventsCount) {
if (mAdapter?.getItem(i)!!.checked == "1") {
todayCheckedEventsCount++
progress.progress = todayCheckedEventsCount
text.text = progress.progress.toString() + "/" + progress.max.toString()
}
}
todayCheckedEventsCount = 0
}
mHandler.postDelayed(mRunnable, 30)
}
mHandler.post(mRunnable)
}
initChart()
}
There's no point in posting runnables if you're using a coroutine. Half the reason coroutines exist is to avoid this messy nested juggling of callbacks. There are also a few errors I see in your coroutine:
It runs on Dispatchers.IO, even though it never does anything blocking.
It runs on a throwaway CoroutineScope that is never cancelled. You should never use the CoroutineScope() constructor if you're not immediately assigning it to a variable or property through which you can cancel it at the appropriate time. You should be using viewLifeCycleOwner.lifecycleScope instead, since it's already set up to automatically cancel at the appropriate time to avoid crashes.
Your runnables you're posting are not canceled at the appropriate time, so even if you use the appropriate scope, they can still cause crashes. But as mentioned above, you don't need them in a coroutine.
(mAdapter?.itemCount != 0) will evaluate to true even if mAdapter is null, which will promptly cause a crash when you use mAdapter!!.
Fixed code looks like this. If this still doesn't do anything, you'll want to check to make sure your adapter is not null at the time this is called.
private fun initFields() {
val progress: ProgressBar = ev_progress_bar
val text: TextView = count
getTodayEvents()
viewLifeCycleOwner.lifecycleScope.launch {
val adapter = mAdapter ?: return
while (true) {
if (adapter.itemCount != 0) {
todayEventsCount = adapter.itemCount
for (i in 0 until todayEventsCount) {
if (adapter.getItem(i).checked == "1") {
todayCheckedEventsCount++
progress.progress = todayCheckedEventsCount
text.text = progress.progress.toString() + "/" + progress.max.toString()
}
}
todayCheckedEventsCount = 0
}
delay(30)
}
}
initChart()
}
I didn't try to follow your logic of what these todayEventsCount and todayCheckedEventsCount properties are. They probably should be locally defined variables in the coroutine.
You're also going to need some condition in the while loop that breaks you out of it. I didn't look closely enough to see what that condition should be. Your original code doesn't break the loop of reposting the runnable forever.

How to express in Kotlin "assign value exactly once on the first call"?

Looking for a natural Kotlin way to let startTime be initialized only in a particular place and exactly once.
The following naive implementation have two problems:
it is not thread safe
it does not express the fact "the variable was or will be assigned exactly once in the lifetime of an Item instance"
class Item {
var startTime: Instant?
fun start(){
if (startTime == null){
startTime = Instant.now()
}
// do stuff
}
}
I believe some kind of a delegate could be applicable here. In other words this code needs something similar to a lazy variable, but without initialization on first read, instead it happens only after explicit call of "touching" method. Maybe the Wrap calls could give an idea of possible implementation.
class Wrap<T>(
supp: () -> T
){
private var value: T? = null
private val lock = ReentrantLock()
fun get(){
return value
}
fun touch(){
lock.lock()
try{
if (value == null){
value = supp()
} else {
throw IllegalStateExecption("Duplicate init")
}
} finally{
lock.unlock()
}
}
}
How about combining AtomicReference.compareAndSet with a custom backing field?
You can use a private setter and make sure that the only place the class sets the value is from the start() method.
class Item(val value: Int) {
private val _startTime = AtomicReference(Instant.EPOCH)
var startTime: Instant?
get() = _startTime.get().takeIf { it != Instant.EPOCH }
private set(value) = check(_startTime.compareAndSet(Instant.EPOCH, value)) { "Duplicate set" }
fun start() {
startTime = Instant.now()
}
override fun toString() = "$value: $startTime"
}
fun main() = runBlocking {
val item1 = Item(1)
val item2 = Item(2)
println(Instant.now())
launch { println(item1); item1.start(); println(item1) }
launch { println(item1) }
delay(1000)
println(item2)
item2.start()
println(item2)
println(item2)
item2.start()
}
Example output:
2021-07-14T08:20:27.546821Z
1: null
1: 2021-07-14T08:20:27.607365Z
1: 2021-07-14T08:20:27.607365Z
2: null
2: 2021-07-14T08:20:28.584114Z
2: 2021-07-14T08:20:28.584114Z
Exception in thread "main" java.lang.IllegalStateException: Duplicate set
I think your Wrap class is a good starting point to implement this. I would definitely make it a property delegate and touch() could be much simplified:
fun touch() {
synchronized(this) {
check(value == null) { "Duplicate init" }
value = supp()
}
}
Then you can remove lock. But generally, this is a good approach.
If you would like to reuse lazy util from stdlib then you can do this by wrapping it with another object which does not read its value until asked:
class ManualLazy<T : Any>(private val lazy: Lazy<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? {
return if (lazy.isInitialized()) lazy.value else null
}
fun touch() {
lazy.value
}
}
class Item {
private val _startTime = ManualLazy(lazy { Instant.now() })
val startTime: Instant? by _startTime
fun start(){
_startTime.touch()
}
}
Of course, depending on your needs you can implement it in a much different way, using a similar technique.
This may be considered exploiting or hacking lazy util. I agree and I think Wrap approach is a better one.

Kotlin flows as a message queue between coroutines

I'm attempting to use Kotlin's Flow class as a message queue to transfer data from a producer (a camera) to a set of workers (image analyzers) running on separate coroutines.
The producer in my case is a camera, and will run substantially faster than the workers. Back pressure should be handled by dropping data so that the image analyzers are always operating on the latest images from the camera.
When using channels, this solution works, but seems messy and does not provide an easy way for me to translate the data between the camera and the analyzers (like flow.map).
class ImageAnalyzer<Result> {
fun analyze(image: Bitmap): Result {
// perform some work on the image and return a Result. This can take a long time.
}
}
class CameraAdapter {
private val imageChannel = Channel<Bitmap>(capacity = Channel.RENDEZVOUS)
private val imageReceiveMutex = Mutex()
// additional code to make this camera work and listen to lifecycle events of the enclosing activity.
protected fun sendImageToStream(image: CameraOutput) {
// use channel.offer to ensure the latest images are processed
runBlocking { imageChannel.offer(image) }
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
runBlocking { imageChannel.close() }
}
/**
* Get the stream of images from the camera.
*/
fun getImageStream(): ReceiveChannel<Bitmap> = imageChannel
}
class ImageProcessor<Result>(workers: List<ImageAnalyzer<Result>>) {
private val analysisResults = Channel<Result>(capacity = Channel.RENDEZVOUS)
private val cancelMutex = Mutex()
var finished = false // this can be set elsewhere when enough images have been analyzed
fun subscribeTo(channel: ReceiveChannel<Bitmap>, processingCoroutineScope: CoroutineScope) {
// omit some checks to make sure this is not already subscribed
processingCoroutineScope.launch {
val workerScope = this
workers.forEachIndexed { index, worker ->
launch(Dispatchers.Default) {
startWorker(channel, workerScope, index, worker)
}
}
}
}
private suspend fun startWorker(
channel: ReceiveChannel<Bitmap>,
workerScope: CoroutineScope,
workerId: Int,
worker: ImageAnalyzer
) {
for (bitmap in channel) {
analysisResults.send(worker.analyze(bitmap))
cancelMutex.withLock {
if (finished && workerScope.isActive) {
workerScope.cancel()
}
}
}
}
}
class ExampleApplication : CoroutineScope {
private val cameraAdapter: CameraAdapter = ...
private val imageProcessor: ImageProcessor<Result> = ...
fun analyzeCameraStream() {
imageProcessor.subscribeTo(cameraAdapter.getImageStream())
}
}
What's the proper way to do this? I would like to use a ChannelFlow instead of a Channel to pass data between the camera and the ImageProcessor. This would allow me to call flow.map to add metadata to the images before they're sent to the analyzers. However, when doing so, each ImageAnalyzer gets a copy of the same image instead of processing different images in parallel. Is it possible to use a Flow as a message queue rather than a broadcaster?
I got this working with flows! It was important to keep the flows backed by a channel throughout this sequence so that each worker would pick up unique images to operate on. I've confirmed this functionality through unit tests.
Here's my updated code for posterity:
class ImageAnalyzer<Result> {
fun analyze(image: Bitmap): Result {
// perform some work on the image and return a Result. This can take a long time.
}
}
class CameraAdapter {
private val imageStream = Channel<Bitmap>(capacity = Channel.RENDEZVOUS)
private val imageReceiveMutex = Mutex()
// additional code to make this camera work and listen to lifecycle events of the enclosing activity.
protected fun sendImageToStream(image: CameraOutput) {
// use channel.offer to enforce the drop back pressure strategy
runBlocking { imageChannel.offer(image) }
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
runBlocking { imageChannel.close() }
}
/**
* Get the stream of images from the camera.
*/
fun getImageStream(): Flow<Bitmap> = imageChannel.receiveAsFlow()
}
class ImageProcessor<Result>(workers: List<ImageAnalyzer<Result>>) {
private val analysisResults = Channel<Result>(capacity = Channel.RENDEZVOUS)
private val cancelMutex = Mutex()
var finished = false // this can be set elsewhere when enough images have been analyzed
fun subscribeTo(flow: Flow<Bitmap>, processingCoroutineScope: CoroutineScope): Job {
// omit some checks to make sure this is not already subscribed
return processingCoroutineScope.launch {
val workerScope = this
workers.forEachIndexed { index, worker ->
launch(Dispatchers.Default) {
startWorker(flow, workerScope, index, worker)
}
}
}
}
private suspend fun startWorker(
flow: Flow<Bitmap>,
workerScope: CoroutineScope,
workerId: Int,
worker: ImageAnalyzer
) {
while (workerScope.isActive) {
flow.collect { bitmap ->
analysisResults.send(worker.analyze(bitmap))
cancelMutex.withLock {
if (finished && workerScope.isActive) {
workerScope.cancel()
}
}
}
}
}
fun getAnalysisResults(): Flow<Result> = analysisResults.receiveAsFlow()
}
class ExampleApplication : CoroutineScope {
private val cameraAdapter: CameraAdapter = ...
private val imageProcessor: ImageProcessor<Result> = ...
fun analyzeCameraStream() {
imageProcessor.subscribeTo(cameraAdapter.getImageStream())
}
}
It appears that, so long as the flow is backed by a channel, the subscribers will each get a unique image.

Kotlin coroutines progress counter

I'm making thousands of HTTP requests using async/await and would like to have a progress indicator. I've added one in a naive way, but noticed that the counter value never reaches the total when all requests are done. So I've created a simple test and, sure enough, it doesn't work as expected:
fun main(args: Array<String>) {
var i = 0
val range = (1..100000)
range.map {
launch {
++i
}
}
println("$i ${range.count()}")
}
The output is something like this, where the first number always changes:
98800 100000
I'm probably missing some important detail about concurrency/synchronization in JVM/Kotlin, but don't know where to start. Any tips?
UPDATE: I ended up using channels as Marko suggested:
/**
* Asynchronously fetches stats for all symbols and sends a total number of requests
* to the `counter` channel each time a request completes. For example:
*
* val counterActor = actor<Int>(UI) {
* var counter = 0
* for (total in channel) {
* progressLabel.text = "${++counter} / $total"
* }
* }
*/
suspend fun getAssetStatsWithProgress(counter: SendChannel<Int>): Map<String, AssetStats> {
val symbolMap = getSymbols()?.let { it.map { it.symbol to it }.toMap() } ?: emptyMap()
val total = symbolMap.size
return symbolMap.map { async { getAssetStats(it.key) } }
.mapNotNull { it.await().also { counter.send(total) } }
.map { it.symbol to it }
.toMap()
}
The explanation what exactly makes your wrong approach fail is secondary: the primary thing is fixing the approach.
Instead of async-await or launch, for this communication pattern you should instead have an actor to which all the HTTP jobs send their status. This will automatically handle all your concurrency issues.
Here's some sample code, taken from the link you provided in the comment and adapted to your use case. Instead of some third party asking it for the counter value and updating the GUI with it, the actor runs in the UI context and updates the GUI itself:
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.channels.*
import kotlin.system.*
import kotlin.coroutines.experimental.*
object IncCounter
fun counterActor() = actor<IncCounter>(UI) {
var counter = 0
for (msg in channel) {
updateView(++counter)
}
}
fun main(args: Array<String>) = runBlocking {
val counter = counterActor()
massiveRun(CommonPool) {
counter.send(IncCounter)
}
counter.close()
println("View state: $viewState")
}
// Everything below is mock code that supports the example
// code above:
val UI = newSingleThreadContext("UI")
fun updateView(newVal: Int) {
viewState = newVal
}
var viewState = 0
suspend fun massiveRun(context: CoroutineContext, action: suspend () -> Unit) {
val numCoroutines = 1000
val repeatActionCount = 1000
val time = measureTimeMillis {
val jobs = List(numCoroutines) {
launch(context) {
repeat(repeatActionCount) { action() }
}
}
jobs.forEach { it.join() }
}
println("Completed ${numCoroutines * repeatActionCount} actions in $time ms")
}
Running it prints
Completed 1000000 actions in 2189 ms
View state: 1000000
You're losing writes because i++ is not an atomic operation - the value has to be read, incremented, and then written back - and you have multiple threads reading and writing i at the same time. (If you don't provide launch with a context, it uses a threadpool by default.)
You're losing 1 from your count every time two threads read the same value as they will then both write that value plus one.
Synchronizing in some way, for example by using an AtomicInteger solves this:
fun main(args: Array<String>) {
val i = AtomicInteger(0)
val range = (1..100000)
range.map {
launch {
i.incrementAndGet()
}
}
println("$i ${range.count()}") // 100000 100000
}
There's also no guarantee that these background threads will be done with their work by the time you print the result and your program ends - you can test it easily by adding just a very small delay inside launch, a couple milliseconds. With that, it's a good idea to wrap this all in a runBlocking call which will keep the main thread alive and then wait for the coroutines to all finish:
fun main(args: Array<String>) = runBlocking {
val i = AtomicInteger(0)
val range = (1..100000)
val jobs: List<Job> = range.map {
launch {
i.incrementAndGet()
}
}
jobs.forEach { it.join() }
println("$i ${range.count()}") // 100000 100000
}
Have you read Coroutines basics? There's exact same problem as yours:
val c = AtomicInteger()
for (i in 1..1_000_000)
launch {
c.addAndGet(i)
}
println(c.get())
This example completes in less than a second for me, but it prints some arbitrary number, because some coroutines don't finish before main() prints the result.
Because launch is not blocking, there's no guarantee all of coroutines will finish before println. You need to use async, store the Deferred objects and await for them to finish.