UniRx 에셋 추가

This commit is contained in:
Mingu Kim
2025-06-02 00:27:36 +09:00
parent 915f292d7d
commit 8a54d47b56
510 changed files with 42973 additions and 0 deletions

View 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
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 137cd44250b484d4ba2390d510f8423f
timeCreated: 1455373897
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2fa461d2fc0c4ec4999e0b9aff16dd47
timeCreated: 1455373898
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8de419b467eded246bc4fc5e70859f73
timeCreated: 1455373900
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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>
{
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e9dbcd28e4f3965408744e0ee03b7bc8
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d9b0c2f29645e1f468259893bf9afb68
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d5fdc90caca9cbe4b9cd9c3fae81e7f6
timeCreated: 1455373901
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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)
{
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 359bf19588606a14fb0edc6efa97deaf
timeCreated: 1455373898
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: