Cancel retrofit request when using it with coroutine flow - retrofit2

I have retrofit interface like below:
#GET("products")
fun getProducts(
#Query("limit") limit: Int,
#Query("offset") offset: Int
): Flow<List<Product>>
I'm calling this method like below:
restService
.getProducts(limit, offset)
.flowOn(ioContext)
.catch { /* handle error */ }
.collect { /* handle result */ }
This code snippet could be called in situations:
When pulled Swipe refresh
When loading the next portion of data.
That's why I want to cancel the previous request before making a new one. How I can achieve this?

I think your best option is to run the collection in a coroutine that gets cancelled when the data is not needed anymore. It can be as part of a flow. An example:
combineTransform(limitFlow, offsetFlow, refreshSignal) {
emitAll(restService.getProducts(limit, offset))
}.collect { }
(here refreshSignal would emit whenever a refresh is needed).
The flow returned by getProducts() will be cancelled each time new values are emitted from the input flows.
You could as well cancel the collection manually, keeping the job and calling cancel on it.
val getProductsJob = launch {
restService
.getProducts(limit, offset)
.flowOn(ioContext)
.catch { /* handle error */ }
.collect { /* handle result */ }
}
... then you can cancel that job whenever you feel like:
getProductsJob.cancel()

Related

Glib & Gstreamer: Does a probe callback behave like another thread in glib

I am trying to ask a question with reference to the question Glib: Calling a iterative loop function
I am actually doing GStreamer and trying to use Glib library function in my application as much as possible. The program require some system event/response before performing some action in response to some user input
Design of flow
User input the option and application take it as user_input is asserted to be true
Application need install a callback (called it callback_A) -- which wait out for buffer flowing in that point of application
Callback-A will be called whenever buffer passes through a point.
In callback A, Application to wait for some particular condition (ie a key-frame buffer passing through) at a point. If a key frame buffer pass through, it will then install a second callback at some point downstream, callback-B. An EOS event is send out through the pipeline. Otherwise wait for next time a buffer pass through
In callback B, it will wait for the event (EOS) to arrive and determine further action. If everything is completed, set task_completed to be true
function return to main while loop. The blocking (task_completed) is released and the application will report the task completed to UI
Problem'
Currently I faced a problem of the callback not completing their task (takes a long time) before it went to being blocked by task completed (and being blocked thereafter)
Question
In Glib, is a callback within the same memory space(or thread) as its caller?
In Glib, how do I overcome the problem of being blocked? Is there some methods to ensure that the task are being run before time up and control is returned to the caller
Will a gthread help? Putting the two call-back as a separate thread since they need to wait for some events to happen
This may be too much. Any alternatives, example polling instead of callback in this case.
Code
Here is my pseudocode
gbool user_input;
gbool task_completed = false;
static void
callback_B(GstPad *pad,
GstPadProbeInfo *info,
gpointer udata)
{
//// wait for some events--- call it event B
GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
if (GST_EVENT_TYPE (event) != GST_EVENT_EOS)
return GST_PAD_PROBE_OK;
/// do something
/// ......
task_completed =true;
return GST_PAD_PROBE_REMOVE;
}
static void
callback_A( GstPad *pad,
GstBuffer * buffer,
gpointer udata)
{
//// wait for some event call it event A
if( !GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT))
{
/// install callback-B to determine some condition
gst_pad_add_probe ( pad,
GST_PAD_PROBE_TYPE_BLOCK,
(GSourceFunc)callback_B,
//NULL,
NULL,
NULL);
GstPad* padB = gst_pad_get_peer (pad);
gst_pad_send_event(padB, gst_event_new_eos());
}
else
{
return GST_PAD_PROBE_REMOVE;
}
}
gboolean
check_cmd_session(NULL )
{
if(user_input)
{
// ........ some other actions *****************
/// initialize task_complete to be false
task_completed = false;
//// install callback_A
gst_pad_add_probe(padA,
GST_PAD_PROBE_TYPE_BUFFER,
callback_A,
NULL,
NULL);
while(!task_completed)
g_usleep(10000);
/// notify UI of changes done
notify_UI();
}
}

DBFlow - Async DB calls

