UniRx 에셋 추가
This commit is contained in:
328
Gameton-06/Assets/Plugins/UniRx/Scripts/Subjects/AsyncSubject.cs
Normal file
328
Gameton-06/Assets/Plugins/UniRx/Scripts/Subjects/AsyncSubject.cs
Normal file
@@ -0,0 +1,328 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniRx.InternalUtil;
|
||||
|
||||
#if (NET_4_6 || NET_STANDARD_2_0)
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
#endif
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
public sealed class AsyncSubject<T> : ISubject<T>, IOptimizedObservable<T>, IDisposable
|
||||
#if (NET_4_6 || NET_STANDARD_2_0)
|
||||
, INotifyCompletion
|
||||
#endif
|
||||
{
|
||||
object observerLock = new object();
|
||||
|
||||
T lastValue;
|
||||
bool hasValue;
|
||||
bool isStopped;
|
||||
bool isDisposed;
|
||||
Exception lastError;
|
||||
IObserver<T> outObserver = EmptyObserver<T>.Instance;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (!isStopped) throw new InvalidOperationException("AsyncSubject is not completed yet");
|
||||
if (lastError != null) lastError.Throw();
|
||||
return lastValue;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasObservers
|
||||
{
|
||||
get
|
||||
{
|
||||
return !(outObserver is EmptyObserver<T>) && !isStopped && !isDisposed;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompleted { get { return isStopped; } }
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
IObserver<T> old;
|
||||
T v;
|
||||
bool hv;
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (isStopped) return;
|
||||
|
||||
old = outObserver;
|
||||
outObserver = EmptyObserver<T>.Instance;
|
||||
isStopped = true;
|
||||
v = lastValue;
|
||||
hv = hasValue;
|
||||
}
|
||||
|
||||
if (hv)
|
||||
{
|
||||
old.OnNext(v);
|
||||
old.OnCompleted();
|
||||
}
|
||||
else
|
||||
{
|
||||
old.OnCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
if (error == null) throw new ArgumentNullException("error");
|
||||
|
||||
IObserver<T> old;
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (isStopped) return;
|
||||
|
||||
old = outObserver;
|
||||
outObserver = EmptyObserver<T>.Instance;
|
||||
isStopped = true;
|
||||
lastError = error;
|
||||
}
|
||||
|
||||
old.OnError(error);
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (isStopped) return;
|
||||
|
||||
this.hasValue = true;
|
||||
this.lastValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<T> observer)
|
||||
{
|
||||
if (observer == null) throw new ArgumentNullException("observer");
|
||||
|
||||
var ex = default(Exception);
|
||||
var v = default(T);
|
||||
var hv = false;
|
||||
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (!isStopped)
|
||||
{
|
||||
var listObserver = outObserver as ListObserver<T>;
|
||||
if (listObserver != null)
|
||||
{
|
||||
outObserver = listObserver.Add(observer);
|
||||
}
|
||||
else
|
||||
{
|
||||
var current = outObserver;
|
||||
if (current is EmptyObserver<T>)
|
||||
{
|
||||
outObserver = observer;
|
||||
}
|
||||
else
|
||||
{
|
||||
outObserver = new ListObserver<T>(new ImmutableList<IObserver<T>>(new[] { current, observer }));
|
||||
}
|
||||
}
|
||||
|
||||
return new Subscription(this, observer);
|
||||
}
|
||||
|
||||
ex = lastError;
|
||||
v = lastValue;
|
||||
hv = hasValue;
|
||||
}
|
||||
|
||||
if (ex != null)
|
||||
{
|
||||
observer.OnError(ex);
|
||||
}
|
||||
else if (hv)
|
||||
{
|
||||
observer.OnNext(v);
|
||||
observer.OnCompleted();
|
||||
}
|
||||
else
|
||||
{
|
||||
observer.OnCompleted();
|
||||
}
|
||||
|
||||
return Disposable.Empty;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (observerLock)
|
||||
{
|
||||
isDisposed = true;
|
||||
outObserver = DisposedObserver<T>.Instance;
|
||||
lastError = null;
|
||||
lastValue = default(T);
|
||||
}
|
||||
}
|
||||
|
||||
void ThrowIfDisposed()
|
||||
{
|
||||
if (isDisposed) throw new ObjectDisposedException("");
|
||||
}
|
||||
|
||||
public bool IsRequiredSubscribeOnCurrentThread()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
class Subscription : IDisposable
|
||||
{
|
||||
readonly object gate = new object();
|
||||
AsyncSubject<T> parent;
|
||||
IObserver<T> unsubscribeTarget;
|
||||
|
||||
public Subscription(AsyncSubject<T> parent, IObserver<T> unsubscribeTarget)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.unsubscribeTarget = unsubscribeTarget;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
if (parent != null)
|
||||
{
|
||||
lock (parent.observerLock)
|
||||
{
|
||||
var listObserver = parent.outObserver as ListObserver<T>;
|
||||
if (listObserver != null)
|
||||
{
|
||||
parent.outObserver = listObserver.Remove(unsubscribeTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.outObserver = EmptyObserver<T>.Instance;
|
||||
}
|
||||
|
||||
unsubscribeTarget = null;
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if (NET_4_6 || NET_STANDARD_2_0)
|
||||
|
||||
/// <summary>
|
||||
/// Gets an awaitable object for the current AsyncSubject.
|
||||
/// </summary>
|
||||
/// <returns>Object that can be awaited.</returns>
|
||||
public AsyncSubject<T> GetAwaiter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a callback action that will be invoked when the subject completes.
|
||||
/// </summary>
|
||||
/// <param name="continuation">Callback action that will be invoked when the subject completes.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="continuation"/> is null.</exception>
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
if (continuation == null)
|
||||
throw new ArgumentNullException("continuation");
|
||||
|
||||
OnCompleted(continuation, true);
|
||||
}
|
||||
|
||||
void OnCompleted(Action continuation, bool originalContext)
|
||||
{
|
||||
//
|
||||
// [OK] Use of unsafe Subscribe: this type's Subscribe implementation is safe.
|
||||
//
|
||||
this.Subscribe/*Unsafe*/(new AwaitObserver(continuation, originalContext));
|
||||
}
|
||||
|
||||
class AwaitObserver : IObserver<T>
|
||||
{
|
||||
private readonly SynchronizationContext _context;
|
||||
private readonly Action _callback;
|
||||
|
||||
public AwaitObserver(Action callback, bool originalContext)
|
||||
{
|
||||
if (originalContext)
|
||||
_context = SynchronizationContext.Current;
|
||||
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
InvokeOnOriginalContext();
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
InvokeOnOriginalContext();
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
}
|
||||
|
||||
private void InvokeOnOriginalContext()
|
||||
{
|
||||
if (_context != null)
|
||||
{
|
||||
//
|
||||
// No need for OperationStarted and OperationCompleted calls here;
|
||||
// this code is invoked through await support and will have a way
|
||||
// to observe its start/complete behavior, either through returned
|
||||
// Task objects or the async method builder's interaction with the
|
||||
// SynchronizationContext object.
|
||||
//
|
||||
_context.Post(c => ((Action)c)(), _callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
_callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last element of the subject, potentially blocking until the subject completes successfully or exceptionally.
|
||||
/// </summary>
|
||||
/// <returns>The last element of the subject. Throws an InvalidOperationException if no element was received.</returns>
|
||||
/// <exception cref="InvalidOperationException">The source sequence is empty.</exception>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Await pattern for C# and VB compilers.")]
|
||||
public T GetResult()
|
||||
{
|
||||
if (!isStopped)
|
||||
{
|
||||
var e = new ManualResetEvent(false);
|
||||
OnCompleted(() => e.Set(), false);
|
||||
e.WaitOne();
|
||||
}
|
||||
|
||||
if (lastError != null)
|
||||
{
|
||||
lastError.Throw();
|
||||
}
|
||||
|
||||
if (!hasValue)
|
||||
throw new InvalidOperationException("NO_ELEMENTS");
|
||||
|
||||
return lastValue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 137cd44250b484d4ba2390d510f8423f
|
||||
timeCreated: 1455373897
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using UniRx.InternalUtil;
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
public sealed class BehaviorSubject<T> : ISubject<T>, IDisposable, IOptimizedObservable<T>
|
||||
{
|
||||
object observerLock = new object();
|
||||
|
||||
bool isStopped;
|
||||
bool isDisposed;
|
||||
T lastValue;
|
||||
Exception lastError;
|
||||
IObserver<T> outObserver = EmptyObserver<T>.Instance;
|
||||
|
||||
public BehaviorSubject(T defaultValue)
|
||||
{
|
||||
lastValue = defaultValue;
|
||||
}
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (lastError != null) lastError.Throw();
|
||||
return lastValue;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasObservers
|
||||
{
|
||||
get
|
||||
{
|
||||
return !(outObserver is EmptyObserver<T>) && !isStopped && !isDisposed;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
IObserver<T> old;
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (isStopped) return;
|
||||
|
||||
old = outObserver;
|
||||
outObserver = EmptyObserver<T>.Instance;
|
||||
isStopped = true;
|
||||
}
|
||||
|
||||
old.OnCompleted();
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
if (error == null) throw new ArgumentNullException("error");
|
||||
|
||||
IObserver<T> old;
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (isStopped) return;
|
||||
|
||||
old = outObserver;
|
||||
outObserver = EmptyObserver<T>.Instance;
|
||||
isStopped = true;
|
||||
lastError = error;
|
||||
}
|
||||
|
||||
old.OnError(error);
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
IObserver<T> current;
|
||||
lock (observerLock)
|
||||
{
|
||||
if (isStopped) return;
|
||||
|
||||
lastValue = value;
|
||||
current = outObserver;
|
||||
}
|
||||
|
||||
current.OnNext(value);
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<T> observer)
|
||||
{
|
||||
if (observer == null) throw new ArgumentNullException("observer");
|
||||
|
||||
var ex = default(Exception);
|
||||
var v = default(T);
|
||||
var subscription = default(Subscription);
|
||||
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (!isStopped)
|
||||
{
|
||||
var listObserver = outObserver as ListObserver<T>;
|
||||
if (listObserver != null)
|
||||
{
|
||||
outObserver = listObserver.Add(observer);
|
||||
}
|
||||
else
|
||||
{
|
||||
var current = outObserver;
|
||||
if (current is EmptyObserver<T>)
|
||||
{
|
||||
outObserver = observer;
|
||||
}
|
||||
else
|
||||
{
|
||||
outObserver = new ListObserver<T>(new ImmutableList<IObserver<T>>(new[] { current, observer }));
|
||||
}
|
||||
}
|
||||
|
||||
v = lastValue;
|
||||
subscription = new Subscription(this, observer);
|
||||
}
|
||||
else
|
||||
{
|
||||
ex = lastError;
|
||||
}
|
||||
}
|
||||
|
||||
if (subscription != null)
|
||||
{
|
||||
observer.OnNext(v);
|
||||
return subscription;
|
||||
}
|
||||
else if (ex != null)
|
||||
{
|
||||
observer.OnError(ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
observer.OnCompleted();
|
||||
}
|
||||
|
||||
return Disposable.Empty;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (observerLock)
|
||||
{
|
||||
isDisposed = true;
|
||||
outObserver = DisposedObserver<T>.Instance;
|
||||
lastError = null;
|
||||
lastValue = default(T);
|
||||
}
|
||||
}
|
||||
|
||||
void ThrowIfDisposed()
|
||||
{
|
||||
if (isDisposed) throw new ObjectDisposedException("");
|
||||
}
|
||||
|
||||
public bool IsRequiredSubscribeOnCurrentThread()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
class Subscription : IDisposable
|
||||
{
|
||||
readonly object gate = new object();
|
||||
BehaviorSubject<T> parent;
|
||||
IObserver<T> unsubscribeTarget;
|
||||
|
||||
public Subscription(BehaviorSubject<T> parent, IObserver<T> unsubscribeTarget)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.unsubscribeTarget = unsubscribeTarget;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
if (parent != null)
|
||||
{
|
||||
lock (parent.observerLock)
|
||||
{
|
||||
var listObserver = parent.outObserver as ListObserver<T>;
|
||||
if (listObserver != null)
|
||||
{
|
||||
parent.outObserver = listObserver.Remove(unsubscribeTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.outObserver = EmptyObserver<T>.Instance;
|
||||
}
|
||||
|
||||
unsubscribeTarget = null;
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fa461d2fc0c4ec4999e0b9aff16dd47
|
||||
timeCreated: 1455373898
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
public interface IConnectableObservable<T> : IObservable<T>
|
||||
{
|
||||
IDisposable Connect();
|
||||
}
|
||||
|
||||
public static partial class Observable
|
||||
{
|
||||
class ConnectableObservable<T> : IConnectableObservable<T>
|
||||
{
|
||||
readonly IObservable<T> source;
|
||||
readonly ISubject<T> subject;
|
||||
readonly object gate = new object();
|
||||
Connection connection;
|
||||
|
||||
public ConnectableObservable(IObservable<T> source, ISubject<T> subject)
|
||||
{
|
||||
this.source = source.AsObservable();
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
public IDisposable Connect()
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
// don't subscribe twice
|
||||
if (connection == null)
|
||||
{
|
||||
var subscription = source.Subscribe(subject);
|
||||
connection = new Connection(this, subscription);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<T> observer)
|
||||
{
|
||||
return subject.Subscribe(observer);
|
||||
}
|
||||
|
||||
class Connection : IDisposable
|
||||
{
|
||||
readonly ConnectableObservable<T> parent;
|
||||
IDisposable subscription;
|
||||
|
||||
public Connection(ConnectableObservable<T> parent, IDisposable subscription)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.subscription = subscription;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (parent.gate)
|
||||
{
|
||||
if (subscription != null)
|
||||
{
|
||||
subscription.Dispose();
|
||||
subscription = null;
|
||||
parent.connection = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8de419b467eded246bc4fc5e70859f73
|
||||
timeCreated: 1455373900
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Gameton-06/Assets/Plugins/UniRx/Scripts/Subjects/ISubject.cs
Normal file
14
Gameton-06/Assets/Plugins/UniRx/Scripts/Subjects/ISubject.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
public interface ISubject<TSource, TResult> : IObserver<TSource>, IObservable<TResult>
|
||||
{
|
||||
}
|
||||
|
||||
public interface ISubject<T> : ISubject<T, T>, IObserver<T>, IObservable<T>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9dbcd28e4f3965408744e0ee03b7bc8
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,251 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniRx.InternalUtil;
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
public sealed class ReplaySubject<T> : ISubject<T>, IOptimizedObservable<T>, IDisposable
|
||||
{
|
||||
object observerLock = new object();
|
||||
|
||||
bool isStopped;
|
||||
bool isDisposed;
|
||||
Exception lastError;
|
||||
IObserver<T> outObserver = EmptyObserver<T>.Instance;
|
||||
|
||||
readonly int bufferSize;
|
||||
readonly TimeSpan window;
|
||||
readonly DateTimeOffset startTime;
|
||||
readonly IScheduler scheduler;
|
||||
Queue<TimeInterval<T>> queue = new Queue<TimeInterval<T>>();
|
||||
|
||||
|
||||
public ReplaySubject()
|
||||
: this(int.MaxValue, TimeSpan.MaxValue, Scheduler.DefaultSchedulers.Iteration)
|
||||
{
|
||||
}
|
||||
|
||||
public ReplaySubject(IScheduler scheduler)
|
||||
: this(int.MaxValue, TimeSpan.MaxValue, scheduler)
|
||||
{
|
||||
}
|
||||
|
||||
public ReplaySubject(int bufferSize)
|
||||
: this(bufferSize, TimeSpan.MaxValue, Scheduler.DefaultSchedulers.Iteration)
|
||||
{
|
||||
}
|
||||
|
||||
public ReplaySubject(int bufferSize, IScheduler scheduler)
|
||||
: this(bufferSize, TimeSpan.MaxValue, scheduler)
|
||||
{
|
||||
}
|
||||
|
||||
public ReplaySubject(TimeSpan window)
|
||||
: this(int.MaxValue, window, Scheduler.DefaultSchedulers.Iteration)
|
||||
{
|
||||
}
|
||||
|
||||
public ReplaySubject(TimeSpan window, IScheduler scheduler)
|
||||
: this(int.MaxValue, window, scheduler)
|
||||
{
|
||||
}
|
||||
|
||||
// full constructor
|
||||
public ReplaySubject(int bufferSize, TimeSpan window, IScheduler scheduler)
|
||||
{
|
||||
if (bufferSize < 0) throw new ArgumentOutOfRangeException("bufferSize");
|
||||
if (window < TimeSpan.Zero) throw new ArgumentOutOfRangeException("window");
|
||||
if (scheduler == null) throw new ArgumentNullException("scheduler");
|
||||
|
||||
this.bufferSize = bufferSize;
|
||||
this.window = window;
|
||||
this.scheduler = scheduler;
|
||||
startTime = scheduler.Now;
|
||||
}
|
||||
|
||||
void Trim()
|
||||
{
|
||||
var elapsedTime = Scheduler.Normalize(scheduler.Now - startTime);
|
||||
|
||||
while (queue.Count > bufferSize)
|
||||
{
|
||||
queue.Dequeue();
|
||||
}
|
||||
while (queue.Count > 0 && elapsedTime.Subtract(queue.Peek().Interval).CompareTo(window) > 0)
|
||||
{
|
||||
queue.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
IObserver<T> old;
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (isStopped) return;
|
||||
|
||||
old = outObserver;
|
||||
outObserver = EmptyObserver<T>.Instance;
|
||||
isStopped = true;
|
||||
Trim();
|
||||
}
|
||||
|
||||
old.OnCompleted();
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
if (error == null) throw new ArgumentNullException("error");
|
||||
|
||||
IObserver<T> old;
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (isStopped) return;
|
||||
|
||||
old = outObserver;
|
||||
outObserver = EmptyObserver<T>.Instance;
|
||||
isStopped = true;
|
||||
lastError = error;
|
||||
Trim();
|
||||
}
|
||||
|
||||
old.OnError(error);
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
IObserver<T> current;
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (isStopped) return;
|
||||
|
||||
// enQ
|
||||
queue.Enqueue(new TimeInterval<T>(value, scheduler.Now - startTime));
|
||||
Trim();
|
||||
|
||||
current = outObserver;
|
||||
}
|
||||
|
||||
current.OnNext(value);
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<T> observer)
|
||||
{
|
||||
if (observer == null) throw new ArgumentNullException("observer");
|
||||
|
||||
var ex = default(Exception);
|
||||
var subscription = default(Subscription);
|
||||
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (!isStopped)
|
||||
{
|
||||
var listObserver = outObserver as ListObserver<T>;
|
||||
if (listObserver != null)
|
||||
{
|
||||
outObserver = listObserver.Add(observer);
|
||||
}
|
||||
else
|
||||
{
|
||||
var current = outObserver;
|
||||
if (current is EmptyObserver<T>)
|
||||
{
|
||||
outObserver = observer;
|
||||
}
|
||||
else
|
||||
{
|
||||
outObserver = new ListObserver<T>(new ImmutableList<IObserver<T>>(new[] { current, observer }));
|
||||
}
|
||||
}
|
||||
|
||||
subscription = new Subscription(this, observer);
|
||||
}
|
||||
|
||||
ex = lastError;
|
||||
Trim();
|
||||
foreach (var item in queue)
|
||||
{
|
||||
observer.OnNext(item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (subscription != null)
|
||||
{
|
||||
return subscription;
|
||||
}
|
||||
else if (ex != null)
|
||||
{
|
||||
observer.OnError(ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
observer.OnCompleted();
|
||||
}
|
||||
|
||||
return Disposable.Empty;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (observerLock)
|
||||
{
|
||||
isDisposed = true;
|
||||
outObserver = DisposedObserver<T>.Instance;
|
||||
lastError = null;
|
||||
queue = null;
|
||||
}
|
||||
}
|
||||
|
||||
void ThrowIfDisposed()
|
||||
{
|
||||
if (isDisposed) throw new ObjectDisposedException("");
|
||||
}
|
||||
|
||||
public bool IsRequiredSubscribeOnCurrentThread()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
class Subscription : IDisposable
|
||||
{
|
||||
readonly object gate = new object();
|
||||
ReplaySubject<T> parent;
|
||||
IObserver<T> unsubscribeTarget;
|
||||
|
||||
public Subscription(ReplaySubject<T> parent, IObserver<T> unsubscribeTarget)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.unsubscribeTarget = unsubscribeTarget;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
if (parent != null)
|
||||
{
|
||||
lock (parent.observerLock)
|
||||
{
|
||||
var listObserver = parent.outObserver as ListObserver<T>;
|
||||
if (listObserver != null)
|
||||
{
|
||||
parent.outObserver = listObserver.Remove(unsubscribeTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.outObserver = EmptyObserver<T>.Instance;
|
||||
}
|
||||
|
||||
unsubscribeTarget = null;
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9b0c2f29645e1f468259893bf9afb68
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
169
Gameton-06/Assets/Plugins/UniRx/Scripts/Subjects/Subject.cs
Normal file
169
Gameton-06/Assets/Plugins/UniRx/Scripts/Subjects/Subject.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UniRx.InternalUtil;
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
public sealed class Subject<T> : ISubject<T>, IDisposable, IOptimizedObservable<T>
|
||||
{
|
||||
object observerLock = new object();
|
||||
|
||||
bool isStopped;
|
||||
bool isDisposed;
|
||||
Exception lastError;
|
||||
IObserver<T> outObserver = EmptyObserver<T>.Instance;
|
||||
|
||||
public bool HasObservers
|
||||
{
|
||||
get
|
||||
{
|
||||
return !(outObserver is EmptyObserver<T>) && !isStopped && !isDisposed;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
IObserver<T> old;
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (isStopped) return;
|
||||
|
||||
old = outObserver;
|
||||
outObserver = EmptyObserver<T>.Instance;
|
||||
isStopped = true;
|
||||
}
|
||||
|
||||
old.OnCompleted();
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
if (error == null) throw new ArgumentNullException("error");
|
||||
|
||||
IObserver<T> old;
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (isStopped) return;
|
||||
|
||||
old = outObserver;
|
||||
outObserver = EmptyObserver<T>.Instance;
|
||||
isStopped = true;
|
||||
lastError = error;
|
||||
}
|
||||
|
||||
old.OnError(error);
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
outObserver.OnNext(value);
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<T> observer)
|
||||
{
|
||||
if (observer == null) throw new ArgumentNullException("observer");
|
||||
|
||||
var ex = default(Exception);
|
||||
|
||||
lock (observerLock)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (!isStopped)
|
||||
{
|
||||
var listObserver = outObserver as ListObserver<T>;
|
||||
if (listObserver != null)
|
||||
{
|
||||
outObserver = listObserver.Add(observer);
|
||||
}
|
||||
else
|
||||
{
|
||||
var current = outObserver;
|
||||
if (current is EmptyObserver<T>)
|
||||
{
|
||||
outObserver = observer;
|
||||
}
|
||||
else
|
||||
{
|
||||
outObserver = new ListObserver<T>(new ImmutableList<IObserver<T>>(new[] { current, observer }));
|
||||
}
|
||||
}
|
||||
|
||||
return new Subscription(this, observer);
|
||||
}
|
||||
|
||||
ex = lastError;
|
||||
}
|
||||
|
||||
if (ex != null)
|
||||
{
|
||||
observer.OnError(ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
observer.OnCompleted();
|
||||
}
|
||||
|
||||
return Disposable.Empty;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (observerLock)
|
||||
{
|
||||
isDisposed = true;
|
||||
outObserver = DisposedObserver<T>.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
void ThrowIfDisposed()
|
||||
{
|
||||
if (isDisposed) throw new ObjectDisposedException("");
|
||||
}
|
||||
|
||||
public bool IsRequiredSubscribeOnCurrentThread()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
class Subscription : IDisposable
|
||||
{
|
||||
readonly object gate = new object();
|
||||
Subject<T> parent;
|
||||
IObserver<T> unsubscribeTarget;
|
||||
|
||||
public Subscription(Subject<T> parent, IObserver<T> unsubscribeTarget)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.unsubscribeTarget = unsubscribeTarget;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
if (parent != null)
|
||||
{
|
||||
lock (parent.observerLock)
|
||||
{
|
||||
var listObserver = parent.outObserver as ListObserver<T>;
|
||||
if (listObserver != null)
|
||||
{
|
||||
parent.outObserver = listObserver.Remove(unsubscribeTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.outObserver = EmptyObserver<T>.Instance;
|
||||
}
|
||||
|
||||
unsubscribeTarget = null;
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5fdc90caca9cbe4b9cd9c3fae81e7f6
|
||||
timeCreated: 1455373901
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
|
||||
namespace UniRx
|
||||
{
|
||||
public static class SubjectExtensions
|
||||
{
|
||||
public static ISubject<T> Synchronize<T>(this ISubject<T> subject)
|
||||
{
|
||||
return new AnonymousSubject<T>((subject as IObserver<T>).Synchronize(), subject);
|
||||
}
|
||||
|
||||
public static ISubject<T> Synchronize<T>(this ISubject<T> subject, object gate)
|
||||
{
|
||||
return new AnonymousSubject<T>((subject as IObserver<T>).Synchronize(gate), subject);
|
||||
}
|
||||
|
||||
class AnonymousSubject<T, U> : ISubject<T, U>
|
||||
{
|
||||
readonly IObserver<T> observer;
|
||||
readonly IObservable<U> observable;
|
||||
|
||||
public AnonymousSubject(IObserver<T> observer, IObservable<U> observable)
|
||||
{
|
||||
this.observer = observer;
|
||||
this.observable = observable;
|
||||
}
|
||||
|
||||
public void OnCompleted()
|
||||
{
|
||||
observer.OnCompleted();
|
||||
}
|
||||
|
||||
public void OnError(Exception error)
|
||||
{
|
||||
if (error == null) throw new ArgumentNullException("error");
|
||||
|
||||
observer.OnError(error);
|
||||
}
|
||||
|
||||
public void OnNext(T value)
|
||||
{
|
||||
observer.OnNext(value);
|
||||
}
|
||||
|
||||
public IDisposable Subscribe(IObserver<U> observer)
|
||||
{
|
||||
if (observer == null) throw new ArgumentNullException("observer");
|
||||
|
||||
return observable.Subscribe(observer);
|
||||
}
|
||||
}
|
||||
|
||||
class AnonymousSubject<T> : AnonymousSubject<T, T>, ISubject<T>
|
||||
{
|
||||
public AnonymousSubject(IObserver<T> observer, IObservable<T> observable)
|
||||
: base(observer, observable)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 359bf19588606a14fb0edc6efa97deaf
|
||||
timeCreated: 1455373898
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user