I understand that the kotlin sequence is just a coroutine with a yield suspend function (correct me if I'm wrong).
So the yield function sets the value and suspends the coroutine until the next value is requested.
yield source code
override suspend fun yield(value: T) {
nextValue = value
state = State_Ready
return suspendCoroutineUninterceptedOrReturn { c ->
nextStep = c
COROUTINE_SUSPENDED
}
}
Link to kotlin source code github
I see that it set the nextValue & state and suspends the coroutine by calling suspendCoroutineUninterceptedOrReturn.
Now I would like to know, where does this get resume?
I don't see any resume call inside next() function
override fun next(): T {
when (state) {
State_NotReady, State_ManyNotReady -> return nextNotReady()
State_ManyReady -> {
state = State_ManyNotReady
return nextIterator!!.next()
}
State_Ready -> {
state = State_NotReady
#Suppress("UNCHECKED_CAST")
val result = nextValue as T
nextValue = null
return result
}
else -> throw exceptionalState()
}
}
Just from looking at the source code page you linked:
Calling next() first checks if hasNext() has already been called. If it has, it can immediately return the next value, which was queued in hasNext(). If hasNext() has not yet been called, nextNotReady() is called, which basically calls hasNext() to prepare that next value and then returns it.
So the real work all happens in hasNext(). It checks if the current continuation’s iterator (which exists if yieldAll() is called) has another value, in which case it can queue it without resuming the continuation. But if the current iterator is done or there was no iterator, then it calls resume on the continuation, which makes it run until the next yield() or yieldAll() call.
This description is a big simplification. There are different edge cases that it is handling based on the order of next/hasNext calls.
Related
The problem is very simple, but I can't really seem to wrap my head around it. I'm launching a non-blocking thread in the IO scope in order to read from a file. However, I can't get the result in time before I return from the method - it always returns the initial empty value "". What am I missing here?
private fun getFileContents(): String {
var result = ""
val fileName = getFilename()
val job = CoroutineScope(Dispatchers.IO).launch {
kotlin.runCatching {
val file = getFile(fileName)
file.openFileInput().use { inputStream ->
result = String(inputStream.readBytes(), Charsets.UTF_8)
}
}
}
return result
}
Coroutines are launched asynchronously. Your non-suspending function cannot wait for the result without blocking. For more information about why asynchronous code results in your function returning with the default result, read the answers here.
getFileContents() has to be a suspend function to be able to return something without blocking, in which case you don't need to launch a coroutine either. But then whatever calls this function must be in a suspend function or coroutine.
private suspend fun getFileContents(): String = withContext(Dispatchers.IO) {
val fileName = getFilename()
kotlin.runCatching {
val file = getFile(fileName)
file.openFileInput().use { inputStream ->
result = String(inputStream.readBytes(), Charsets.UTF_8)
}
}.getOrDefault("")
}
There are two "worlds" of code: either you are in a suspending/coroutine context or you are not. When you are in a function that is not a suspend function, you can only return results that can be computed immediately, or you can block until the result is ready.
Generally, if you're using coroutines, you launch a coroutine at some high level in your code, and then you are free to use suspend functions everywhere because almost all of your code is initially triggered by a coroutine. By "high level", I mean you launch the coroutine when a UI screen appears or a UI button is pressed, for example.
Basically, your coroutine launches are usually in UI listeners and UI event functions, not in lower-level code like the function in your question. The coroutine calls a suspend function, which can call other suspend functions, so you don't need to launch more coroutines to perform your various sequential tasks.
The alternate solution is to return a Deferred with the result, like this:
private fun getFileContents(): Deferred<String> {
val fileName = getFilename()
return CoroutineScope(Dispatchers.IO).async {
kotlin.runCatching {
val file = getFile(fileName)
file.openFileInput().use { inputStream ->
result = String(inputStream.readBytes(), Charsets.UTF_8)
}
}.getOrDefault("")
}
}
But to unpack the result, you will need to call await() on the Deferred instance inside a coroutine somewhere.
I'm trying to convert a tail-recursive piece of code that uses Arrow Try, Either and achieve what it does functionally.
The function evaluates the function that was passed in as the argument. If the result is successful, it returns a value of type T. If the evaluation results in a failure, it returns the throwable.
Here is the original function -
private tailrec fun <T> eventuallyAux(
startTime: LocalTime,
timeout: Duration,
retryInterval: Duration,
function: () -> T
): T {
val result = Try { function() }.toEither()
return when (result) {
is Either.Right -> result.b
is Either.Left -> {
throwEventuallyExceptionIfTimedOut(startTime, timeout, result.a)
Thread.sleep(retryInterval.toMillis())
eventuallyAux(startTime, timeout, retryInterval, function)
}
}
}
This is what I have managed to scrape up using Result in kotlin std-lib instead of toEither():
private tailrec fun <T> eventuallyAux(
startTime: LocalTime,
timeout: Duration,
retryInterval: Duration,
function: () -> T
): T {
return runCatching(function)
.onSuccess { it.right() }
.onFailure {
throwEventuallyExceptionIfTimedOut(startTime, timeout, it)
Thread.sleep(retryInterval.toMillis())
eventuallyAux(startTime, timeout, retryInterval, function)
}
}
The compiler complains that a value is being returned with the type Result<T> instead of T. How do I get around this and return T if the evaluation is successful and pass the throwable to the tailrec function if it is a failure?
Thanks
runCatching wraps your return value in a Result, that's why the return type is Result<T>. onSuccess etc just perform an action on the wrapped value, but return the Result itself
That's also why it's not tail-recursive - that requires the recursive call to be the last thing you do in the function, so there's nothing else to do in there, and the current function call can be popped off the stack. No need to backtrack through it when there's nothing to do in there! The result can just be passed to whatever comes before that on the stack. It's like removing the call and starting a new one, like iteration, instead of nesting them.
But because your onFailure code before the Result itself is returned, there's still code to execute in that call, still work to do. So your recursive call isn't a tail call - it's not the last thing, so the current function call can't be removed from the call stack yet. So you end up with regular recursion because it can't be optimised.
Unfortunately you can't do tail calls inside a try/catch either because that's not supported. You need to basically replicate the approach in the original code, which is:
call the function and get the Result
if it's a success, return the unwrapped value
otherwise, return the result of a recursive call
That way, the last thing you do is make the tail call, and the result of that is passed directly back to the original caller. You should be able to do it like this:
private tailrec fun <T> eventuallyAux(
startTime: LocalTime,
timeout: Duration,
retryInterval: Duration,
function: () -> T
): T {
val result = runCatching(function)
return if (result.isSuccess) result.getOrThrow()
else {
throwEventuallyExceptionIfTimedOut(startTime, timeout, it)
Thread.sleep(retryInterval.toMillis())
// last thing that happens, nothing else to do but return the result of this
eventuallyAux(startTime, timeout, retryInterval, function)
}
}
You can't use result.getOrElse because the else block is a lambda - I'm assuming it's because it gets converted to a Function object and calling that isn't handled for tail recursion optimisation, but I haven't looked into it. Doing it this way, with the most basic if/else structure, lets you control exactly what happens when and make sure it gets optimised
My code stop working after call dao method, if I use GlobalScope code working, but then LiveData not update changes
override suspend fun addNewTask(title: String,priority : Int,date: Date): Boolean {
var isSuccess = false
val newTask = hashMapOf(
"title" to title,
"priority" to priority,
"task_points" to converterListOfListToJson(listOf()),
"date" to Timestamp(date)
)
val user: User? = userDao.getCurrentUser()
val newTaskList = user?.tasks?.toMutableList()
val generatedDoc = db.collection(CollectionNames.tasks).document()
val userTasks = db.collection(CollectionNames.users).document(user!!.id)
generatedDoc.set(newTask)
.addOnSuccessListener { isSuccess = true }
.addOnFailureListener { Log.e(TAG, "Error writing document") }.await()
userTasks.update(
"tasks", FieldValue.arrayUnion(generatedDoc.id)
).await()
newTaskList?.add(generatedDoc.id)
taskDao.insert(Task(generatedDoc.id, title, date, listOf(), priority.toLong())) //after code out
user.tasks = newTaskList!!.toList()
userDao.updateUser(user)
return isSuccess
}
This call of this method, addNewTask full complete but after code just end method
fun createTask(title : String, priority : String,date: Date) {
viewModelScope.launch{
tasksUseCase.addNewTask(title, priority, date)
tasksUseCase.updateTaskFromLocalDB().collect { //it is not called
_taskList.postValue(it)
}
}
}
In DAO I just use annotation update and insert
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(task: Task)
My function call in dialog fragment on click, but with viewModel of parent fragment, function deadlock without errors
.addOnSuccessListener { isSuccess = true } will change the variable to true some time in the future, when the background work is done. Your code that uses isSuccess is called before it has a chance to finish the background work.
When you used GlobalScope, it probably used the default dispatcher Dispatchers.Default, so you had a race condition and in some cases it could succeed.
When you used viewModelScope, it uses Dispatchers.Main, so there is no possibility of the result listener being called before the end of this suspend function unless there is some other suspending call in between.
What you need to do to fix it is run your task in a synchronous, suspending way instead of an asynchronous way. Since I don't know what class generatedDoc is, I can't help with that. Many libraries include extension suspend function, usually named await(), that let you get the result synchronously. If they don't provide that, you can write your own using suspendCancellableCoroutine. There are many other questions about that on here that you can search for to see how to use it.
The problem was that the viewModel was a DialogFragment which was destroyed after the method was called, due to which the ViewModelScope also ceased to exist and the method crashed in the middle
I am learning coroutines and need some help to understand a basic use case.
Implement a non-blocking method that:
Fetches a single item from a (reactive) DB
Determines a range (i.e. the month that the item lives in) based on that item's timestamp
Fetches all items in that month
Returns the items as Flow
Approach
Because it must return a Flow I will not use suspend (like I would when returning a single item). Returning Flow and suspend (which kind of returns a Mono) are most commonly mutually exclusive, right?
So I came up with this signature:
override fun getHistory(beforeUtcMillisExclusive: Long): Flow<Item>
Trying an implementation:
val itemInNextPeriod = itemRepository.findOneByTimestampLessThan(beforeUtcMillisExclusive)
if (itemInNextPeriod == null) {
return emptyFlow()
} else {
val range = calcRange(itemInNextPeriod.timestamp)
return itemRepository.findByTimestampGreaterThanEqualAndTimestampLessThan(range.start, range.end)
}
This gives me on the very first line:
Suspend function 'findOneByTimestampLessThan' should be called only
from a coroutine or another suspend function
I understand the problem that we are not allowed to call a suspend function here and the proposed solution by IntelliJ "adding suspend" does not make sense, when already returning a flow.
So, from this question I got the idea of using a return flow {...}:
return flow {
val itemInNextPeriod = itemRepository.findOneByTimestampLessThan(beforeUtcMillisExclusive)
if (itemInNextPeriod == null) {
return#flow
} else {
val range = calcRange(itemInNextPeriod.timestamp)
return#flow itemRepository.findByTimestampGreaterThanEqualAndTimestampLessThan(range.start,
range.end)
}
}
The second repository call findByTimestampGreaterThanEqualAndTimestampLessThan returns Flow<Item> and I do not understand why I cannot return it.
This function must return a value of type Unit
Type mismatch.
Required:
Unit
Found:
Flow
return#flow returns from the lambda, not from enclosing function.
You need to reemit items from Flow returned by findByTimestampGreaterThanEqualAndTimestampLessThan call into Flow you're building with flow function:
return flow {
val itemInNextPeriod = itemRepository.findOneByTimestampLessThan(beforeUtcMillisExclusive)
if (itemInNextPeriod != null) {
val range = calcRange(itemInNextPeriod.timestamp)
emitAll(itemRepository.findByTimestampGreaterThanEqualAndTimestampLessThan(range.start, range.end))
}
}
I'm trying to write a code, that will let me test situations, in which Javas #Synchronized is not enough, to synchronize Kotlin coroutines. From my understanding, the code below:
var sharedCounter: Long = 0
#Synchronized
suspend fun updateCounter() {
delay(2)
sharedCounter++
delay(2)
yield()
}
fun main() = runBlocking {
var regularCounter: Long = 0
val scope = CoroutineScope(Dispatchers.IO + Job())
val jobs = mutableListOf<Job>()
repeat(1000) {
val job = scope.launch {
for (i in 1..1_000) {
regularCounter++
updateCounter()
}
}
jobs.add(job)
}
jobs.forEach { it.join() }
println("The number of shared counter is $sharedCounter")
println("The number of regular counter is $regularCounter")
}
should result in both sharedCounter and regularCounter NOT being equal to 1000000.
This code was based on this and this articles.
For some reason, sharedCounter always equals 1000000 and I'm not sure why.
I've tried testing larger for loops, but it did not "break" the synchronization either.
The synchronization lock is released at each suspend function call, but re-acquired before continuing. In your code, delay() and yield() are the suspension points. But sharedCounter++ is all local code, so the lock is held while it performs its three steps of getting the value, incrementing it, and setting it. So, the first delay() releases the lock, the continuation resumes so it re-locks and performs sharedCounter++, and then the lock is released again at the second delay() call.