History

GSF.Core: Added StaticTimer class to the solution.

StephenCWills committed 10 years ago

Commit 58e58201 parent b8314662 files · +174 −0Browse files

Showing the changes to Source/Libraries/GSF.Core/Threading/StaticTimer.cs (1 of 2 files). Show all

Unified · Split

Source/Libraries/GSF.Core/Threading/StaticTimer.cs+173−0
@@ -0,0 +1,173 @@
1+//******************************************************************************************************
2+// StaticTimer.cs - Gbtc
3+//
4+// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.
5+//
6+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may
9+// not use this file except in compliance with the License. You may obtain a copy of the License at:
10+//
11+// http://opensource.org/licenses/MIT
12+//
13+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+// License for the specific language governing permissions and limitations.
16+//
17+// Code Modification History:
18+// ----------------------------------------------------------------------------------------------------
19+// 11/10/2016 - Stephen C. Wills
20+// Generated original version of source code.
21+//
22+//******************************************************************************************************
23+
24+using System;
25+using System.Collections.Concurrent;
26+using System.Collections.Generic;
27+
28+namespace GSF.Threading
29+{
30+ /// <summary>
31+ /// Represents a timer manager which registers callbacks
32+ /// with existing timers to reduce thread pool queue burden.
33+ /// </summary>
34+ public class StaticTimer
35+ {
36+ #region [ Members ]
37+
38+ // Nested Types
39+ private class TimerAction
40+ {
41+ private WeakAction m_action;
42+ private CancellationToken m_cancellationToken;
43+
44+ public TimerAction(Action action, CancellationToken cancellationToken)
45+ {
46+ m_action = new WeakAction(action);
47+ m_cancellationToken = cancellationToken;
48+ }
49+
50+ public bool IsCancelled
51+ {
52+ get
53+ {
54+ return m_cancellationToken.IsCancelled;
55+ }
56+ }
57+
58+ public bool TryInvoke()
59+ {
60+ return m_action.TryInvoke();
61+ }
62+ }
63+
64+ // Fields
65+ private readonly ConcurrentDictionary<int, ConcurrentQueue<TimerAction>> m_dataStreamMonitorActions;
66+
67+ #endregion
68+
69+ #region [ Constructors ]
70+
71+ /// <summary>
72+ /// Creates a new instance of the <see cref="StaticTimer"/> class.
73+ /// </summary>
74+ public StaticTimer()
75+ {
76+ m_dataStreamMonitorActions = new ConcurrentDictionary<int, ConcurrentQueue<TimerAction>>();
77+ }
78+
79+ #endregion
80+
81+ #region [ Methods ]
82+
83+ /// <summary>
84+ /// Registers the given callback with the timer running at the given interval.
85+ /// </summary>
86+ /// <param name="interval">The interval at which to run the timer.</param>
87+ /// <param name="monitorAction">The action to be performed when the timer is triggered.</param>
88+ public ICancellationToken RegisterDataStreamMonitor(int interval, Action monitorAction)
89+ {
90+ CancellationToken cancellationToken = new CancellationToken();
91+ RegisterDataStreamMonitor(interval, new TimerAction(monitorAction, cancellationToken));
92+ return cancellationToken;
93+ }
94+
95+ private void RegisterDataStreamMonitor(int interval, TimerAction monitorAction)
96+ {
97+ ConcurrentQueue<TimerAction> newQueue = null;
98+ ConcurrentQueue<TimerAction> queue;
99+
100+ lock (m_dataStreamMonitorActions)
101+ {
102+ queue = m_dataStreamMonitorActions.GetOrAdd(interval, i => newQueue = new ConcurrentQueue<TimerAction>());
103+ queue.Enqueue(monitorAction);
104+ }
105+
106+ if (queue == newQueue)
107+ StartTimer(interval, queue);
108+ }
109+
110+ private void StartTimer(int interval, ConcurrentQueue<TimerAction> queue)
111+ {
112+ MonitorDataStream(interval, queue, new List<TimerAction>());
113+ }
114+
115+ private void MonitorDataStream(int interval, ConcurrentQueue<TimerAction> queue, List<TimerAction> list)
116+ {
117+ TimerAction action;
118+
119+ for (int i = 0; i < list.Count; i++)
120+ {
121+ action = list[i];
122+
123+ // False here indicates that the
124+ // action has been garbage collected
125+ if (action.IsCancelled || !action.TryInvoke())
126+ {
127+ int end = list.Count - 1;
128+ list[i] = list[end];
129+ list.RemoveAt(end);
130+ i--;
131+ }
132+ }
133+
134+ // The queue is only used to populate the
135+ // list from outside the async loop
136+ while (queue.TryDequeue(out action))
137+ list.Add(action);
138+
139+ if (list.Count > 0)
140+ {
141+ // We still have callbacks in the list so continue the async loop
142+ new Action(() => MonitorDataStream(interval, queue, list)).DelayAndExecute(interval);
143+ }
144+ else
145+ {
146+ lock (m_dataStreamMonitorActions)
147+ {
148+ // We are retiring this async loop so we need
149+ // to remove its queue from the lookup table
150+ m_dataStreamMonitorActions.TryRemove(interval, out queue);
151+
152+ // However, actions may have been queued up in the meantime
153+ // so we need to re-register them with a new async loop
154+ while (queue.TryDequeue(out action))
155+ RegisterDataStreamMonitor(interval, action);
156+ }
157+ }
158+ }
159+
160+ #endregion
161+
162+ #region [ Static ]
163+
164+ // Static Fields
165+
166+ /// <summary>
167+ /// Default instance of the <see cref="StaticTimer"/> class.
168+ /// </summary>
169+ public static readonly StaticTimer Default = new StaticTimer();
170+
171+ #endregion
172+ }
173+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t