0

I am trying to bridge some existing rx code and api that uses futures. When I manually dispose an observable I would expect onDispose() to be always called. It usually does but when I specify a custom scheduler it sometimes does not get called. My example:

class Work {

private val disposables = CompositeDisposable()

fun getFuture(): ListenableFuture<String> {

    val future = ResolvableFuture.create<String>()

    disposables.add(

            Observable.fromCallable {
                try {
                    Thread.sleep(2000)
                } catch (ex: InterruptedException) {

                }
                "1"
            }.firstOrError()
                .onErrorReturn { "2" }
                .doOnDispose {
                    println("disposing 1 on ${Thread.currentThread().name}")
                    //sometimes this dispose does not get called
                    future.set("2")
                }
                .subscribeOn(Schedulers.io())
                .doOnDispose {
                    println("disposing 2 on ${Thread.currentThread().name}")
                    //only this dispose gets called every time
                    //future.set("2")
                }
                .subscribe(Consumer {
                    future.set("2")
                })
    )

    return future
}

fun stop() {
    disposables.clear()
}

}

@Test
fun `doOnDispose does not get called`() {
    println("------------")

    for (i in 1..100) {

        val work = Work()

        val future = work.getFuture()

        println("Cancelling")

        work.stop()

        println("Getting ${Thread.currentThread().name}")
        val result = future.get(2, TimeUnit.SECONDS)

        assertEquals("2", result)

        println("------------")
    }
}

What happens is only the second onDispose gets called every time. The one before .subscribeOn() is sometimes not called at all.

Pawel
  • 191
  • 1
  • 6

1 Answers1

0

You are mixing modes here. Don't do that. Either use RxJava or use Futures. You have created the start of a maintenance nightmare.

A thread is created on the io() scheduler, which immediately sleeps for 2 seconds. The observer chain is disposed ("disposing 2"), releasing resources, and the dispose() works its way back up the chain. However, dispose() won't do anything on the io() thread because it is blocked by sleep(). There is now a race condition whether the thread completes or the dispose operation works first.

I can't advise you how to fix the problem as I don't know what you are trying to do. I just know that what you have is unreliable.

Bob Dalgleish
  • 8,167
  • 4
  • 32
  • 42
  • Yes I definitely would not use futures but you somehow need to glue android workmanger with Rxjava otherwise you have to use toBlocking() which is bad https://developer.android.com/jetpack/docs/release-notes – Pawel Oct 24 '18 at 21:13
  • Well, `get()` on `Future` is also blocking. I suspect that you may not be clear on the lifecycle of an observer chain: If you need to free up resources when the operation finishes or is no longer needed, it is probably better to create an observable that manages all of these steps. – Bob Dalgleish Oct 25 '18 at 13:35