In DBFlow's docs you can read that:
While generally saving data synchronous should be avoided, for small
amounts of data it has little effect.
That's great! ...But, on the other hand there's this notice:
Doing operations on the main thread can block it if you read and write
to the DB on a different thread while accessing DB on the main.
So if I understand this correctly, if I have for example android service that periodically reads/writes to db using async transactions (in separate thread) and user clicks a button in Activity that executes simple model.save() then the main thread of my android application would be blocked and app would 'freeze' until db is unlocked.
Is that right?
If it is, the solution could be to place all db calls on async thread's queue, like:
interface AsyncResult {
fun onResult(success: Boolean)
}
fun MyModel.save(callback: AsyncResult) {
database<AppDatabase>().beginTransactionAsync { this.save() }
.success { callback.onResult(true) }
.error { _, _ -> callback.onResult(false)}
.build()
.execute()
}
and exeute it from Activity like:
myModel.save(object: AsyncResult {
override fun onResult(success: Boolean) = if (success) showToast("got it")
})
But aren't the simple calls like save(), insert() wrapped in implicit transactions, so we end up with nested transactions?
Not to mention all the boilerplate code on the model and view layers.
What is the proper way to deal with such a problem?

Cross Thread UI

I have an issue with cross threading on a UI. I have read all the ways to do it and have implemented them as seen below.
public void UpdateList(object obj)
{
// do we need to switch threads?
if (listBox1.InvokeRequired)
{
MethodInvoker del = () => UpdateList(obj);
this.Invoke(del);
return;
}
// ok so now we're here, this means we're able to update the control
// so we unbox the object into a string
string text = (string)obj;
// and update
listBox1.Items.Add(text);
}
The issue comes when I try to do a
hubConnection.Start().Wait();
After that call I am trying to update my list.
Without the wait is fine. When I add the Wait it hangs on the UpdateList Invoke. There is no error...it just hangs.
I am handling this call in a button event.
Wait() is creating a deadlock on the mainthread.
Replace the hubconnection.Start.Wait() with:
await hubconnection.Start() in an async method:
public void async StartHubClickedEvent(...){
await hubconnection.Start()
}
The Microsoft Async library enables use of async/awaut on .net 4.0 and VS12.
Install-Package Microsoft.Bcl.Async
See Deadlock when thread uses dispatcher and the main thread is waiting for thread to finish
You've generated a recursive loop. Assuming an Invoke is Required, you'll call up the same method, hit if (listBox1.InvokeRequired) again (which will still pass true) and start looping as you keep calling up the same method again and again. It's better to do an If..Else pattern here where you directly invoke the change on the ListBox or simply perform the change without the invoke
An Example
if (listBox1.InvokeRequired)
{
listBox1.Invoke(()=> { listBox1.Items.Add((string)text) };
}
else
{
string text = (string)obj;
// and update
listBox1.Items.Add(text);
}

setTimer() to generate WM_TIMER message only once

I am using timers with resource ID added and based on WM_TIMER message.
I would like to call a routine like DrunkenDragon() on OnTimer() but for only once after SetTimer(id,10sec,NULL) was called. We know that Call to KillTimer() inside DrunkenDragon() routine would fix the solution. Is it okay to go with this, or am I missing out something great with timers.
(Only answering this in case someone else comes across it like I did and was unsatisfied with the answers available)
So, in WindowClass.h, what you can do is an enumeration of the timer identifiers you want to use. While you certainly can use raw numeric values, using symbols is probably easier to work with in the long run.
class WindowClass : CWnd
{
// other parts of the interface...
protected:
enum
{
TIMER_MAIN_UPDATE = 1,
TIMER_PLASTERED_DRAGON
};
};
Meanwhile, back at in WindowClass.cpp,
int WindowClass::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
// { ... other initialization code }
// In case you want to do other types of updates at regular intervals.
SetTimer(TIMER_MAIN_UPDATE, 1000, NULL);
// Note the symbolic identifiers.
SetTimer(TIMER_PLASTERED_DRAGON, 10000, NULL);
return 0;
}
That's only any good if you want to do it 10 seconds after the window's been created, though. You can also just call SetTimer() in some other event handler whenever you'd like:
void WindowClass::OnJustGotPaid()
{
// { ... other handling }
// Since our dragon is a real lightweight, it apparently only takes
// 10 seconds to get him puking up flaming vomit.
SetTimer(TIMER_PLASTERED_DRAGON, 10000, NULL);
}
When it comes time for the actual event to be handled, it is typically handled in the Windows OnTimer() callback. A timer event can be directed to a different (custom) callback, if desired, by specifying a valid function pointer in SetTimer()'s third parameter instead of NULL.
void WindowClass::OnTimer(UINT_PTR p_timer_id)
{
switch(p_timer_id)
{
default:
break;
case TIMER_MAIN_UPDATE:
// { ... main update code }
break;
case TIMER_PLASTERED_DRAGON:
// Killing the timer first in case DrunkenDragon() takes a good
// long while for whatever reason.
KillTimer(TIMER_PLASTERED_DRAGON);
DrunkenDragon();
break;
}
}
int CYourDialog::OnInitDialog()
{
__super::OnInitDialog();
SetTimer(0x10, 10000, NULL);
return true;
}
void CYourDialog::OnTimer(UINT_PTR ignore)
{
DrunkenDragon();
}
And ensure you have ON_WM_TIMER in message-map.
You are not missing anything and you would have to use KillTimer for system to stop generating WM_TIMER messages.
You can also use CreateTimerQueueTimer and set parameters the way a callback is called only once.
See this for more details.

