I have the following RxJava 2 code (in Kotlin), which have an Observable that
disposable = Observable.create<String>({
subscriber ->
try {
Thread.sleep(2000)
subscriber.onNext("Test")
subscriber.onComplete()
} catch (exception: Exception) {
subscriber.onError(exception)
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ result -> Log.d("Test", "Completed $result") },
{ error -> Log.e("Test", "Completed ${error.message}") })
While it is still Thread.sleep(2000), I perform disposable?.dispose() call, it will error out
FATAL EXCEPTION: RxCachedThreadScheduler-1
Process: com.elyeproj.rxstate, PID: 10202
java.lang.InterruptedException
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:371)
at java.lang.Thread.sleep(Thread.java:313)
at presenter.MainPresenter$loadData$1.subscribe(MainPresenter.kt:41)
at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:40)
I expect the dispose would help to cancel the operation silently, or at most, have the error catch with with the Log.e on the subscribe. However, it just crash as per the error message above.
Why did the Exception escape? Isn't dispose suppose to cancel the entire operation silently without crashing?
There is a combination of factors here:
dispose of a stream that uses subscribeOn also disposes of the thread used. This also involves calling Thread.interrupt() when using Schedulers.io(). This causes the exception in your case.
InterruptedException is an Exception thrown by Thread.sleep, so it is caught by your code and passed to onError like any other exception.
Calling onError after dispose redirects the error to the global error handler due to RxJava2's policy of NEVER throwing away errors. To work around this check subscriber.isDisposed() before calling onError or use RxJava 2.1.1's new subscriber.tryOnError.
if (!subscriber.isDisposed()) {
subscriber.onError(exception)
}
If you are using rxjava2, please add this in your initialisation code
RxJavaPlugins.setErrorHandler(t -> {
logger.log(Level.SEVERE,null, t);
});
Hope it helps
Related
Context
I have a webservice writing a test id in a queue. Then, a listener reads the queue, searches the test and starts it. During those steps, it writes updates of the test in the database in order to be shown to the user. To be more precise: the test is launched in a docker container and at the end of it, I want to update the status of the test to FINISHED. For that, I use the docker java library with a callback.
Problem
When it comes to call the callback, I receive multiple error messages on the call to update the test (but it happens only once, if I try twice the second time works, but it still writes a lot of error messages from the transaction manager).
Here are the error messages logged:
2020-11-20 09:20:43,639 WARN [docker-java-stream--1032099154] (org.jboss.jca.core.connectionmanager.listener.TxConnectionListener) IJ000305: Connection error occured: org.jboss.jca.core.connectionmanager.listener.TxConnectionListener#600268e6[state=NORMAL managed connection=org.jboss.jca.adapters.jdbc.local.LocalManagedConnection#236f1a69 connection handles=1 lastReturned=1605860423264 lastValidated=1605860242146 lastCheckedOut=1605860443564 trackByTx=true pool=org.jboss.jca.core.connectionmanager.pool.strategy.OnePool#2efb0d3b mcp=SemaphoreConcurrentLinkedQueueManagedConnectionPool#3e8e7a62[pool=ApplicationDS] xaResource=LocalXAResourceImpl#482fdad2[connectionListener=600268e6 connectionManager=4c83f895 warned=false currentXid=null productName=Oracle productVersion=Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - Production
Version 18.3.0.0.0 jndiName=java:/ApplicationDS] txSync=TransactionSynchronization#1387480544{tx=Local transaction (delegate=TransactionImple < ac, BasicAction: 0:ffffac110002:-50a6b0bf:5fb6bdb9:73db4 status: ActionStatus.ABORTING >, owner=Local transaction context for provider JBoss JTA transaction provider) wasTrackByTx=true enlisted=true cancel=false}]: java.sql.SQLRecoverableException: IO Error: Socket read interrupted
2020-11-20 09:20:43,647 INFO [docker-java-stream--1032099154] (org.jboss.jca.core.connectionmanager.listener.TxConnectionListener) IJ000302: Unregistered handle that was not registered: org.jboss.jca.adapters.jdbc.jdk8.WrappedConnectionJDK8#4f3c1cb2 for managed connection: org.jboss.jca.adapters.jdbc.local.LocalManagedConnection#236f1a69
2020-11-20 09:20:43,656 WARN [docker-java-stream--1032099154] (com.arjuna.ats.jta) ARJUNA016031: XAOnePhaseResource.rollback for < formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffffac110002:-50a6b0bf:5fb6bdb9:73db4, node_name=1, branch_uid=0:ffffac110002:-50a6b0bf:5fb6bdb9:73db8, subordinatenodename=null, eis_name=java:/ApplicationDS > failed with exception: org.jboss.jca.core.spi.transaction.local.LocalXAException: IJ001160: Could not rollback local transaction
Caused by: org.jboss.jca.core.spi.transaction.local.LocalResourceException: IO Error: Socket read interrupted
at org.jboss.ironjacamar.jdbcadapters#1.4.22.Final//org.jboss.jca.adapters.jdbc.local.LocalManagedConnection.rollback(LocalManagedConnection.java:139)
...
Caused by: java.sql.SQLRecoverableException: IO Error: Socket read interrupted
at com.oracle.jdbc//oracle.jdbc.driver.T4CConnection.doRollback(T4CConnection.java:1140)
...
Caused by: java.io.InterruptedIOException: Socket read interrupted
at com.oracle.jdbc//oracle.net.nt.TimeoutSocketChannel.handleInterrupt(TimeoutSocketChannel.java:258)
...
Explanation
At the beginning, I thought about a connection problem, maybe the the transaction is no more available at the callback time (because the docker run took too long), maybe it has to be invalidated.
But at the end, as written in the console, it came from an interruption of the thread when it tries to acquire the lock to update the test and I discovered where this interruption came from: I took a look at the method executeAndStream in DefaultInvocationBuilder from the docker java library and I discovered this:
Thread thread = new Thread(() -> {
Thread streamingThread = Thread.currentThread();
try (DockerHttpClient.Response response = execute(request)) {
callback.onStart(() -> {
streamingThread.interrupt();
response.close();
});
sourceConsumer.accept(response);
callback.onComplete();
} catch (Exception e) {
callback.onError(e);
}
}, "docker-java-stream-" + Objects.hashCode(request));
thread.setDaemon(true);
thread.start();
And here it is, the closable given to onStart interrupts the thread. After that, I discovered in the method onComplete from ResultCallbackTemplate (that I was extending for my callback) a close on that closable:
#Override
public void onComplete() {
try {
close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Resolution
The problem finally came from the following code I wrote:
#Override
public void onComplete() {
super.onComplete();
updateTest(FINISHED);
}
I was calling the onComplete method from the parent without knowing what is does and as usual, first before doing anything else. To correct that, I only had to call the super method at the end:
#Override
public void onComplete() {
updateTest(FINISHED);
super.onComplete();
}
I'm learning Java threads tutorial and can't understand the explanation of last question 'Threads and exceptions':
Now suppose we run this test:
#Test public void testThreadOops() {
new Thread(() -> { throw new Error("thread oops"); }).start();
}
Is a stack trace for Error: thread oops printed? ( ) Yes ( ) No
The test: ( ) Passes ( ) Fails
The explanation for this question is:
The Error occurs on the newly-created thread, terminating that thread with a stack trace on the console. But the test method testThreadOops returns normally – there is no exception on the main thread – and the JUnit test passes. It does not detect the oops.
Why is there no exception in the main thread?
Is a stack trace for Error: thread oops printed?
It depends.
An uncaught exception on a child thread (i.e. not the "main" thread) will be passed to the thread's uncaught exception handler, the threadgroup uncaught exception handler, or the default uncaught exception handler. These would typically be responsible for printing a stacktrace.
If you (or your framework) don't set a handler, the default behavior is to do nothing, and there won't be a stacktace anywhere.
(The methods for setting handlers are described in the javadocs for Thread.)
Why is there no exception in the main thread?
Because the exception is not thrown on the main thread. It is thrown ... and must be caught / handled ... on the child thread's stack.
Think of it this way. If an exception on a child thread was somehow rethrown on the parent thread, where would you catch it? How would you deal with it? What if it was a checked exception, and the context didn't allow that particular exception to be thrown?
There is no exception thrown in the main thread simply because this code doesn't throw an exception in the main thread. It starts a new thread, and throws an exception in that new thread - not in the main thread.
When an exception is thrown (and not caught) in a thread, that thread terminates, but it doesn't "pass" the exception back to the "parent" thread that started it in the first place. If that happened, any thread which started other threads would continuously be at risk of stopping at any time because of an exception thrown in one of its "child" threads. If the "parent" thread were stopped half-way through some computation, it could leave some data in an invalid state.
In the method below you are throwing an exception in the new thread you create not the main thread of the application. So no exception is thrown in the main thread.
new Thread(() -> { throw new Error("thread oops"); }).start();
Normally when an exception occurs in a thread it terminates itself instead of passing the exception in the main thread. This is to protect the main(parent thread) from stopping incase of child thread cause an exception.
This question already has an answer here:
How to handle dispose in RxJava without InterruptedException
(1 answer)
Closed 2 years ago.
I have a simple code below
compositeDisposable.add(Observable.create<Int> { Thread.sleep(1000) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {Log.d("Track", it.localizedMessage)}, {}))
Handler().postDelayed({compositeDisposable.clear()}, 100)
It purposely use Thread.sleep(1000), just to trigger the InterruptedException. I purposely delay 100 milliseconds, so that ensure the sleep in the chain has started, and dispose it.
(Note, I know using of Thread.sleep is not preferred. I'm just writing this code to test and understand why onError is not called on this scenario, and how to prevent the crash elegantly without need to use try-catch in the RxJava chain)
At that time, when it is triggered, the error is not cause onError (i.e. doesn't reach the Log. But instead it throws the below error and crash the App.
io.reactivex.exceptions.UndeliverableException: java.lang.InterruptedException
at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:366)
at io.reactivex.internal.operators.observable.ObservableCreate$CreateEmitter.onError(ObservableCreate.java:74)
at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:43)
at io.reactivex.Observable.subscribe(Observable.java:11194)
at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:463)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.InterruptedException
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:373)
at java.lang.Thread.sleep(Thread.java:314)
at com.elyeproj.porterduff.AnimateDrawPorterDuffView$startAnimate$1.subscribe(AnimateDrawPorterDuffView.kt:45)
at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:40)
at io.reactivex.Observable.subscribe(Observable.java:11194)
at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:463)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
Why was't the InterruptedException caught by the onError in RxJava?
As per pointed by #akarnokd, in https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling, looks like the RxJava has been disposed before the throw, hence the error thrown later got through. To address the issue just need to register
RxJavaPlugins.setErrorHandler { e -> /*Do whatever we need with the error*/ }
While reading jvm specification I came across exception (2.10 Exceptions)
Most exceptions occur synchronously as a result of an action by the
thread in which they occur. An asynchronous exception, by contrast,
can potentially occur at any point in the execution of a program ...
What are the differences between the two types ?
Keeping reading, the specification gives and example of an asynchronous exception
An asynchronous exception occurred because: – The stop method of class
Thread or ThreadGroup was invoked, or – An internal error occurred in
the Java Virtual Machine implementation.
But what makes stop method of a class thread special in this regard ?
Can you explain the differences between the two and give some examples of them, other than the given ones ?
The stop method can be called from another thread which could leave the data altered by the thread stopped in an inconsistent state.
For this reason Thread.stop() is being removed. I suggest you not use it.
Can you explain the differences between the two and give some examples of them, other than the given ones
The difference is that an asynchronous exception is triggered by another thread at any point in the code.
They should not happen under normal operation.
A specific implementation of the JVM could have other errors but there is not exhaustive list.
There isn't much you can do about except shutdown gracefully.
Here's an example showing the two types of exception. Consider the following code snippets, which show the two kinds of exception being raised, and handled:
public static void main(String[] args) throws Exception {
try {
syncException();
} catch (RuntimeException re) {
System.out.println("-1-");
re.printStackTrace();
}
CompletableFuture<Void> f = null;
try {
f = asyncException();
} catch (RuntimeException ex) {
System.out.println("-2-" + Thread.currentThread().getName());
ex.printStackTrace();
}
try {
f.get();
} catch (Exception ex) {
System.out.println("-3-");
ex.printStackTrace();
}
}
A synchronous exception
private static void syncException() {
throw new RuntimeException("Sync exception #" + Thread.currentThread().getName());
}
And an asynchronous exception - raised in a different thread from the calling code:
private static CompletableFuture<Void> asyncException() {
return CompletableFuture.runAsync(() -> {
throw new RuntimeException("Async exception #" + Thread.currentThread().getName());
}, Executors.newFixedThreadPool(1));
}
Now, when that code is executed, the following stack traces are produced:
The synchronous exception's stack trace:
-1-
java.lang.RuntimeException: Sync exception #main
at stackoverflow.Main.syncException(Main.java:34)
at stackoverflow.Main.main(Main.java:11)
Note that -2- was not printed, because the exception was asynchronous. See the third catch block's stack trace for how asynchronous exceptions are handled. Note that the thread in which the exception was raised is different from the one printing the stack trace:
-3-
java.util.concurrent.ExecutionException: java.lang.RuntimeException: Async exception #pool-1-thread-1
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
at stackoverflow.Main.main(Main.java:26)
Caused by: java.lang.RuntimeException: Async exception #pool-1-thread-1
at stackoverflow.Main.lambda$0(Main.java:39)
at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1626)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
An asynchronous exception, by contrast, can potentially occur at any point in the execution of a program ...
Just a comment on this: you'll notice that the code in thread pool-1-thread-1 could raise the exception any time of the main thread's execution. This is probably relative among threads. But in this example, the main thread being the main programe execution, we can say that the "Async exception #pool-1-thread-1" exception occurred asynchronously.
We have a ThreadPoolExecutor-back task execution module. Some tasks are apparently finished in the application codes but the worker code somehow skip all the try/catch/finally and just go pick up the next task, causing the previous one to miss important status reporting code.
Like in this example, 257832:5 was picked up by thread-8 but this thread eventually just start another task.
2012-07-11 15:53:39,389 INFO [pool-1-thread-6]: Task (258861:5) : Starting.
2012-07-11 15:53:39,389 INFO [pool-1-thread-6]: A task running ### logged by app logic
2012-07-11 15:54:18,186 INFO [pool-1-thread-6]: Task (258868:5) : Starting. ### pick up another suddenly!
2012-07-11 15:54:18,186 INFO [pool-1-thread-6]: A task running ### logged by app logic
2012-07-11 15:54:18,445 INFO [pool-1-thread-6]: Task (258868:5) : returned from Task.doWork.
2012-07-11 15:54:18,446 INFO [pool-1-thread-6]: Task (258868:5) : finalizing.
2012-07-11 15:54:18,487 INFO [pool-1-thread-6]: Task (258868:5) : notifying status result: 200.
2012-07-11 15:54:18,487 INFO [pool-1-thread-6]: Task (258868:5) : Finished
The Runnable for the ThreadPoolExecutor) looks fine. It looks like
public void run() {
log.info(String.format("Task (%s:%s) : Starting.", wfId, taskId));
try {
Object result = task.doWork(...); // call the application codes
log.info(String.format("Task (%s:%s) : returned from Task.doWork.", wfId, taskId));
status = "DONE";
} catch (Throwable t) {
log.error("Error executing task: " + taskId + " - " + className);
} finally {
log.info(String.format("Task (%s:%s) : finalizing.", wfId, taskId));
// notify status to a server
log.info(String.format("Task (%s:%s) : Finished", wfId, taskId));
}
}
// the task framework looks like
// Use a queue size larger than numThreads, since the ThreadPoolExecutor#getActiveCount() call only returns an approximate count
this.executor = new ThreadPoolExecutor(numThreads, numThreads, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(numThreads * 2));
executor.execute(new TaskRunnable(...));
Note:
we catch the ultimate Throwable but there is no exception logged between the two tasks
there is no sign that exit() is called or program restart
the first one skipped all the log lines, whether the one right after the call to the app code or the ones in the catch and finally blocks, and skip the status reporting codes
this only happens randomly with low probability; but due to the large number of tasks executed, it still causes us a lot of headache.
It happens as if the thread pool executor simply evicts the running runnable magically (else it would require an InterruptedException which would be caught as a Throwable; Java threads are not to be stopped non-cooperatively except during a shutdown/exit) and skip all the blocks. I checked the ThreeadPoolExecutor javadoc, nothing should cause such event to happen.
What could have happened?
One possible explanation for the log messages from the finally block no appearing is that an exception was thrown while the finally block was executing:
If log was null at that point, you would get an NPE. (If log is declared as final you can eliminate this as a possible cause.)
Depending on what the objects are, you could get an unchecked exception in the wfId or taskId objects' toString() methods.
The Logger object could be broken ...
It is also possible that you are looking at the wrong source code for some reason.
Yet another theoretical possibility is that the Android platform implements Thread.destroy() to actually do something, and something has called it on the thread. If this DEPRECATED method was implemented according to the original javadoc, you'd find that finally blocks were not executed. It might also be possible to do the equivalent of this from outside the JVM. But if this is what is going on, all bets are off!!