CountDownLatch in Android locking thread
Asked Answered
V

2

5

I've just started playing around with CountDownLatch in my Android app. Currently I am trying to make two Volley requests to my api, and wait until the data has been retrieved and stored before continuing with thread execution.

This is a sample of my code:

    // new CountDownLatch for 2 requests
    final CountDownLatch allDoneSignal = new CountDownLatch(2);

    transactions.getResourcesForRealm(Contact.class, "", new ICallBack<Contact>() {
        @Override
        public void onSuccess(ArrayList<Contact> resources, String resourceId) {
            transactions.createRealmObject(resources, Contact.class);

            allDoneSignal.countDown();
        }

        @Override
        public void onFail(ArrayList<Contact> resources) {

        }
    });

    transactions.getResourcesForRealm(Meeting.class, "", new ICallBack<Meeting>() {
        @Override
        public void onSuccess(ArrayList<Meeting> resources, String resourceId) {
            transactions.createRealmObject(resources, Meeting.class);

            allDoneSignal.countDown();
        }

        @Override
        public void onFail(ArrayList<Meeting> resources) {

        }
    });

    try {
        allDoneSignal.await();
        // continue executing code
        // ...
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

The issue is that it doesn't seem to "complete" the countdown and therefore freezes because the latch is never released. I have confirmed that the API requests are working and the onSuccess callback is hit successfully, but the thread hangs.

UPDATE I've just noticed that with the CountDownLatch set to 0, it hits onSuccess, but when I set it to anything greater than 0, it freezes and onSuccess is never called. Seems like something's funky with the threading.

Voltaism answered 1/8, 2016 at 8:52 Comment(6)
I may be wrong but for thread and value related issue please check Volatile variable concept.Sough
That should work as expected - can you maybe log something just before calling countDown to make sure that the method is really called?Sahaptin
@PradeepKumarKushwaha There is no need for volatile here, the latch already gives sufficient synchronization guarantees.Sahaptin
I will attempt to Log.wtf this...Voltaism
Also you say "new countdownlatch for 2 requests" - maybe double check that the allDoneSignal variable is the same in the three places (if the code is exactly like you posted in a single method then it obviously is)...Sahaptin
can you provide the entire code and the code of getResourcesForRealm?Vicar
V
6

Your code is too error prone, you need to call countDown() in a finally block and call it also in onFail otherwise in case of failure your application will freeze for ever. So your code should rather be something like:

transactions.getResourcesForRealm(Contact.class, "", new ICallBack<Contact>() {
    @Override
    public void onSuccess(ArrayList<Contact> resources, String resourceId) {
        try {
            transactions.createRealmObject(resources, Contact.class);
        } finally {
            allDoneSignal.countDown();
        }
    }

    @Override
    public void onFail(ArrayList<Contact> resources) {
        allDoneSignal.countDown();
    }
});

transactions.getResourcesForRealm(Meeting.class, "", new ICallBack<Meeting>() {
    @Override
    public void onSuccess(ArrayList<Meeting> resources, String resourceId) {
        try {
            transactions.createRealmObject(resources, Meeting.class);
        } finally {
            allDoneSignal.countDown();
        }
    }

    @Override
    public void onFail(ArrayList<Meeting> resources) {
        allDoneSignal.countDown();
    }
});
Vicar answered 1/8, 2016 at 9:7 Comment(5)
While this is a good advice in general, it doesn't answer the question, the question is a very bad and requires assumption that you don't have to do.Tabriz
@SleimanJneidi yes I know but we all know that CountDownLatch works well so if the program freezes it is because countDown() is not called twice as expected for some reason so I simply provide a way to make sure that it is called even in case of failureVicar
Thanks for the good error handling, I've implemented this and i'm trying to figure out why it doesn't hit countDown()Voltaism
@Voltaism I believe that it means that onSuccess is called by the calling thread, try to print the value of Thread.currentThread()Vicar
I have tried passing the latch in as a parameter for the async request and counting down in the new thread. It still just hangs.Voltaism
R
4

Sorry for the late answer, but if it's still any help to anyone:

You need to do the ".await" in a separate thread because it blocks the current thread.

Example:

final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
new Thread(new Runnable() {
    @Override
    public void run() {
        allDoneSignal.await();
        mainThreadHandler.post(new Runnable() {
            doSomethingWhenAllDone();
        });
}
}).start()
Rotz answered 30/7, 2018 at 8:10 Comment(1)
hello. Is this a working solution?Nth

© 2022 - 2024 — McMap. All rights reserved.