Skip to content
This repository has been archived by the owner on Apr 30, 2021. It is now read-only.

MTP-764 DReAM: GlobalClock needs to trigger timers when FastForward() is being called #142

Merged
merged 8 commits into from
Jul 30, 2015
128 changes: 0 additions & 128 deletions src/mindtouch.dream/Tasking/TaskEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -525,134 +525,6 @@ public class TaskEnv : Dictionary<object, object> {
}
}

/// <summary>
/// Invoke a two argument action.
/// </summary>
/// <typeparam name="T1">Type of first argument.</typeparam>
/// <typeparam name="T2">Type of second argument.</typeparam>
/// <param name="handler">Action to invoke.</param>
/// <param name="arg1">First argument.</param>
/// <param name="arg2">Second argument.</param>
public void Invoke<T1, T2>(Action<T1, T2> handler, T1 arg1, T2 arg2) {
if(handler == null) {
throw new ArgumentNullException("handler");
}
if(_referenceCount < 1) {
throw new InvalidOperationException("Cannot call invoke an unaquired TaskEnv");
}
#pragma warning disable 219
System.Diagnostics.StackTrace stacktrace = DebugUtil.GetStackTrace();
#pragma warning restore 219

// check if handler can be invoked in-place or needs to queued up
if(_dispatchQueue != null) {
_dispatchQueue.QueueWorkItem(() => {

// store current thread-specific settings
TaskEnv previousEnv = _currentEnv;
try {

// set thread-specific settings
_currentEnv = this;

// execute handler
handler(arg1, arg2);
} catch(Exception e) {
_log.WarnExceptionMethodCall(e, "Invoke: unhandled exception in handler");
} finally {
Release();

// restore thread-specific settings
_currentEnv = previousEnv;
}
});
} else {

// store current thread-specific settings
TaskEnv previousEnv = _currentEnv;
try {

// set thread-specific settings
_currentEnv = this;

// execute handler
handler(arg1, arg2);
} catch(Exception e) {
_log.WarnExceptionMethodCall(e, "Invoke: unhandled exception in handler");
} finally {
Release();

// restore thread-specific settings
_currentEnv = previousEnv;
}
}
}

/// <summary>
/// Invoke a three argument action.
/// </summary>
/// <typeparam name="T1">Type of first argument.</typeparam>
/// <typeparam name="T2">Type of second argument.</typeparam>
/// <typeparam name="T3">Type of third argument.</typeparam>
/// <param name="handler">Action to invoke.</param>
/// <param name="arg1">First argument.</param>
/// <param name="arg2">Second argument.</param>
/// <param name="arg3">Third argument.</param>
public void Invoke<T1, T2, T3>(Action<T1, T2, T3> handler, T1 arg1, T2 arg2, T3 arg3) {
if(handler == null) {
throw new ArgumentNullException("handler");
}
if(_referenceCount < 1) {
throw new InvalidOperationException("Cannot call invoke an unaquired TaskEnv");
}
#pragma warning disable 219
System.Diagnostics.StackTrace stacktrace = DebugUtil.GetStackTrace();
#pragma warning restore 219

// check if handler can be invoked in-place or needs to queued up
if(_dispatchQueue != null) {
_dispatchQueue.QueueWorkItem(() => {

// store current thread-specific settings
TaskEnv previousEnv = _currentEnv;
try {

// set thread-specific settings
_currentEnv = this;

// execute handler
handler(arg1, arg2, arg3);
} catch(Exception e) {
_log.WarnExceptionMethodCall(e, "Invoke: unhandled exception in handler");
} finally {
Release();

// restore thread-specific settings
_currentEnv = previousEnv;
}
});
} else {

// store current thread-specific settings
TaskEnv previousEnv = _currentEnv;
try {

// set thread-specific settings
_currentEnv = this;

// execute handler
handler(arg1, arg2, arg3);
} catch(Exception e) {
_log.WarnExceptionMethodCall(e, "Invoke: unhandled exception in handler");
} finally {
Release();

// restore thread-specific settings
_currentEnv = previousEnv;
}
}
}

