question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Premature thread interruption when using observeOn operator

See original GitHub issue

When using RxJava 2.1.3 to 2.1.6, we observed a possible racing condition which caused the observeOn thread to be interrupted prematurely.

A sample code snippet could be the following (not a complete example):

Observable.fromPublisher(publisher)
.observeOn(Schedulers.computation())
.firstElement()
.map(operation)
.blockingGet();

In the above example, observeOn operator would create a ObservableObserveOn instance. When there is the first item coming (ObservableObserveOn.ObserveOnObserver.onNext() is called), a Runnable is scheduled via the NewThreadWorker.scheduleActual(). Inside NewThreadWorker.scheduleActual(), a ScheduledRunnable is created wrapping the given Runnable, and submit the ScheduledRunnable to the executor for execution. Then the future returned by the executor is set into ScheduledRunnable via setFuture method.

The problem lies when the ScheduledRunnable.dispose() method is called between submitting to the executor and ScheduledRunnable.setFuture method by the underlying Runnable wrapped in ScheduledRunnable (which is executed in parallel by the executor). In this case, the Runnable contains the firstElement() operator would create a ObservableElementAtMaybe instance. ObservableElementAtMaybe.onNext() method would call the dispose() method before calling actual.onSuccess(). So combining these, if ScheduledRunnable.setFuture is called after ScheduledRunnable.dispose() in ObservableElementAtMaybe.onNext(), the actual.onSuccess() would be interrupted, which is not what we want.

tl;dr In the sample code, if the execution order is the following, it would create a thread interruption that interfere with the processing of the chained operators.

Thread 1: future = executor.submit(ScheduledRunnable)
Thread 2 (executor thread): ScheduledRunnable.dispose()
Thread 1: ScheduledRunnable.setFuture(future)
Thread 2: actual callback (e.g. actual.onSuccess()) <-- Thread interrupted. Throws exception when we call some synchronization methods in a non-blocking way (e.g. Semaphore.tryAcquire(0, TimeUnit.SECONDS)). 

A possible solution is to change the order, i.e. call actual.onSuccess() before dispose(). I searched the code base and found out that in general, dispose() is called before the actual callback. I am not sure if there are other concerns with this specific ordering (i.e. dispose before actual callback), but it certainly sounds odd to me.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:13 (6 by maintainers)

github_iconTop GitHub Comments

2reactions
visualagecommented, Nov 8, 2017

I would argue that the behavior of interrupting a downstream processing thread for observeOn when the observeOn is disposed is questionable. I think it makes perfect sense to interrupt the upstream threads when an operator is disposed, but definitely not downstream threads.

If your argument stands, i.e. we should not have interrupt-sensitive code in RxJava callbacks, why cancel the future with interrupt in the first place?

0reactions
adrian-bakercommented, Jan 7, 2020

I do the maintenance for free and I look at people’s profiles to see if they or their employer do significant contribution to open-source

I’m not making any judgement at all on your personal contribution. Just trying making the point that this issue perhaps should not be closed without a fix, since it creates surprising and difficult to debug breakages downstream.

Read more comments on GitHub >

github_iconTop Results From Across the Web

ObserveOn operator - ReactiveX
The ObserveOn operator specifies a different Scheduler that the Observable will use to send notifications to its observers. As shown in this illustration,...
Read more >
Coroutines and RxJava — An Asynchronicity Comparison ...
Threading in RxJava is controlled by two operators: subscribeOn() and observeOn() . In a nutshell, subscribeOn specifies the Scheduler on which ...
Read more >
Understanding Thread Interruption in Java - Praveer's Musings
If using Thread directly in your code, you may call interrupt() on the instance of thread. If using Executor framework, you may cancel...
Read more >
APIContracts Reference - ReactiveCocoa
Prefer writing operators that apply to both signals and producers; Compose existing operators when possible; Forward failure and interruption events as soon ...
Read more >
Reactor 3 Reference Guide
I Used an Operator on my Flux but it Doesn't Seem to Apply. What Gives? C.3. ... How to switch threading contexts using...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found