-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #962 from benjchristensen/serialize-synchronize
Migrate from SynchronizedObserver to SerializedObserver
- Loading branch information
Showing
22 changed files
with
2,896 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
186 changes: 186 additions & 0 deletions
186
rxjava-core/src/main/java/rx/observers/SerializedObserver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
package rx.observers; | ||
|
||
import java.util.ArrayList; | ||
|
||
import rx.Observer; | ||
|
||
/** | ||
* Enforce single-threaded, serialized, ordered execution of onNext, onCompleted, onError. | ||
* <p> | ||
* When multiple threads are notifying they will be serialized by: | ||
* <p> | ||
* <li>Allowing only one thread at a time to emit</li> | ||
* <li>Adding notifications to a queue if another thread is already emitting</li> | ||
* <li>Not holding any locks or blocking any threads while emitting</li> | ||
* <p> | ||
* | ||
* @param <T> | ||
*/ | ||
public class SerializedObserver<T> implements Observer<T> { | ||
private final Observer<? super T> actual; | ||
|
||
private boolean emitting = false; | ||
private boolean terminated = false; | ||
private ArrayList<Object> queue = new ArrayList<Object>(); | ||
|
||
private static Sentinel NULL_SENTINEL = new Sentinel(); | ||
private static Sentinel COMPLETE_SENTINEL = new Sentinel(); | ||
|
||
private static class Sentinel { | ||
|
||
} | ||
|
||
private static class ErrorSentinel extends Sentinel { | ||
final Throwable e; | ||
|
||
ErrorSentinel(Throwable e) { | ||
this.e = e; | ||
} | ||
} | ||
|
||
public SerializedObserver(Observer<? super T> s) { | ||
this.actual = s; | ||
} | ||
|
||
@Override | ||
public void onCompleted() { | ||
boolean canEmit = false; | ||
ArrayList<Object> list = null; | ||
synchronized (this) { | ||
if (terminated) { | ||
return; | ||
} | ||
terminated = true; | ||
if (!emitting) { | ||
// emit immediately | ||
emitting = true; | ||
canEmit = true; | ||
if (queue.size() > 0) { | ||
list = queue; // copy reference | ||
queue = new ArrayList<Object>(); // new version; | ||
} | ||
} else { | ||
// someone else is already emitting so just queue it | ||
queue.add(COMPLETE_SENTINEL); | ||
} | ||
} | ||
if (canEmit) { | ||
// we won the right to emit | ||
try { | ||
drainQueue(list); | ||
actual.onCompleted(); | ||
} finally { | ||
synchronized (this) { | ||
emitting = false; | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void onError(final Throwable e) { | ||
boolean canEmit = false; | ||
ArrayList<Object> list = null; | ||
synchronized (this) { | ||
if (terminated) { | ||
return; | ||
} | ||
terminated = true; | ||
if (!emitting) { | ||
// emit immediately | ||
emitting = true; | ||
canEmit = true; | ||
if (queue.size() > 0) { | ||
list = queue; // copy reference | ||
queue = new ArrayList<Object>(); // new version; | ||
} | ||
} else { | ||
// someone else is already emitting so just queue it ... after eliminating the queue to shortcut | ||
queue.clear(); | ||
queue.add(new ErrorSentinel(e)); | ||
} | ||
} | ||
if (canEmit) { | ||
// we won the right to emit | ||
try { | ||
drainQueue(list); | ||
actual.onError(e); | ||
} finally { | ||
synchronized (this) { | ||
emitting = false; | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void onNext(T t) { | ||
boolean canEmit = false; | ||
ArrayList<Object> list = null; | ||
synchronized (this) { | ||
if (terminated) { | ||
return; | ||
} | ||
if (!emitting) { | ||
// emit immediately | ||
emitting = true; | ||
canEmit = true; | ||
if (queue.size() > 0) { | ||
list = queue; // copy reference | ||
queue = new ArrayList<Object>(); // new version; | ||
} | ||
} else { | ||
// someone else is already emitting so just queue it | ||
if (t == null) { | ||
queue.add(NULL_SENTINEL); | ||
} else { | ||
queue.add(t); | ||
} | ||
} | ||
} | ||
if (canEmit) { | ||
// we won the right to emit | ||
try { | ||
drainQueue(list); | ||
actual.onNext(t); | ||
} finally { | ||
synchronized (this) { | ||
if (terminated) { | ||
list = queue; // copy reference | ||
queue = new ArrayList<Object>(); // new version; | ||
} else { | ||
// release this thread | ||
emitting = false; | ||
canEmit = false; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// if terminated this will still be true so let's drain the rest of the queue | ||
if (canEmit) { | ||
drainQueue(list); | ||
} | ||
} | ||
|
||
public void drainQueue(ArrayList<Object> list) { | ||
if (list == null || list.size() == 0) { | ||
return; | ||
} | ||
for (Object v : list) { | ||
if (v != null) { | ||
if (v instanceof Sentinel) { | ||
if (v == NULL_SENTINEL) { | ||
actual.onNext(null); | ||
} else if (v == COMPLETE_SENTINEL) { | ||
actual.onCompleted(); | ||
} else if (v instanceof ErrorSentinel) { | ||
actual.onError(((ErrorSentinel) v).e); | ||
} | ||
} else { | ||
actual.onNext((T) v); | ||
} | ||
} | ||
} | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
rxjava-core/src/main/java/rx/observers/SerializedSubscriber.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package rx.observers; | ||
|
||
import rx.Observer; | ||
import rx.Subscriber; | ||
|
||
/** | ||
* Enforce single-threaded, serialized, ordered execution of onNext, onCompleted, onError. | ||
* <p> | ||
* When multiple threads are notifying they will be serialized by: | ||
* <p> | ||
* <li>Allowing only one thread at a time to emit</li> | ||
* <li>Adding notifications to a queue if another thread is already emitting</li> | ||
* <li>Not holding any locks or blocking any threads while emitting</li> | ||
* <p> | ||
* | ||
* @param <T> | ||
*/ | ||
public class SerializedSubscriber<T> extends Subscriber<T> { | ||
|
||
private final Observer<T> s; | ||
|
||
public SerializedSubscriber(Subscriber<? super T> s) { | ||
this.s = new SerializedObserver<T>(s); | ||
} | ||
|
||
@Override | ||
public void onCompleted() { | ||
s.onCompleted(); | ||
} | ||
|
||
@Override | ||
public void onError(Throwable e) { | ||
s.onError(e); | ||
} | ||
|
||
@Override | ||
public void onNext(T t) { | ||
s.onNext(t); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.