Showing the changes to Source/Libraries/GSF.Core.Shared/Threading/LogicalThread.cs (1 of 4 files). Show all
Unified · Split
| @@ -158,7 +158,8 @@ namespace GSF.Threading · 157 unmodified lines | ||
| 158 | 158 | private LogicalThreadScheduler m_scheduler; |
| 159 | 159 | private ConcurrentQueue<Action>[] m_queues; |
| 160 | 160 | private Dictionary<object, object> m_threadLocalStorage; |
| 161 | − private int m_isActive; | |
| 161 | + private ICancellationToken m_nextExecutionToken; | |
| 162 | + private int m_activePriority; | |
| 162 | 163 | |
| 163 | 164 | private LogicalThreadStatistics m_statistics; |
| 164 | 165 | |
| @@ -170,16 +171,7 @@ namespace GSF.Threading · 5 unmodified lines | ||
| 170 | 171 | /// Creates a new instance of the <see cref="LogicalThread"/> class. |
| 171 | 172 | /// </summary> |
| 172 | 173 | public LogicalThread() |
| 173 | − : this(1) | |
| 174 | − { | |
| 175 | − } | |
| 176 | − | |
| 177 | − /// <summary> | |
| 178 | − /// Creates a new instance of the <see cref="LogicalThread"/> class. | |
| 179 | − /// </summary> | |
| 180 | − /// <param name="priorityLevels">The number of levels of priority supported by this logical thread.</param> | |
| 181 | − public LogicalThread(int priorityLevels) | |
| 182 | − : this(DefaultScheduler, priorityLevels) | |
| 174 | + : this(DefaultScheduler) | |
| 183 | 175 | { |
| 184 | 176 | } |
| 185 | 177 | |
| @@ -187,20 +179,16 @@ namespace GSF.Threading · 1 unmodified line | ||
| 187 | 179 | /// Creates a new instance of the <see cref="LogicalThread"/> class. |
| 188 | 180 | /// </summary> |
| 189 | 181 | /// <param name="scheduler">The <see cref="LogicalThreadScheduler"/> that created this thread.</param> |
| 190 | − /// <param name="priorityLevels">The number of levels of priority supported by this logical thread.</param> | |
| 191 | 182 | /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> is null.</exception> |
| 192 | − /// <exception cref="ArgumentException"><paramref name="priorityLevels"/> is less than or equal to zero.</exception> | |
| 193 | − internal LogicalThread(LogicalThreadScheduler scheduler, int priorityLevels) | |
| 183 | + internal LogicalThread(LogicalThreadScheduler scheduler) | |
| 194 | 184 | { |
| 195 | 185 | if ((object)scheduler == null) |
| 196 | 186 | throw new ArgumentNullException(nameof(scheduler)); |
| 197 | 187 | |
| 198 | − if (priorityLevels <= 0) | |
| 199 | − throw new ArgumentException($"A logical thread must have at least one priority level.", nameof(priorityLevels)); | |
| 200 | − | |
| 201 | 188 | m_scheduler = scheduler; |
| 202 | − m_queues = new ConcurrentQueue<Action>[priorityLevels]; | |
| 189 | + m_queues = new ConcurrentQueue<Action>[PriorityLevels]; | |
| 203 | 190 | m_threadLocalStorage = new Dictionary<object, object>(); |
| 191 | + m_nextExecutionToken = new CancellationToken(); | |
| 204 | 192 | m_statistics = new LogicalThreadStatistics(); |
| 205 | 193 | |
| 206 | 194 | for (int i = 0; i < m_queues.Length; i++) |
| @@ -219,7 +207,7 @@ namespace GSF.Threading · 12 unmodified lines | ||
| 219 | 207 | { |
| 220 | 208 | get |
| 221 | 209 | { |
| 222 | − return m_queues.Length; | |
| 210 | + return m_scheduler.PriorityLevels; | |
| 223 | 211 | } |
| 224 | 212 | } |
| 225 | 213 | |
| @@ -235,6 +223,49 @@ namespace GSF.Threading · 9 unmodified lines | ||
| 235 | 223 | } |
| 236 | 224 | } |
| 237 | 225 | |
| 226 | + /// <summary> | |
| 227 | + /// Gets or sets the priority at which the | |
| 228 | + /// logical thread is queued by the scheduler. | |
| 229 | + /// </summary> | |
| 230 | + internal int ActivePriority | |
| 231 | + { | |
| 232 | + get | |
| 233 | + { | |
| 234 | + return Interlocked.CompareExchange(ref m_activePriority, 0, 0); | |
| 235 | + } | |
| 236 | + set | |
| 237 | + { | |
| 238 | + Interlocked.Exchange(ref m_activePriority, value); | |
| 239 | + } | |
| 240 | + } | |
| 241 | + | |
| 242 | + /// <summary> | |
| 243 | + /// Gets the priority of the next action to be processed on this logical thread. | |
| 244 | + /// </summary> | |
| 245 | + internal int NextPriority | |
| 246 | + { | |
| 247 | + get | |
| 248 | + { | |
| 249 | + return PriorityLevels - m_queues.TakeWhile(queue => queue.IsEmpty).Count(); | |
| 250 | + } | |
| 251 | + } | |
| 252 | + | |
| 253 | + /// <summary> | |
| 254 | + /// Gets or sets the cancellation token for the next time | |
| 255 | + /// the thread's actions will be executed by the scheduler. | |
| 256 | + /// </summary> | |
| 257 | + internal ICancellationToken NextExecutionToken | |
| 258 | + { | |
| 259 | + get | |
| 260 | + { | |
| 261 | + return Interlocked.CompareExchange(ref m_nextExecutionToken, null, null); | |
| 262 | + } | |
| 263 | + set | |
| 264 | + { | |
| 265 | + Interlocked.Exchange(ref m_nextExecutionToken, value); | |
| 266 | + } | |
| 267 | + } | |
| 268 | + | |
| 238 | 269 | #endregion |
| 239 | 270 | |
| 240 | 271 | #region [ Methods ] |
| @@ -260,7 +291,7 @@ namespace GSF.Threading · 19 unmodified lines | ||
| 260 | 291 | throw new ArgumentException($"Priority {priority} is outside the range between 1 and {PriorityLevels}.", nameof(priority)); |
| 261 | 292 | |
| 262 | 293 | m_queues[PriorityLevels - priority].Enqueue(action); |
| 263 | − m_scheduler.SignalItemHandler(this); | |
| 294 | + m_scheduler.SignalItemHandler(this, priority); | |
| 264 | 295 | } |
| 265 | 296 | |
| 266 | 297 | /// <summary> |
| @@ -305,20 +336,28 @@ namespace GSF.Threading · 38 unmodified lines | ||
| 305 | 336 | } |
| 306 | 337 | |
| 307 | 338 | /// <summary> |
| 308 | − /// Activates the thread if it is not already active. | |
| 339 | + /// Attempts to activate the thread at the given priority. | |
| 309 | 340 | /// </summary> |
| 310 | − /// <returns>True if this call to TryActivate caused the thread to be active.</returns> | |
| 311 | − internal bool TryActivate() | |
| 341 | + /// <param name="priority">The priority at which to activate the thread.</param> | |
| 342 | + /// <returns>True if the thread's priority needs to be changed to the given priority; false otherwise.</returns> | |
| 343 | + internal bool TryActivate(int priority) | |
| 312 | 344 | { |
| 313 | − return Interlocked.CompareExchange(ref m_isActive, 1, 0) == 0; | |
| 345 | + // Always get the execution token before the | |
| 346 | + // active priority to mitigate race conditions | |
| 347 | + ICancellationToken nextExecutionToken = NextExecutionToken; | |
| 348 | + int activePriority = ActivePriority; | |
| 349 | + return (activePriority < priority) && nextExecutionToken.Cancel(); | |
| 314 | 350 | } |
| 315 | 351 | |
| 316 | 352 | /// <summary> |
| 317 | − /// Deactivates the thread. | |
| 353 | + /// Completely deactivates the thread, making it available for reactivation at any priority. | |
| 318 | 354 | /// </summary> |
| 319 | 355 | internal void Deactivate() |
| 320 | 356 | { |
| 321 | − Interlocked.Exchange(ref m_isActive, 0); | |
| 357 | + // Always update the active priority before the | |
| 358 | + // execution token to mitigate race conditions | |
| 359 | + ActivePriority = 0; | |
| 360 | + NextExecutionToken = new CancellationToken(); | |
| 322 | 361 | } |
| 323 | 362 | |
| 324 | 363 | /// <summary> |