PageRenderTime 31ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/rxjava-core/src/test/java/rx/subjects/ReplaySubjectConcurrencyTest.java

https://gitlab.com/francesmorales/RxJava
Java | 327 lines | 252 code | 47 blank | 28 comment | 23 complexity | 5b148a66bfd47e6d71572b6eb0a63a4d MD5 | raw file
  1. /**
  2. * Copyright 2014 Netflix, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package rx.subjects;
  17. import static org.junit.Assert.*;
  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.List;
  21. import java.util.concurrent.CountDownLatch;
  22. import java.util.concurrent.TimeUnit;
  23. import java.util.concurrent.atomic.AtomicReference;
  24. import org.junit.Test;
  25. import rx.Observable;
  26. import rx.Observable.OnSubscribeFunc;
  27. import rx.Observer;
  28. import rx.Subscriber;
  29. import rx.Subscription;
  30. import rx.functions.Action1;
  31. import rx.subscriptions.Subscriptions;
  32. public class ReplaySubjectConcurrencyTest {
  33. public static void main(String args[]) {
  34. try {
  35. for (int i = 0; i < 100; i++) {
  36. new ReplaySubjectConcurrencyTest().testSubscribeCompletionRaceCondition();
  37. new ReplaySubjectConcurrencyTest().testReplaySubjectConcurrentSubscriptions();
  38. new ReplaySubjectConcurrencyTest().testReplaySubjectConcurrentSubscribersDoingReplayDontBlockEachOther();
  39. }
  40. } catch (InterruptedException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. @Test(timeout = 4000)
  45. public void testReplaySubjectConcurrentSubscribersDoingReplayDontBlockEachOther() throws InterruptedException {
  46. final ReplaySubject<Long> replay = ReplaySubject.create();
  47. Thread source = new Thread(new Runnable() {
  48. @Override
  49. public void run() {
  50. Observable.create(new OnSubscribeFunc<Long>() {
  51. @Override
  52. public Subscription onSubscribe(Observer<? super Long> o) {
  53. System.out.println("********* Start Source Data ***********");
  54. for (long l = 1; l <= 10000; l++) {
  55. o.onNext(l);
  56. }
  57. System.out.println("********* Finished Source Data ***********");
  58. o.onCompleted();
  59. return Subscriptions.empty();
  60. }
  61. }).subscribe(replay);
  62. }
  63. });
  64. source.start();
  65. long v = replay.toBlockingObservable().last();
  66. assertEquals(10000, v);
  67. // it's been played through once so now it will all be replays
  68. final CountDownLatch slowLatch = new CountDownLatch(1);
  69. Thread slowThread = new Thread(new Runnable() {
  70. @Override
  71. public void run() {
  72. Subscriber<Long> slow = new Subscriber<Long>() {
  73. @Override
  74. public void onCompleted() {
  75. System.out.println("*** Slow Observer completed");
  76. slowLatch.countDown();
  77. }
  78. @Override
  79. public void onError(Throwable e) {
  80. }
  81. @Override
  82. public void onNext(Long args) {
  83. if (args == 1) {
  84. System.out.println("*** Slow Observer STARTED");
  85. }
  86. try {
  87. if (args % 10 == 0) {
  88. Thread.sleep(1);
  89. }
  90. } catch (InterruptedException e) {
  91. e.printStackTrace();
  92. }
  93. }
  94. };
  95. replay.subscribe(slow);
  96. try {
  97. slowLatch.await();
  98. } catch (InterruptedException e1) {
  99. e1.printStackTrace();
  100. }
  101. }
  102. });
  103. slowThread.start();
  104. Thread fastThread = new Thread(new Runnable() {
  105. @Override
  106. public void run() {
  107. final CountDownLatch fastLatch = new CountDownLatch(1);
  108. Subscriber<Long> fast = new Subscriber<Long>() {
  109. @Override
  110. public void onCompleted() {
  111. System.out.println("*** Fast Observer completed");
  112. fastLatch.countDown();
  113. }
  114. @Override
  115. public void onError(Throwable e) {
  116. }
  117. @Override
  118. public void onNext(Long args) {
  119. if (args == 1) {
  120. System.out.println("*** Fast Observer STARTED");
  121. }
  122. }
  123. };
  124. replay.subscribe(fast);
  125. try {
  126. fastLatch.await();
  127. } catch (InterruptedException e1) {
  128. e1.printStackTrace();
  129. }
  130. }
  131. });
  132. fastThread.start();
  133. fastThread.join();
  134. // slow should not yet be completed when fast completes
  135. assertEquals(1, slowLatch.getCount());
  136. slowThread.join();
  137. }
  138. @Test
  139. public void testReplaySubjectConcurrentSubscriptions() throws InterruptedException {
  140. final ReplaySubject<Long> replay = ReplaySubject.create();
  141. Thread source = new Thread(new Runnable() {
  142. @Override
  143. public void run() {
  144. Observable.create(new OnSubscribeFunc<Long>() {
  145. @Override
  146. public Subscription onSubscribe(Observer<? super Long> o) {
  147. System.out.println("********* Start Source Data ***********");
  148. for (long l = 1; l <= 10000; l++) {
  149. o.onNext(l);
  150. }
  151. System.out.println("********* Finished Source Data ***********");
  152. o.onCompleted();
  153. return Subscriptions.empty();
  154. }
  155. }).subscribe(replay);
  156. }
  157. });
  158. // used to collect results of each thread
  159. final List<List<Long>> listOfListsOfValues = Collections.synchronizedList(new ArrayList<List<Long>>());
  160. final List<Thread> threads = Collections.synchronizedList(new ArrayList<Thread>());
  161. for (int i = 1; i <= 200; i++) {
  162. final int count = i;
  163. if (count == 20) {
  164. // start source data after we have some already subscribed
  165. // and while others are in process of subscribing
  166. source.start();
  167. }
  168. if (count == 100) {
  169. // wait for source to finish then keep adding after it's done
  170. source.join();
  171. }
  172. Thread t = new Thread(new Runnable() {
  173. @Override
  174. public void run() {
  175. List<Long> values = replay.toList().toBlockingObservable().last();
  176. listOfListsOfValues.add(values);
  177. System.out.println("Finished thread: " + count);
  178. }
  179. });
  180. t.start();
  181. System.out.println("Started thread: " + i);
  182. threads.add(t);
  183. }
  184. // wait for all threads to complete
  185. for (Thread t : threads) {
  186. t.join();
  187. }
  188. // assert all threads got the same results
  189. List<Long> sums = new ArrayList<Long>();
  190. for (List<Long> values : listOfListsOfValues) {
  191. long v = 0;
  192. for (long l : values) {
  193. v += l;
  194. }
  195. sums.add(v);
  196. }
  197. long expected = sums.get(0);
  198. boolean success = true;
  199. for (long l : sums) {
  200. if (l != expected) {
  201. success = false;
  202. System.out.println("FAILURE => Expected " + expected + " but got: " + l);
  203. }
  204. }
  205. if (success) {
  206. System.out.println("Success! " + sums.size() + " each had the same sum of " + expected);
  207. } else {
  208. throw new RuntimeException("Concurrency Bug");
  209. }
  210. }
  211. /**
  212. * Can receive timeout if subscribe never receives an onError/onCompleted ... which reveals a race condition.
  213. */
  214. @Test(timeout = 10000)
  215. public void testSubscribeCompletionRaceCondition() {
  216. for (int i = 0; i < 50; i++) {
  217. final ReplaySubject<String> subject = ReplaySubject.create();
  218. final AtomicReference<String> value1 = new AtomicReference<String>();
  219. subject.subscribe(new Action1<String>() {
  220. @Override
  221. public void call(String t1) {
  222. try {
  223. // simulate a slow observer
  224. Thread.sleep(50);
  225. } catch (InterruptedException e) {
  226. e.printStackTrace();
  227. }
  228. value1.set(t1);
  229. }
  230. });
  231. Thread t1 = new Thread(new Runnable() {
  232. @Override
  233. public void run() {
  234. subject.onNext("value");
  235. subject.onCompleted();
  236. }
  237. });
  238. SubjectObserverThread t2 = new SubjectObserverThread(subject);
  239. SubjectObserverThread t3 = new SubjectObserverThread(subject);
  240. SubjectObserverThread t4 = new SubjectObserverThread(subject);
  241. SubjectObserverThread t5 = new SubjectObserverThread(subject);
  242. t2.start();
  243. t3.start();
  244. t1.start();
  245. t4.start();
  246. t5.start();
  247. try {
  248. t1.join();
  249. t2.join();
  250. t3.join();
  251. t4.join();
  252. t5.join();
  253. } catch (InterruptedException e) {
  254. throw new RuntimeException(e);
  255. }
  256. assertEquals("value", value1.get());
  257. assertEquals("value", t2.value.get());
  258. assertEquals("value", t3.value.get());
  259. assertEquals("value", t4.value.get());
  260. assertEquals("value", t5.value.get());
  261. }
  262. }
  263. private static class SubjectObserverThread extends Thread {
  264. private final ReplaySubject<String> subject;
  265. private final AtomicReference<String> value = new AtomicReference<String>();
  266. public SubjectObserverThread(ReplaySubject<String> subject) {
  267. this.subject = subject;
  268. }
  269. @Override
  270. public void run() {
  271. try {
  272. // a timeout exception will happen if we don't get a terminal state
  273. String v = subject.timeout(2000, TimeUnit.MILLISECONDS).toBlockingObservable().single();
  274. value.set(v);
  275. } catch (Exception e) {
  276. e.printStackTrace();
  277. }
  278. }
  279. }
  280. }