Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TestSubscriber, concurrent reading and writing of events #5712

Closed
tazle opened this issue Nov 9, 2017 · 6 comments
Closed

TestSubscriber, concurrent reading and writing of events #5712

tazle opened this issue Nov 9, 2017 · 6 comments
Labels

Comments

@tazle
Copy link

tazle commented Nov 9, 2017

Some of our tests need to wait for certain events to arrive to a TestSubscriber to detect test completion. TestSubscriber does not currently allow this, because it's not possible to access the events that it has received in thread-safe manner. What we'd like to be able to do is:

Awaitility.waitAtMost(1, SECONDS).until(() -> assertEquals(expectedEvents, testSubscriber.getOnNextEvents()))

However, this sometimes fails with ConcurrentModificationException on the values list of TestSubscriber, because Awaitility is reading it while some onNext() event is being written to it.

We could do

Awaitility.waitAtMost(1, SECONDS).until(() -> assertEquals(expectedEvents.size(), testSubscriber.getValueCount());
assertEquals(expectedEvents, testSubscriber.,getOnNextEvents());

but that is not safe, since more events may arrive after the expected number of events have all been received (especially in error cases, if extra events are produced), so the comparison may still fail with ConcurrentModificationException.

We currently use version 1.2.0, but this seems to apply at least to the 1.x branch. 2.x has changed to a custom VolatileSizeArrayList, but it's not clear to me how that would in any way solve the remaining race in the second example.

I also wonder why VolatileSizeArrayList was introduced, since the existing separate volatile size variable would seem to provide enough happens-before edges to make the waiting safe (add() -> write to valueCount, read of valueCount -> read of the list). However, my understanding of the memory model is a bit hazy, so please correct me here if I'm wrong.

@akarnokd
Copy link
Member

akarnokd commented Nov 9, 2017

There is the awaitValueCount to make sure you access only as many elements as already committed. Then, use either assertReceivedOnNext or use getOnNextEvents().get(index) to access the backing ArrayList without triggering ConcurrentModificationException.

VolatileSizeArrayList was introduced to safeguard values() that returns a List so what you described should not happen in 2.x.

@akarnokd
Copy link
Member

akarnokd commented Nov 9, 2017

VolatileSizeArrayList was introduced to safeguard values() that returns a List so what you described should not happen in 2.x.

Correction: it only defends the get() method, the iterator() may still fail. Also its services are needed in both TestSubscriber and TestObserver.

@tazle
Copy link
Author

tazle commented Nov 9, 2017

Yes, unfortunately iterator() is used by e.g. toString(), which is how this originally popped up (albeit in the more racy form that compares list contents).

In the VolatileSizeArrayList variant values() could be made safe by returning a subList() that range-checks against the last index available at values() call time. I think that would also work for the 1.x variant, if getOnNextEvents() returned a values.subList(0, valueCount)

@akarnokd
Copy link
Member

akarnokd commented Nov 9, 2017

Or VolatileSizeArrayList could implement a custom Iterator.

Either way, looks like subList is a workaround you could apply externally to values()/getOnNextEvents() before doing assertions; no change to RxJava is needed in this case.

@tazle
Copy link
Author

tazle commented Nov 9, 2017

True. Or create a wrapper for TestSubscriber. No pressing need to touch TestSubscriber implementation in any case, although that would of course make it easier to use directly :)

@akarnokd
Copy link
Member

Closing via #5713.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants