1

I'm new to kotlin. I have encountered a problem where the CountDownLatch is not unlocking, I am not sure whether which line causing the problem for the infinite lock.

class RestaurantOverviewDataSource {
fun loadRestaurants(): List<RestaurantOverview>{
    val db = Firebase.firestore
    var restaurants : List<RestaurantOverview> = emptyList()
    val lock = CountDownLatch(1)
    try {
        Log.w(TAG, "FIREBASE")
        db.collection("tb_mydata").get().addOnSuccessListener { result ->
            try{
                var count = 0;
                for (document in result) {
                    count++;
                    var category = document.data["Category"] as String
                    var name = document.data["Name"] as String
                    var contact = document.data["Contact"] as String
                    var addr = document.data["Address"] as String
                    restaurants += RestaurantOverview(count, R.color.grey1, name, category, 20, 4.1f, 150, true, true, true, true)
                    Log.d(TAG, "${document.id} => ${document.data}")
                }
            } finally {
                lock.countDown()
            }
        }.addOnFailureListener { exception ->
            try{
                Log.w(TAG, "Error getting documents.", exception)
                restaurants = listOf(RestaurantOverview(1, R.color.grey1, "KFC 1", "Western / Fast Food", 20, 4.0f, 190, isHalal = true, isVegan = true, isVegetarian = true, isOpen = true))
            } finally {
                lock.countDown()
            }
        }.addOnCompleteListener {
            Log.w(TAG, "on complete")
            lock.countDown()
        }
    }catch (ignored: NullPointerException) { lock.countDown() }
    Log.w(TAG, "INIT AWAIT")
    lock.await()
    Log.w(TAG, "DONE AWAIT")
    return restaurants
}

I have reference and used the suggestion provided at here CountDownLatch not freeing thread, where try{}finally{} is added, but still it's not freeing the thread. Any advice? Thanks in advance.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Joshua Ooi
  • 1,139
  • 7
  • 18

2 Answers2

1

If you get any Exception in try{ } finally blocks it will be catched by catch (ignored: NullPointerException) or by callee of loadRestaurants method. If this happens nobody can call lock.countDown to release it from await.

To avoid this you can also provide a timeout and catch that exception as follows:

try{
    lock.await(5, TimeUnit.SECONDS)
}catch(e : InterruptedException) {}
Stoica Mircea
  • 782
  • 10
  • 22
  • 1
    Tried. I think the problem is cause by the deadlock from main thread as stated by Ryan. Btw, thanks for your advice! – Joshua Ooi May 06 '21 at 02:35
1

I'm assuming that loadRestaurants is called on the main thread.

The problem here is that none of your listeners can ever be run, because you're blocking the main thread. Firebase invokes the callbacks you set with addOnSuccessListener, etc. on the main thread. But in order for other code to run on the main thread, you need to hand back control of the thread to the system. By awaiting the latch, you're not handing back control of the thread to the system.

Basically, the end result is that you're waiting for the latch to be counted down, and Firebase is waiting for you to finish waiting before it invokes the callback that will count it down.

You can confirm this by adding logs to all your listeners and verifying that they're never hit.

To fix it, you'll need to restructure your code. You're not going to be able to return your restaurants list synchronously, because you don't want to block the main thread (even if you could wait, your UI would be frozen while Firebase does its work). Instead, you should do whatever you were going to do with restaurants in the success/failure callbacks themselves.

Ryan M
  • 18,333
  • 31
  • 67
  • 74