Parallel.Invoke - Exception handling

My code runs 4 function to fill in information (Using Invoke) to a class such as:
class Person
{
int Age;
string name;
long ID;
bool isVegeterian
public static Person GetPerson(int LocalID)
{
Person person;
Parallel.Invoke(() => {GetAgeFromWebServiceX(person)},
() => {GetNameFromWebServiceY(person)},
() => {GetIDFromWebServiceZ(person)},
() =>
{
// connect to my database and get information if vegeterian (using LocalID)
....
if (!person.isVegetrian)
return null
....
});
}
}
My question is: I can not return null if he's not a vegeterian, but I want to able to stop all threads, stop processing and just return null. How can it be achieved?
To exit the Parallel.Invoke as early as possible you'd have to do three things:
Schedule the action that detects whether you want to exit early as the first action. It's then scheduled sooner (maybe as first, but that's not guaranteed) so you'll know sooner whether you want to exit.
Throw an exception when you detect the error and catch an AggregateException as Jon's answer indicates.
Use cancellation tokens. However, this only makes sense if you have an opportunity to check their IsCancellationRequested property.
Your code would then look as follows:
var cts = new CancellationTokenSource();
try
{
Parallel.Invoke(
new ParallelOptions { CancellationToken = cts.Token },
() =>
{
if (!person.IsVegetarian)
{
cts.Cancel();
throw new PersonIsNotVegetarianException();
}
},
() => { GetAgeFromWebServiceX(person, cts.Token) },
() => { GetNameFromWebServiceY(person, cts.Token) },
() => { GetIDFromWebServiceZ(person, cts.Token) }
);
}
catch (AggregateException e)
{
var cause = e.InnerExceptions[0];
// Check if cause is a PersonIsNotVegetarianException.
}
However, as I said, cancellation tokens only make sense if you can check them. So there should be an opportunity inside GetAgeFromWebServiceX to check the cancellation token and exit early, otherwise, passing tokens to these methods doesn't make sense.
Well, you can throw an exception from your action, catch AggregateException in GetPerson (i.e. put a try/catch block around Parallel.Invoke), check for it being the right kind of exception, and return null.
That fulfils everything except stopping all the threads. I think it's unlikely that you'll easily be able to stop already running tasks unless you start getting into cancellation tokens. You could stop further tasks from executing by keeping a boolean value to indicate whether any of the tasks so far has failed, and make each task check that before starting... it's somewhat ugly, but it will work.
I suspect that using "full" tasks instead of Parallel.Invoke would make all of this more elegant though.
Surely you need to load your Person from the database first anyway? As it is your code calls the Web services with a null.
If your logic really is sequential, do it sequentially and only do in parallel what makes sense.

Resources