The problem is that you have to handle the case where the timeout timer fires but onNext beats it into the synchronized section.  As onNext does not mutate the timeout status, there would be nothing to stop the timeout  thread from onErroring.  The effect of this is you would get an onNext immediately followed by an onError.

The way to mitigate this is to have onNext increment a counter.  As you can't cover two condition checks in a single, atomic compareAndSet you have to fall back to explicit synchronization.

The Rx team use a similar mechanism to handle this case.