I got following functions for making server calls
suspend fun <T: BaseResponse> processPost(post:Post):T? {
val gson=Gson()
val data=gson.toJson(post.reqData)
val res= sendPost(data,post.script)
Log.d("server","res:"+res.first)
//process response here
return null
}
private fun sendPost(data:String,url:String):Pair<String,Int> {
//send data to server
}
In some cases processPost may enter into infinite loop(for instance to wait for access token refresh).Of course this code should never be run on the main thread.But when I mark this function as suspend IDE is highliting it as redundant.Its not big deal but I'm curious how then can I restrict function execution on the main thread?
It seems that you have quite some learning on coroutines to do. It’s impossible to cover all you need to know in one single answer. That’s what tutorials are for. Anyway I will try to answer just the points you asked. It may not make sense before you learn the concepts, I’m sorry if my answer does not help.
Just like many other things, coroutines are not magic. If you don’t understand what something does, you cannot hope it has the properties you want. It may sound harsh but I want to stress that such mentality is a major cause of bugs.
Making a function suspending allows you to call other suspending functions in the function body. It does not make blocking calls non-blocking, nor does it automatically jump threads for you.
You can use withContext to have the execution jump to another thread.
suspend fun xyz() = withContext(Dispatchers.IO) {
...
}
When you call xyz in the main thread, it’ll hand the task to the IO dispatcher. Without being blocked, it can then handle other stuff in the app.
EDIT regarding the comment.
Sorry for being so patronizing and making a wrong guess about your misconception.
If you just want the compiler/the IDE to shut up about the warning, you can simply add #Suppress("RedundantSuspendModifier") to the function. But you shouldn't, because the compiler knows better than you, at least for now.
The great thing about coroutines is that you can write in direct style without blocking the main thread.
launch(Dispatchers.Main) {
val result = makeAnHttpCall() // this can take a long time
messWithUi(result) // changes to the UI has to be in the main thread
}
I hope it is obvious by now that the suspend modifier is not going to stop the main thread from calling the function.
#Suppress("RedundantSuspendModifier")
suspend fun someHeavyComputation(): Result {
return ...
}
launch(Dispatchers.Main) {
val result = someHeavyComputation() // this will run in the main thread
messWithUi(result)
}
Now if you want the computation not to be done in the main thread:
suspend fun someHeavyComputation() = withContext(Dispatchers.Default) {
... // this will be in a thread pool
}
Further reading: Blocking threads, suspending coroutines.
Related
We have a button in the UI, which, when pressed, will make some remote network call in its own coroutine. However, if the user spams the button for whatever reason, it is possible that the remote data might somehow get corrupted. We would like to prevent this by discarding all requests until the current one is completed.
There are many ways to do this. I have create a simple extension function on CoroutineScope to only launch if the CoroutineScope is not active. This is what I have created:
Extension Function
fun CoroutineScope.safeLaunch(dispatcher: CoroutineDispatcher, block: () -> Unit): Job {
return if (!isActive) {
launch(dispatcher) {
block()
}
} else {
launch {}
}
}
Example Use
fun loadNotifications() {
viewModelScope.safeLaunch(IO) {
getNotifications.invoke() // Suspend function invoke should only be from a coroutine or another suspend function
}
}
The problem is, the above won't compile as I get an error saying
Suspend function invoke should only be from a coroutine or another
suspend function
Does anyone know what I'm doing wrong or how to make it work?
There are multiple problems with this code:
Fixing the error you mentioned is very easy and requires to only specify block as suspendable: block: suspend () -> Unit.
isActive doesn't mean the job/scope is actively running something, but that it hasn't finished. isActive in your example always returns true, even before launching any coroutine on it.
If your server can't handle concurrent actions, then you should really fix this on server side. Limiting the client isn't a proper fix as it can be still exploited by users. Also, you need to remember that multiple clients can perform the same action at the same time.
As you mentioned, there are several ways how this situation can be handled on the client side:
In the case of UI and the button, it is probably the best for the user experience to disable the button or overlay the screen/button with a loading indicator. It gives the user the feedback that the operation is running in the background and at the same time it fixes the problem with multiple calls to the server.
In general case, if we just need to limit concurrency and reject any additional tasks while the last one is still running, probably the easiest is to use Mutex:
private val scope = CoroutineScope(EmptyCoroutineContext)
private val mutex = Mutex()
fun safeLaunch(block: suspend () -> Unit) {
if (!mutex.tryLock()) {
return
}
scope.launch {
try {
block()
} finally {
mutex.unlock()
}
}
}
Note we need a separate mutex per scope or per the type of the task. I don't think it is possible to create such utility as a generic extension function, working with any coroutine scope. Actually, we can implement it in a very similar way to your original code, but by looking at the current job's children. Still, I consider such solution hacking and I discourage it.
I execute UI related operation in the onSubscribe action of do operator. I obtain an
Main Thread Checker: UI API called on a background thread
console error in Xcode. I tried to use observe(on: MainScheduler.instance) but without success.
This my snippet:
checkCurrentNetworkAndConnect().observe(on: MainScheduler.instance).do(onSubscribe: {
// UI related stuff
})
I also tried with subscribe(on: MainScheduler.instance) but doesn't work.
How can I achieve this?
This will happen if you are calling subscribe on a background thread. If you call subscribe on a background thread, then the Observable will be subscribed to on that background thread. To solve this, you will need to use subscribe(on:) after the do operator...
Something like this:
func example() {
checkCurrentNetworkAndConnect()
.do(onSubscribe: {
print("UI related stuff")
})
.subscribe(on: MainScheduler.instance)
.subscribe()
}
With the above, even if you call example() on a background thread, the onSubscribe: closure will be executed on the main thread.
If for some reason you also want the checkCurrentNetworkAndConnect() on a background thread, then you can do that with another subscribe(on:) above the do.
All this said, you should not be performing significant side effects inside a do(onSubscribe:) in the first place. It's one thing to put debug code in there but not anything more than that. I've been using RxSwift for 6 years and I've never needed to use a do(onSubscribe:) at all, much less for significant code. There is likely a better way to accomplish the ultimate goal you are trying to accomplish.
In response to your comment. How you go about doing what you want depends very much on specifics not provided. Below I have made some simplifying assumptions. If your problem is different, I suggest you post a new question with the details...
func example(operations: [Observable<Void>], messages: [String], label: UILabel, disposeBag: DisposeBag) {
Observable.concat(operations.enumerated().map { index, op in
op.map { messages[index + 1] }
})
.startWith(messages[0])
.observe(on: MainScheduler.instance)
.bind(to: label.rx.text)
.disposed(by: disposeBag)
}
The code above assumes:
that you have an array of operations expressed as observables
that none of them need data provided by a previous operation
that none of them emit data you care to use or keep
that every operation has a message that needs to be displayed before the operation starts
that there is an additional message to be displayed when the last operation completes
After reading the official docs on coroutine cancelation, If for the example I have the following code:
val job = scope.launch {
val userId = networkOperationOne()
//check if coroutine is still active before calling operation two?
val userDetails = networkOperationTwo(userId)
}
Should I check isActive before calling network call two?
Let's assume that job.cancel() was called while networkOperationOne() is still in progress and that I'm not calling any suspending function that automatically does the cancelation for me.
It depends on how networkOperationOne and networkOperationTwo are suspending.
They may internally be cooperative anyway, which means you do not have to check isActive.
When in doubt, throw in ensureActive() to perform the check and act accordingly.
In this case, the conditional check is negligible compared to the network request so add one in.
I often have to execute code on a separate thread that is long running, blocking, instable and\or has a potential to hang forever. Since the existence of TPL the internet is full of examples that nicely cancel a task with the cancellation token but I never found an example that kills a task that hangs. Code that hangs forever is likely to be expected as soon as you communicate with hardware or call some third party code. A task that hangs cannot check the cancellation token and is doomed to stay alive forever. In critical applications I equip those tasks with alive signals that are sent on regular time intervals. As soon as a hanging task is detected, it is killed and a new instance is started.
The code below shows an example task that calls a long running placeholder method SomeThirdPartyLongOperation() which has the potential to hang forever. The StopTask() first checks if the task is still running an tries to cancel it with the cancellation token. If that doesn’t work, the task hangs and the underlying thread is interrupted\aborted old school style.
private Task _task;
private Thread _thread;
private CancellationTokenSource _cancellationTokenSource;
public void StartTask()
{
_cancellationTokenSource = new CancellationTokenSource();
_task = Task.Factory.StartNew(() => DoWork(_cancellationTokenSource.Token), _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
public void StopTask()
{
if (_task.Status == TaskStatus.RanToCompletion)
return;
_cancellationTokenSource.Cancel();
try
{
_task.Wait(2000); // Wait for task to end and prevent hanging by timeout.
}
catch (AggregateException aggEx)
{
List<Exception> exceptions = aggEx.InnerExceptions.Where(e => !(e is TaskCanceledException)).ToList(); // Ignore TaskCanceledException
foreach (Exception ex in exceptions)
{
// Process exception thrown by task
}
}
if (!_task.IsCompleted) // Task hangs and didn't respond to cancellation token => old school thread abort
{
_thread.Interrupt();
if (!_thread.Join(2000))
{
_thread.Abort();
}
}
_cancellationTokenSource.Dispose();
if (_task.IsCompleted)
{
_task.Dispose();
}
}
private void DoWork(CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(Thread.CurrentThread.Name)) // Set thread name for debugging
Thread.CurrentThread.Name = "DemoThread";
_thread = Thread.CurrentThread; // Save for interrupting/aborting if thread hangs
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested();
SomeThirdPartyLongOperation(i);
}
}
Although I’ve been using this construct for some years now, I want to know if there are some potential mistakes in it. I’ve never seen an example of a task that saves the underlying thread or gives it a name to simplify debugging, so I’m a bit unsure if this is the right way to go. Comment on any detail is welcome!
Code that hangs forever is likely to be expected as soon as you communicate with hardware or call some third party code.
Communication: absolutely not. There's always a way to timeout with communication APIs, so even with misbehaving hardware, there's no need to force-kill an I/O operation.
Third-party code: only if you're paranoid (or have high demands such as 24x7 automation).
Here's the bottom line:
There's no way to force-kill a task.
You can force-kill a thread, but this can easily cause serious problems with application state, possibility if introducing deadlocks in other parts of the code, and resource leaks.
You can force-kill an appdomain, which solves a large portion of app state / deadlock issues with killing threads. However, it doesn't solve them all, and there's still the problem of resource leaks.
You can force-kill a process. This is the only truly clean and reliable solution.
So, if you choose to trust the third-party code, I recommend that you just call it like any other API. If you require 100% reliability regardless of third-party libraries, you'll need to wrap the third-party dll into a separate process and use cross-process communication to call it.
Your current code force-kills a thread pool thread, which is certainly not recommended; those threads belong to the thread pool, not to you, and this is still true even if you specify LongRunning. If you go the kill-thread route (which is not fully reliable), then I recommend using an explicit thread.
The question is why is this task even hanging at all? I think there's no universal solution to this problem but you should focus on the task to be always responsible and not on forcing to interrupt it.
In this code, it looks like you're looking for a simple thread rather than a task - you shouldn't link tasks to threads - it's very likely that the task will switch to another thread after some async operations and you will end up on killing an innoccent thread that is not connected to your task anymore. If you really need to kill the whole thread then make a dedicated one just for this job.
You shouldn't also name or do anything with any thread that is used for tasks' default pool. Consider this code:
static void Main(string[] args)
{
Task.Run(sth);
Console.Read();
}
static async Task sth()
{
Thread.CurrentThread.Name = "My name";
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
await Task.Delay(1);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(Thread.CurrentThread.Name ?? "No name");
}
the output is:
3
4
No name
New to async and trying to understand when it makes sense to use it.
We are going to have lots methods in webapi2 calling legacy webservices.
We have lots of low level dlls (Company.Dal.dll,Company.Biz.dll) etc.. that have methods that are not async
Question
Does async has to be all the way really ?
Is there any benefit of having an high level dll (all method async) calling low level dlls (dal,biz etc legacy code) where none of the method are async?
Is it there any benefit in having just the high level component to be async and the rest syncronous?
Many thanks for clarification
Any good tutorials explaning this concept
Using async only makes sense if you actually await something. If you don't, the async method will actually be completely synchronous (and you get a warning from the compiler about it).
In this case, async doesn't have any advantages, only disadvantages: the code is more complex and less efficient.
A thread can only do one thing at a time. If procedures keep your thread busy, there is no sense in making them async.
However if there are periods where the thread in your procedure has to wait for something else to finish, your thread might do something useful instead. In those circumstances async-await becomes useful.
Eric lippert once explained async-await with a restaurant metaphor (search on the page for async-await). If you have a cook who has to wait until the bread is toasted, this cook could do something else, like cooking an egg, and get back to the toaster when the "something else" is finished, or when has to wait for something, like await for the egg to be cooked.
In software the things where your thread typically will do nothing except waiting for something to finish is when reading / writing to disk, sending or receiving data over the network etc. Those are typically actions where you can find async versions as well as non-async versions of the procedure. See for instance classes like Stream, TextReader, WebClient, etc.
If your thread has to do a lot of calculations, it is not useful to make the function async, because there is no moment your thread will not do anything but wait, so your thread won't have time to do other things.
However, if your thread could do something useful while the calculations are done, consider letting another thread do those calculations while your thread is doing the other useful stuff:
private async Task<int> MyLengthyProcedure(...)
{
Task<int> calculationTask = Task.Run( () => DoCalculations(...));
// while one of the threads is doing the calculations,
// your thread could do other useful things:
int i = DoOtherCalculations();
// or if there are calculations that could be performed
// by separate threads simultaneously, start a second task
Task<int> otherCalculationTask = Task.Run( () => DoEvenMoreCalculations(...));
// now two threads are doing calculations. This thread is still free to
// do other things
// after a while you need the result of both calculations:
await Task.WhenAll( new Task[] {calculationTask, otherCalculationTask});
// the int return value of DoCalculations and DoOtherCalculations are in
// the Result property of the task object:
int j = calculationTask.Result;
int k = otherCalculationTask.Result;
return i + j + k;
;