Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: rxui5-master
Fetching contributors…

Cannot retrieve contributors at this time

276 lines (250 sloc) 11.61 kB
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
namespace ReactiveUI
{
/*
* N.B. Why we have this evil global class
*
* In a WPF or Silverlight application, most commands must have the Dispatcher
* scheduler set, because notifications will end up being run on another thread;
* this happens most often in a CanExecute observable. Unfortunately, in a Unit
* Test framework, while the MS Test Unit runner will *set* the Dispatcher (so
* we can't even use the lack of its presence to determine whether we're in a
* test runner or not), none of the items queued to it will ever be executed
* during the unit test.
*
* Initially, I tried to plumb the ability to set the scheduler throughout the
* classes, but when you start building applications on top of that, having to
* have *every single* class have a default Scheduler property is really
* irritating, with either default making life difficult.
*
* This class also initializes a whole bunch of other stuff, including the IoC container,
* logging and error handling.
*/
public static class RxApp
{
static RxApp()
{
#if PORTABLE
_TaskpoolScheduler = Scheduler.Default;
#else
_TaskpoolScheduler = TaskPoolScheduler.Default;
#endif
DefaultExceptionHandler = Observer.Create<Exception>(ex => {
// NB: If you're seeing this, it means that an
// ObservableAsPropertyHelper or the CanExecute of a
// ReactiveCommand ended in an OnError. Instead of silently
// breaking, ReactiveUI will halt here if a debugger is attached.
if (Debugger.IsAttached) {
Debugger.Break();
}
RxApp.MainThreadScheduler.Schedule(() => {
throw new Exception(
"An OnError occurred on an object (usually ObservableAsPropertyHelper) that would break a binding or command. To prevent this, Subscribe to the ThrownExceptions property of your objects",
ex);
});
});
var r = new ModernDependencyResolver();
r.InitializeResolver();
_DependencyResolver = r;
if (InUnitTestRunner()) {
LogHost.Default.Warn("*** Detected Unit Test Runner, setting MainThreadScheduler to CurrentThread ***");
LogHost.Default.Warn("If we are not actually in a test runner, please file a bug\n");
_MainThreadScheduler = CurrentThreadScheduler.Instance;
return;
} else {
LogHost.Default.Info("Initializing to normal mode");
}
if (_MainThreadScheduler == null) {
#if !ANDROID
// NB: We can't initialize a scheduler automatically on Android
// because it is intrinsically tied to the current Activity,
// so devs have to set it up by hand :-/
LogHost.Default.Error("*** ReactiveUI Platform DLL reference not added - using Default scheduler *** ");
LogHost.Default.Error("Add a reference to ReactiveUI.{Xaml / Cocoa / etc}.");
LogHost.Default.Error("or consider explicitly setting RxApp.MainThreadScheduler if not");
#endif
_MainThreadScheduler = DefaultScheduler.Instance;
}
}
[ThreadStatic] static IDependencyResolver _UnitTestDependencyResolver;
static IDependencyResolver _DependencyResolver;
/// <summary>
/// Gets or sets the dependency resolver. This class is used throughout
/// ReactiveUI for many internal operations as well as for general use
/// by applications. If this isn't assigned on startup, a default, highly
/// capable implementation will be used, and it is advised for most people
/// to simply use the default implementation.
///
/// Note that to create your own and assign it to the global dependency
/// resolver, you must initialize it via calling InitializeResolver(), or
/// else ReactiveUI internal classes will not be registered and Bad Things™
/// will happen.
/// </summary>
/// <value>The dependency resolver.</value>
public static IDependencyResolver DependencyResolver {
get {
var resolver = _UnitTestDependencyResolver ?? _DependencyResolver;
return _UnitTestDependencyResolver ?? _DependencyResolver;
}
set {
if (InUnitTestRunner()) {
_UnitTestDependencyResolver = value;
_DependencyResolver = _DependencyResolver ?? value;
} else {
_DependencyResolver = value;
}
}
}
/// <summary>
/// Convenience property to return the DependencyResolver cast to a
/// MutableDependencyResolver. The default resolver is also a mutable
/// resolver, so this will be non-null. Use this to register new types
/// on startup if you are using the default resolver
/// </summary>
public static IMutableDependencyResolver MutableResolver {
get { return DependencyResolver as IMutableDependencyResolver; }
set { DependencyResolver = value; }
}
[ThreadStatic] static IScheduler _UnitTestMainThreadScheduler;
static IScheduler _MainThreadScheduler;
/// <summary>
/// MainThreadScheduler is the scheduler used to schedule work items that
/// should be run "on the UI thread". In normal mode, this will be
/// DispatcherScheduler, and in Unit Test mode this will be Immediate,
/// to simplify writing common unit tests.
/// </summary>
public static IScheduler MainThreadScheduler {
get {
var scheduler = _UnitTestMainThreadScheduler ?? _MainThreadScheduler;
return _UnitTestMainThreadScheduler ?? _MainThreadScheduler;
}
set {
// N.B. The ThreadStatic dance here is for the unit test case -
// often, each test will override MainThreadScheduler with their
// own TestScheduler, and if this wasn't ThreadStatic, they would
// stomp on each other, causing test cases to randomly fail,
// then pass when you rerun them.
if (InUnitTestRunner()) {
_UnitTestMainThreadScheduler = value;
_MainThreadScheduler = _MainThreadScheduler ?? value;
} else {
_MainThreadScheduler = value;
}
}
}
[ThreadStatic] static IScheduler _UnitTestTaskpoolScheduler;
static IScheduler _TaskpoolScheduler;
/// <summary>
/// TaskpoolScheduler is the scheduler used to schedule work items to
/// run in a background thread. In both modes, this will run on the TPL
/// Task Pool (or the normal Threadpool on Silverlight).
/// </summary>
public static IScheduler TaskpoolScheduler {
get {
var scheduler = _UnitTestTaskpoolScheduler ?? _TaskpoolScheduler;
return _UnitTestTaskpoolScheduler ?? _TaskpoolScheduler;
}
set {
if (InUnitTestRunner()) {
_UnitTestTaskpoolScheduler = value;
_TaskpoolScheduler = _TaskpoolScheduler ?? value;
} else {
_TaskpoolScheduler = value;
}
}
}
static IObserver<Exception> _DefaultExceptionHandler;
/// <summary>
/// This Observer is signalled whenever an object that has a
/// ThrownExceptions property doesn't Subscribe to that Observable. Use
/// Observer.Create to set up what will happen - the default is to crash
/// the application with an error message.
/// </summary>
public static IObserver<Exception> DefaultExceptionHandler {
get {
return _DefaultExceptionHandler;
}
set {
_DefaultExceptionHandler = value;
}
}
static bool? _InUnitTestRunnerOverride;
static bool? _InUnitTestRunner;
/// <summary>
/// This method allows you to override the return value of
/// RxApp.InUnitTestRunner - a null value means that InUnitTestRunner
/// will determine this using its normal logic.
/// </summary>
public static bool? InUnitTestRunnerOverride
{
get { return _InUnitTestRunnerOverride; }
set {
_InUnitTestRunnerOverride = value;
if(value.HasValue && !value.Value) {
_UnitTestMainThreadScheduler = null;
_UnitTestTaskpoolScheduler = null;
}
}
}
/// <summary>
/// InUnitTestRunner attempts to determine heuristically if the current
/// application is running in a unit test framework
/// </summary>
/// <returns>True if we have determined that a unit test framework is
/// currently running.</returns>
public static bool InUnitTestRunner()
{
if (InUnitTestRunnerOverride.HasValue) {
return InUnitTestRunnerOverride.Value;
}
if (!_InUnitTestRunner.HasValue) {
// NB: This is in a separate static ctor to avoid a deadlock on
// the static ctor lock when blocking on async methods
_InUnitTestRunner = UnitTestDetector.IsInUnitTestRunner() || DesignModeDetector.IsInDesignMode();
}
return _InUnitTestRunner.Value;
}
/// <summary>
/// This method will initialize your custom service locator with the
/// built-in RxUI types. Use this to help initialize containers that
/// don't conform easily to IMutableDependencyResolver.
/// </summary>
/// <param name="registerMethod">Create a method here that will
/// register a constant. For example, the NInject version of
/// this method might look like:
///
/// (obj, type) => kernel.Bind(type).ToConstant(obj)
/// </param>
public static void InitializeCustomResolver(Action<object, Type> registerMethod)
{
var fakeResolver = new FuncDependencyResolver(null,
(fac, type, str) => registerMethod(fac(), type));
fakeResolver.InitializeResolver();
}
static internal bool suppressLogging { get; set; }
#if ANDROID || SILVERLIGHT || IOS
public const int SmallCacheLimit = 32;
public const int BigCacheLimit = 64;
#else
public const int SmallCacheLimit = 64;
public const int BigCacheLimit = 256;
#endif
}
}
// vim: tw=120 ts=4 sw=4 et :
Jump to Line
Something went wrong with that request. Please try again.