/// <summary>
/// Wrap a method call, delegate or lambda in an environment for later invocation.
/// </summary>
Expand Down
31 changes: 18 additions & 13 deletions src/mindtouch.dream/Tasking/TaskTimerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,11 @@ public class TaskTimerStatistics {
// (or maybe this should just be part of the thread clean-up)
lock(_queue) {
while(_queue.Count > 0) {
TaskTimer timer = _queue.Dequeue();
var timer = _queue.Dequeue();
if(timer.TryLockPending()) {

// retrieve the associated behavior and reset the timer
TaskEnv env = timer.Env;
var env = timer.Env;
timer.Env = null;
timer.SetStatus(TaskTimerStatus.Done);

Expand All @@ -325,15 +325,14 @@ public class TaskTimerStatistics {
// an indefinite time.
// check if any timers were gathered for immediate execution
if(timers != null) {
foreach(KeyValuePair<TaskTimer, TaskEnv> entry in timers) {
foreach(var entry in timers) {
entry.Key.Execute(entry.Value);
}
}

_running = false;
}

private void Tick(DateTime now, TimeSpan elapsed) {
private void Tick(DateTime now, TimeSpan elapsed, bool fastforward) {

// ignore ticks that come in after we've initialized a shutdown
if(_shutdown) {
Expand All @@ -348,13 +347,13 @@ public class TaskTimerStatistics {

// dequeue all timers that are ready to go
while((_queue.Count > 0) && (_queue.Peek().When <= now)) {
TaskTimer timer = _queue.Dequeue();
var timer = _queue.Dequeue();

// check if timer can be transitioned
if(timer.TryLockQueued()) {

// retrieve the associated behavior and reset the timer
TaskEnv env = timer.Env;
var env = timer.Env;
timer.Env = null;
timer.SetStatus(TaskTimerStatus.Done);

Expand All @@ -367,15 +366,15 @@ public class TaskTimerStatistics {
// check if a maintance run is due
if(_maintenance <= now) {
_maintenance = now.AddSeconds(TaskTimer.QUEUE_RESCAN);
DateTime horizon = now.AddSeconds(TaskTimer.QUEUE_CUTOFF);
var horizon = now.AddSeconds(TaskTimer.QUEUE_CUTOFF);
lock(_pending) {
List<TaskTimer> activate = new List<TaskTimer>();
foreach(TaskTimer timer in _pending.Keys) {
var activate = new List<TaskTimer>();
foreach(var timer in _pending.Keys) {
if(timer.When <= horizon) {
activate.Add(timer);
}
}
foreach(TaskTimer timer in activate) {
foreach(var timer in activate) {
_pending.Remove(timer);
if(timer.TryQueuePending()) {
_queue.Enqueue(timer);
Expand All @@ -387,8 +386,14 @@ public class TaskTimerStatistics {

// run schedule on its own thread to avoid re-entrancy issues
if(timers != null) {
foreach(KeyValuePair<TaskTimer, TaskEnv> entry in timers) {
entry.Key.Execute(entry.Value);
if(fastforward) {
foreach(var entry in timers) {
entry.Key.ExecuteNow(entry.Value);
}
} else {
foreach(var entry in timers) {
entry.Key.Execute(entry.Value);
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/mindtouch.dream/Tasking/timer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ public class TaskTimer {
env.Invoke(_handler, this);
}

internal void ExecuteNow(TaskEnv env) {
env.InvokeNow(() => _handler(this));
}

internal void SetStatus(TaskTimerStatus status) {
Interlocked.Exchange(ref _status, (int)status);
}
Expand Down
5 changes: 4 additions & 1 deletion src/mindtouch.dream/Threading/DispatchThreadScheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,10 @@ internal static class DispatchThreadScheduler {
}
}

private static void Tick(DateTime now, TimeSpan elapsed) {
private static void Tick(DateTime now, TimeSpan elapsed, bool fastforward) {

// NOTE (2015-07-25, steveb): 'fastforward' is not used by the DispatchThreadScheduler

lock(_syncRoot) {

// check if resource manager has been idle for a while
Expand Down
1 change: 1 addition & 0 deletions src/mindtouch.dream/mindtouch.dream.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
<Compile Include="dream\RegistrationInspector.cs" />
<Compile Include="Extensions\ReflectionEx.cs" />
<Compile Include="Numeric.cs" />
<Compile Include="system\DisposeCallback.cs" />
<Compile Include="system\GitBranchAttribute.cs" />
<Compile Include="system\GitRevisionAttribute.cs" />
<Compile Include="system\GitUriAttribute.cs" />
Expand Down
29 changes: 29 additions & 0 deletions src/mindtouch.dream/system/Collections/Generic/DictionaryUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,34 @@ public static class DictionaryUtil {
}
return result;
}

public static IEnumerable<TValue> CollectAllValues<TKey, TValue>(this IDictionary<TKey, ICollection<TValue>> dictionary, IEnumerable<TKey> keys, out IEnumerable<TKey> missing) {
var result = new List<TValue>();
var missingResult = new List<TKey>();
foreach(var key in keys) {
ICollection<TValue> value;
if(dictionary.TryGetValue(key, out value)) {
result.AddRange(value);
} else {
missingResult.Add(key);
}
}
missing = missingResult;
return result;
}

public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source, bool overwriteDuplicates = false) {
var result = new Dictionary<TKey, TValue>();
if(overwriteDuplicates) {
foreach(var pair in source) {
result[pair.Key] = pair.Value;
}
} else {
foreach(var pair in source) {
result.Add(pair.Key, pair.Value);
}
}
return result;
}
}
}
50 changes: 50 additions & 0 deletions src/mindtouch.dream/system/DisposeCallback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* MindTouch Dream - a distributed REST framework
* Copyright (C) 2006-2014 MindTouch, Inc.
* www.mindtouch.com oss@mindtouch.com
*
* For community documentation and downloads visit mindtouch.com;
* please review the licensing section.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace System {

/// <summary>
/// Class to trigger a callback when Dispose() is called.
/// </summary>
public class DisposeCallback : IDisposable {

//--- Fields ---
private readonly Action _callback;
private bool _disposed;

//--- Constructors ---
public DisposeCallback(Action callback) {
if(callback == null) {
throw new ArgumentNullException("callback");
}
_callback = callback;
}

//--- Methods ---
public void Dispose() {
if(_disposed) {
throw new ObjectDisposedException("DisposeCallback");
}
_disposed = true;
_callback();
}
}
}
Loading