Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial commit: added files #1

Merged
merged 14 commits into from
Jun 16, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
root = true

# All files
[*]
indent_style = space
indent_size = 4
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

#if !NO_THREAD
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Threading;

namespace System.Reactive.Concurrency
{
//
// WARNING: This code is kept *identically* in two places. One copy is kept in System.Reactive.Core for non-PLIB platforms.
// Another copy is kept in System.Reactive.PlatformServices to enlighten the default lowest common denominator
// behavior of Rx for PLIB when used on a more capable platform.
//
internal class /*Default*/ConcurrencyAbstractionLayerWasmImpl : IConcurrencyAbstractionLayer
{
public IDisposable StartTimer(Action<object> action, object state, TimeSpan dueTime) => new Timer(action, state, Normalize(dueTime));

public IDisposable StartPeriodicTimer(Action action, TimeSpan period)
{
if (period < TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(period));

//
// The contract for periodic scheduling in Rx is that specifying TimeSpan.Zero as the period causes the scheduler to
// call back periodically as fast as possible, sequentially.
//
if (period == TimeSpan.Zero)
{
return new FastPeriodicTimer(action);
}
else
{
return new PeriodicTimer(action, period);
}
}

public IDisposable QueueUserWorkItem(Action<object> action, object state)
{
System.Threading.ThreadPool.QueueUserWorkItem(_ => action(_), state);
return Disposable.Empty;
}

public void Sleep(TimeSpan timeout) => System.Threading.Thread.Sleep(Normalize(timeout));

public IStopwatch StartStopwatch() => new StopwatchImpl();

public bool SupportsLongRunning => false;

public void StartThread(Action<object> action, object state)
{
new Thread(() =>
{
action(state);
})
{ IsBackground = true }.Start();
}

private static TimeSpan Normalize(TimeSpan dueTime) => dueTime < TimeSpan.Zero ? TimeSpan.Zero : dueTime;

//
// Some historical context. In the early days of Rx, we discovered an issue with
// the rooting of timers, causing them to get GC'ed even when the IDisposable of
// a scheduled activity was kept alive. The original code simply created a timer
// as follows:
//
// var t = default(Timer);
// t = new Timer(_ =>
// {
// t = null;
// Debug.WriteLine("Hello!");
// }, null, 5000, Timeout.Infinite);
//
// IIRC the reference to "t" captured by the closure wasn't sufficient on .NET CF
// to keep the timer rooted, causing problems on Windows Phone 7. As a result, we
// added rooting code using a dictionary (SD 7280), which we carried forward all
// the way to Rx v2.0 RTM.
//
// However, the desktop CLR's implementation of System.Threading.Timer exhibits
// other characteristics where a timer can root itself when the timer is still
// reachable through the state or callback parameters. To illustrate this, run
// the following piece of code:
//
// static void Main()
// {
// Bar();
//
// while (true)
// {
// GC.Collect();
// GC.WaitForPendingFinalizers();
// Thread.Sleep(100);
// }
// }
//
// static void Bar()
// {
// var t = default(Timer);
// t = new Timer(_ =>
// {
// t = null; // Comment out this line to see the timer stop
// Console.WriteLine("Hello!");
// }, null, 5000, Timeout.Infinite);
// }
//
// When the closure over "t" is removed, the timer will stop automatically upon
// garbage collection. However, when retaining the reference, this problem does
// not exist. The code below exploits this behavior, avoiding unnecessary costs
// to root timers in a thread-safe manner.
//
// Below is a fragment of SOS output, proving the proper rooting:
//
// !gcroot 02492440
// HandleTable:
// 005a13fc (pinned handle)
// -> 03491010 System.Object[]
// -> 024924dc System.Threading.TimerQueue
// -> 02492450 System.Threading.TimerQueueTimer
// -> 02492420 System.Threading.TimerCallback
// -> 02492414 TimerRootingExperiment.Program+<>c__DisplayClass1
// -> 02492440 System.Threading.Timer
//
// With the USE_TIMER_SELF_ROOT symbol, we shake off this additional rooting code
// for newer platforms where this no longer needed. We checked this on .NET Core
// as well as .NET 4.0, and only #define this symbol for those platforms.
//
// NB: 4/13/2017 - All target platforms for the 4.x release have the self-rooting
// behavior described here, so we removed the USE_TIMER_SELF_ROOT
// symbol.
//

private sealed class Timer : IDisposable
{
private Action<object> _action;
private volatile System.Threading.Timer _timer;

public Timer(Action<object> action, object state, TimeSpan dueTime)
{
_action = action;

// Don't want the spin wait in Tick to get stuck if this thread gets aborted.
try { }
finally
{
//
// Rooting of the timer happens through the this.Tick delegate's target object,
// which is the current instance and has a field to store the Timer instance.
//
_timer = new System.Threading.Timer(Tick, state, dueTime, TimeSpan.FromMilliseconds(System.Threading.Timeout.Infinite));
}
}

private void Tick(object state)
{
try
{
_action(state);
}
finally
{
SpinWait.SpinUntil(IsTimerAssigned);
Dispose();
}
}

private bool IsTimerAssigned() => _timer != null;

public void Dispose()
{
var timer = _timer;
if (timer != TimerStubs.Never)
{
_action = Stubs<object>.Ignore;
_timer = TimerStubs.Never;

timer.Dispose();
}
}
}

private sealed class PeriodicTimer : IDisposable
{
private Action _action;
private volatile System.Threading.Timer _timer;

public PeriodicTimer(Action action, TimeSpan period)
{
_action = action;

//
// Rooting of the timer happens through the this.Tick delegate's target object,
// which is the current instance and has a field to store the Timer instance.
//
_timer = new System.Threading.Timer(Tick, null, period, period);
}

private void Tick(object state) => _action();

public void Dispose()
{
var timer = _timer;
if (timer != null)
{
_action = Stubs.Nop;
_timer = null;

timer.Dispose();
}
}
}

private sealed class FastPeriodicTimer : IDisposable
{
private readonly Action _action;
private volatile bool disposed;

public FastPeriodicTimer(Action action)
{
_action = action;

new System.Threading.Thread(Loop)
{
Name = "Rx-FastPeriodicTimer",
IsBackground = true
}
.Start();
}

private void Loop()
{
while (!disposed)
{
_action();
}
}

public void Dispose()
{
disposed = true;
}
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace System.Reactive.PlatformServices
{
public static class PlatformEnlightenmentProviderExtensions
{
/// <summary>
/// Sets the <see cref="PlatformEnlightenmentProvider.Current"/> to the <see cref="WasmPlatformEnlightenmentProvider"/> one.
/// </summary>
#pragma warning disable IDE0060
public static void EnableWasm(this IPlatformEnlightenmentProvider provider)
#pragma warning restore IDE0060 // Remove unused parameter
{
#pragma warning disable CS0618 // Type or member is obsolete
PlatformEnlightenmentProvider.Current = new WasmPlatformEnlightenmentProvider();
#pragma warning restore CS0618 // Type or member is obsolete
}


}
}
25 changes: 25 additions & 0 deletions Rx.NET/Source/src/System.Reactive/Internal/StopwatchImpl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;

namespace System.Reactive.Concurrency
{
//
// WARNING: This code is kept *identically* in two places. One copy is kept in System.Reactive.Core for non-PLIB platforms.
// Another copy is kept in System.Reactive.PlatformServices to enlighten the default lowest common denominator
// behavior of Rx for PLIB when used on a more capable platform.
//
internal class /*Default*/StopwatchImpl : IStopwatch
{
private readonly Stopwatch _sw;

public StopwatchImpl()
{
_sw = Stopwatch.StartNew();
}

public TimeSpan Elapsed => _sw.Elapsed;
}
}
30 changes: 30 additions & 0 deletions Rx.NET/Source/src/System.Reactive/Internal/Stubs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

namespace System.Reactive
{
internal static class Stubs<T>
{
public static readonly Action<T> Ignore = _ => { };
public static readonly Func<T, T> I = _ => _;
}

internal static class Stubs
{
public static readonly Action Nop = () => { };
//public static readonly Action<Exception> Throw = ex => { ex.Throw(); };
}

#if !NO_THREAD
internal static class TimerStubs
{
#if NETSTANDARD1_3
public static readonly System.Threading.Timer Never = new System.Threading.Timer(_ => { }, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
#else
public static readonly System.Threading.Timer Never = new System.Threading.Timer(_ => { });
#endif
}
#endif
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

//
// WARNING: The full namespace-qualified type name should stay the same for the discovery in System.Reactive.Core to work!
//
using System.ComponentModel;
using System.Diagnostics;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reflection;
using System.Runtime.InteropServices;

namespace System.Reactive.PlatformServices
{
/// <summary>
/// (Infrastructure) Provider for platform-specific framework enlightenments.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public class WasmPlatformEnlightenmentProvider : CurrentPlatformEnlightenmentProvider
{
private readonly static bool _isWasm = RuntimeInformation.IsOSPlatform(OSPlatform.Create("WEBASSEMBLY"));

/// <summary>
/// (Infastructure) Tries to gets the specified service.
/// </summary>
/// <typeparam name="T">Service type.</typeparam>
/// <param name="args">Optional set of arguments.</param>
/// <returns>Service instance or <c>null</c> if not found.</returns>
public override T GetService<T>(object[] args) //where T : class
{
var t = typeof(T);

#if !NO_THREAD || WINDOWS
if (t == typeof(IConcurrencyAbstractionLayer))
{
#if NETSTANDARD2_0
if (_isWasm)
weitzhandler marked this conversation as resolved.
Show resolved Hide resolved
{
return (T)(object)new ConcurrencyAbstractionLayerWasmImpl();
}
#endif
}
#endif

if (t == typeof(IScheduler) && args != null)
{
#if NETSTANDARD2_0
if (_isWasm)
{
return (T)(object)WasmScheduler.Default;
}
#endif
}

return base.GetService<T>(args);
}
}
}
Loading