Terraria v1.4.4.9
Terraria source code documentation
Loading...
Searching...
No Matches
ThreadPoolWorkQueue.cs
Go to the documentation of this file.
5using Internal;
7
8namespace System.Threading;
9
10internal sealed class ThreadPoolWorkQueue
11{
12 internal static class WorkStealingQueueList
13 {
14 private static volatile WorkStealingQueue[] _queues = new WorkStealingQueue[0];
15
16 public static WorkStealingQueue[] Queues => _queues;
17
18 public static void Add(WorkStealingQueue queue)
19 {
20 WorkStealingQueue[] queues;
22 do
23 {
24 queues = _queues;
25 array = new WorkStealingQueue[queues.Length + 1];
26 Array.Copy(queues, array, queues.Length);
27 array[^1] = queue;
28 }
29 while (Interlocked.CompareExchange(ref _queues, array, queues) != queues);
30 }
31
32 public static void Remove(WorkStealingQueue queue)
33 {
34 WorkStealingQueue[] queues;
36 do
37 {
38 queues = _queues;
39 if (queues.Length == 0)
40 {
41 break;
42 }
43 int num = Array.IndexOf(queues, queue);
44 if (num == -1)
45 {
46 break;
47 }
48 array = new WorkStealingQueue[queues.Length - 1];
49 if (num == 0)
50 {
51 Array.Copy(queues, 1, array, 0, array.Length);
52 continue;
53 }
54 if (num == queues.Length - 1)
55 {
56 Array.Copy(queues, array, array.Length);
57 continue;
58 }
59 Array.Copy(queues, array, num);
60 Array.Copy(queues, num + 1, array, num, array.Length - num);
61 }
62 while (Interlocked.CompareExchange(ref _queues, array, queues) != queues);
63 }
64 }
65
66 internal sealed class WorkStealingQueue
67 {
68 internal volatile object[] m_array = new object[32];
69
70 private volatile int m_mask = 31;
71
72 private volatile int m_headIndex;
73
74 private volatile int m_tailIndex;
75
76 private SpinLock m_foreignLock = new SpinLock(enableThreadOwnerTracking: false);
77
79
80 public int Count
81 {
82 get
83 {
84 bool lockTaken = false;
85 try
86 {
87 m_foreignLock.Enter(ref lockTaken);
88 return Math.Max(0, m_tailIndex - m_headIndex);
89 }
90 finally
91 {
92 if (lockTaken)
93 {
94 m_foreignLock.Exit(useMemoryBarrier: false);
95 }
96 }
97 }
98 }
99
100 public void LocalPush(object obj)
101 {
102 int num = m_tailIndex;
103 if (num == int.MaxValue)
104 {
106 }
107 if (num < m_headIndex + m_mask)
108 {
109 Volatile.Write(ref m_array[num & m_mask], obj);
110 m_tailIndex = num + 1;
111 return;
112 }
113 bool lockTaken = false;
114 try
115 {
116 m_foreignLock.Enter(ref lockTaken);
117 int headIndex = m_headIndex;
118 int num2 = m_tailIndex - m_headIndex;
119 if (num2 >= m_mask)
120 {
121 object[] array = new object[m_array.Length << 1];
122 for (int i = 0; i < m_array.Length; i++)
123 {
124 array[i] = m_array[(i + headIndex) & m_mask];
125 }
126 m_array = array;
127 m_headIndex = 0;
128 num = (m_tailIndex = num2);
129 m_mask = (m_mask << 1) | 1;
130 }
131 Volatile.Write(ref m_array[num & m_mask], obj);
132 m_tailIndex = num + 1;
133 }
134 finally
135 {
136 if (lockTaken)
137 {
138 m_foreignLock.Exit(useMemoryBarrier: false);
139 }
140 }
141 }
142
143 [MethodImpl(MethodImplOptions.NoInlining)]
145 {
146 bool lockTaken = false;
147 try
148 {
149 m_foreignLock.Enter(ref lockTaken);
150 int num = m_tailIndex;
151 if (num == int.MaxValue)
152 {
154 num = (m_tailIndex &= m_mask);
155 }
156 return num;
157 }
158 finally
159 {
160 if (lockTaken)
161 {
162 m_foreignLock.Exit(useMemoryBarrier: true);
163 }
164 }
165 }
166
167 public bool LocalFindAndPop(object obj)
168 {
169 if (m_array[(m_tailIndex - 1) & m_mask] == obj)
170 {
171 object obj2 = LocalPop();
172 return obj2 != null;
173 }
174 for (int num = m_tailIndex - 2; num >= m_headIndex; num--)
175 {
176 if (m_array[num & m_mask] == obj)
177 {
178 bool lockTaken = false;
179 try
180 {
181 m_foreignLock.Enter(ref lockTaken);
182 if (m_array[num & m_mask] == null)
183 {
184 return false;
185 }
186 Volatile.Write(ref m_array[num & m_mask], null);
187 if (num == m_tailIndex)
188 {
189 m_tailIndex--;
190 }
191 else if (num == m_headIndex)
192 {
193 m_headIndex++;
194 }
195 return true;
196 }
197 finally
198 {
199 if (lockTaken)
200 {
201 m_foreignLock.Exit(useMemoryBarrier: false);
202 }
203 }
204 }
205 }
206 return false;
207 }
208
209 public object LocalPop()
210 {
212 {
213 return null;
214 }
215 return LocalPopCore();
216 }
217
218 private object LocalPopCore()
219 {
220 int num;
221 object obj;
222 while (true)
223 {
224 int tailIndex = m_tailIndex;
225 if (m_headIndex >= tailIndex)
226 {
227 return null;
228 }
229 tailIndex--;
230 Interlocked.Exchange(ref m_tailIndex, tailIndex);
231 if (m_headIndex <= tailIndex)
232 {
233 num = tailIndex & m_mask;
234 obj = Volatile.Read(ref m_array[num]);
235 if (obj != null)
236 {
237 break;
238 }
239 continue;
240 }
241 bool lockTaken = false;
242 try
243 {
244 m_foreignLock.Enter(ref lockTaken);
245 if (m_headIndex <= tailIndex)
246 {
247 int num2 = tailIndex & m_mask;
248 object obj2 = Volatile.Read(ref m_array[num2]);
249 if (obj2 != null)
250 {
251 m_array[num2] = null;
252 return obj2;
253 }
254 continue;
255 }
256 m_tailIndex = tailIndex + 1;
257 return null;
258 }
259 finally
260 {
261 if (lockTaken)
262 {
263 m_foreignLock.Exit(useMemoryBarrier: false);
264 }
265 }
266 }
267 m_array[num] = null;
268 return obj;
269 }
270
271 public object TrySteal(ref bool missedSteal)
272 {
273 while (CanSteal)
274 {
275 bool lockTaken = false;
276 try
277 {
278 m_foreignLock.TryEnter(ref lockTaken);
279 if (lockTaken)
280 {
281 int headIndex = m_headIndex;
282 Interlocked.Exchange(ref m_headIndex, headIndex + 1);
283 if (headIndex < m_tailIndex)
284 {
285 int num = headIndex & m_mask;
286 object obj = Volatile.Read(ref m_array[num]);
287 if (obj == null)
288 {
289 continue;
290 }
291 m_array[num] = null;
292 return obj;
293 }
294 m_headIndex = headIndex;
295 }
296 }
297 finally
298 {
299 if (lockTaken)
300 {
301 m_foreignLock.Exit(useMemoryBarrier: false);
302 }
303 }
304 missedSteal = true;
305 break;
306 }
307 return null;
308 }
309 }
310
311 private struct CacheLineSeparated
312 {
313 private readonly PaddingFor32 pad1;
314
315 public volatile int numOutstandingThreadRequests;
316
317 private readonly PaddingFor32 pad2;
318 }
319
320 internal bool loggingEnabled;
321
323
324 internal readonly ConcurrentQueue<IThreadPoolWorkItem> timeSensitiveWorkQueue = (ThreadPool.SupportsTimeSensitiveWorkItems ? new ConcurrentQueue<IThreadPoolWorkItem>() : null);
325
327
328 public static long LocalCount
329 {
330 get
331 {
332 long num = 0L;
334 foreach (WorkStealingQueue workStealingQueue in queues)
335 {
336 num += workStealingQueue.Count;
337 }
338 return num;
339 }
340 }
341
342 public long GlobalCount => (ThreadPool.SupportsTimeSensitiveWorkItems ? timeSensitiveWorkQueue.Count : 0) + workItems.Count;
343
345 {
347 }
348
350 {
351 return ThreadPoolWorkQueueThreadLocals.threadLocals ?? CreateThreadLocals();
352 }
353
354 [MethodImpl(MethodImplOptions.NoInlining)]
356 {
357 return ThreadPoolWorkQueueThreadLocals.threadLocals = new ThreadPoolWorkQueueThreadLocals(this);
358 }
359
360 [MethodImpl(MethodImplOptions.AggressiveInlining)]
362 {
363 if (!FrameworkEventSource.Log.IsEnabled())
364 {
365 if (loggingEnabled)
366 {
367 loggingEnabled = false;
368 }
369 }
370 else
371 {
373 }
374 }
375
376 [MethodImpl(MethodImplOptions.NoInlining)]
378 {
380 }
381
382 internal void EnsureThreadRequested()
383 {
385 while (num < Environment.ProcessorCount)
386 {
388 if (num2 == num)
389 {
391 break;
392 }
393 num = num2;
394 }
395 }
396
398 {
400 while (num > 0)
401 {
403 if (num2 != num)
404 {
405 num = num2;
406 continue;
407 }
408 break;
409 }
410 }
411
412 public void EnqueueTimeSensitiveWorkItem(IThreadPoolWorkItem timeSensitiveWorkItem)
413 {
414 if (loggingEnabled && FrameworkEventSource.Log.IsEnabled())
415 {
416 FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(timeSensitiveWorkItem);
417 }
418 timeSensitiveWorkQueue.Enqueue(timeSensitiveWorkItem);
420 }
421
422 [MethodImpl(MethodImplOptions.NoInlining)]
424 {
425 IThreadPoolWorkItem result;
426 bool flag = timeSensitiveWorkQueue.TryDequeue(out result);
427 return result;
428 }
429
430 public void Enqueue(object callback, bool forceGlobal)
431 {
432 if (loggingEnabled && FrameworkEventSource.Log.IsEnabled())
433 {
434 FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject(callback);
435 }
436 ThreadPoolWorkQueueThreadLocals threadPoolWorkQueueThreadLocals = null;
437 if (!forceGlobal)
438 {
439 threadPoolWorkQueueThreadLocals = ThreadPoolWorkQueueThreadLocals.threadLocals;
440 }
441 if (threadPoolWorkQueueThreadLocals != null)
442 {
443 threadPoolWorkQueueThreadLocals.workStealingQueue.LocalPush(callback);
444 }
445 else
446 {
447 workItems.Enqueue(callback);
448 }
450 }
451
452 internal static bool LocalFindAndPop(object callback)
453 {
454 return ThreadPoolWorkQueueThreadLocals.threadLocals?.workStealingQueue.LocalFindAndPop(callback) ?? false;
455 }
456
457 public object Dequeue(ThreadPoolWorkQueueThreadLocals tl, ref bool missedSteal)
458 {
459 WorkStealingQueue workStealingQueue = tl.workStealingQueue;
460 object result;
461 if ((result = workStealingQueue.LocalPop()) == null && !workItems.TryDequeue(out result))
462 {
464 int num = queues.Length;
465 int num2 = num - 1;
466 uint num3 = tl.random.NextUInt32() % (uint)num;
467 while (num > 0)
468 {
469 num3 = ((num3 < num2) ? (num3 + 1) : 0u);
470 WorkStealingQueue workStealingQueue2 = queues[num3];
471 if (workStealingQueue2 != workStealingQueue && workStealingQueue2.CanSteal)
472 {
473 result = workStealingQueue2.TrySteal(ref missedSteal);
474 if (result != null)
475 {
476 return result;
477 }
478 }
479 num--;
480 }
482 {
484 }
485 }
486 return result;
487 }
488
489 internal static bool Dispatch()
490 {
492 s_workQueue.MarkThreadRequestSatisfied();
493 s_workQueue.RefreshLoggingEnabled();
494 bool flag = true;
495 try
496 {
497 ThreadPoolWorkQueue threadPoolWorkQueue = s_workQueue;
498 ThreadPoolWorkQueueThreadLocals orCreateThreadLocals = threadPoolWorkQueue.GetOrCreateThreadLocals();
499 object threadLocalCompletionCountObject = orCreateThreadLocals.threadLocalCompletionCountObject;
500 Thread currentThread = orCreateThreadLocals.currentThread;
501 currentThread._executionContext = null;
502 currentThread._synchronizationContext = null;
503 int num = Environment.TickCount;
504 object obj = null;
505 while (true)
506 {
507 if (obj == null)
508 {
509 bool missedSteal = false;
510 obj = threadPoolWorkQueue.Dequeue(orCreateThreadLocals, ref missedSteal);
511 if (obj == null)
512 {
513 flag = missedSteal;
514 return true;
515 }
516 }
517 if (threadPoolWorkQueue.loggingEnabled && FrameworkEventSource.Log.IsEnabled())
518 {
519 FrameworkEventSource.Log.ThreadPoolDequeueWorkObject(obj);
520 }
521 threadPoolWorkQueue.EnsureThreadRequested();
523 {
525 }
526 else
527 {
528 DispatchWorkItem(obj, currentThread);
529 }
530 obj = null;
532 currentThread.ResetThreadPoolThread();
533 int tickCount = Environment.TickCount;
534 if (!ThreadPool.NotifyWorkItemComplete(threadLocalCompletionCountObject, tickCount))
535 {
536 orCreateThreadLocals.TransferLocalWork();
537 return false;
538 }
539 if ((uint)(tickCount - num) >= 30u)
540 {
542 {
543 break;
544 }
545 num = tickCount;
546 threadPoolWorkQueue.RefreshLoggingEnabled();
547 obj = threadPoolWorkQueue.TryDequeueTimeSensitiveWorkItem();
548 }
549 }
550 return true;
551 }
552 finally
553 {
554 if (flag)
555 {
556 s_workQueue.EnsureThreadRequested();
557 }
558 }
559 }
560
561 [MethodImpl(MethodImplOptions.NoInlining)]
562 private static void DispatchWorkItemWithWorkerTracking(object workItem, Thread currentThread)
563 {
564 bool flag = false;
565 try
566 {
567 ThreadPool.ReportThreadStatus(isWorking: true);
568 flag = true;
569 DispatchWorkItem(workItem, currentThread);
570 }
571 finally
572 {
573 if (flag)
574 {
575 ThreadPool.ReportThreadStatus(isWorking: false);
576 }
577 }
578 }
579
580 [MethodImpl(MethodImplOptions.AggressiveInlining)]
581 private static void DispatchWorkItem(object workItem, Thread currentThread)
582 {
583 if (workItem is Task task)
584 {
585 task.ExecuteFromThreadPool(currentThread);
586 }
587 else
588 {
589 Unsafe.As<IThreadPoolWorkItem>(workItem).Execute();
590 }
591 }
592}
int IList. IndexOf(object value)
Definition Array.cs:1228
static unsafe void Copy(Array sourceArray, Array destinationArray, int length)
Definition Array.cs:624
bool TryDequeue([MaybeNullWhen(false)] out T result)
static int ProcessorCount
static int TickCount
static byte Max(byte val1, byte val2)
Definition Math.cs:738
static void ResetThreadPoolThread(Thread currentThread)
static int CompareExchange(ref int location1, int value, int comparand)
static int Exchange(ref int location1, int value)
readonly ThreadPoolWorkQueue.WorkStealingQueue workStealingQueue
void EnqueueTimeSensitiveWorkItem(IThreadPoolWorkItem timeSensitiveWorkItem)
static void DispatchWorkItem(object workItem, Thread currentThread)
void Enqueue(object callback, bool forceGlobal)
readonly ConcurrentQueue< IThreadPoolWorkItem > timeSensitiveWorkQueue
object Dequeue(ThreadPoolWorkQueueThreadLocals tl, ref bool missedSteal)
static bool LocalFindAndPop(object callback)
readonly ConcurrentQueue< object > workItems
IThreadPoolWorkItem TryDequeueTimeSensitiveWorkItem()
static void DispatchWorkItemWithWorkerTracking(object workItem, Thread currentThread)
ThreadPoolWorkQueueThreadLocals CreateThreadLocals()
ThreadPoolWorkQueueThreadLocals GetOrCreateThreadLocals()
static bool SupportsTimeSensitiveWorkItems
Definition ThreadPool.cs:30
static readonly ThreadPoolWorkQueue s_workQueue
Definition ThreadPool.cs:16
static bool EnableWorkerTracking
Definition ThreadPool.cs:60
static void ReportThreadStatus(bool isWorking)
static bool NotifyWorkItemComplete(object threadLocalCompletionCountObject, int currentTimeMs)
static void RequestWorkerThread()
void ResetThreadPoolThread()
Definition Thread.cs:511
static bool Read(ref bool location)
Definition Volatile.cs:67
static void Write(ref bool location, bool value)
Definition Volatile.cs:74
void TryEnter(ref bool lockTaken)
Definition SpinLock.cs:103
void Enter(ref bool lockTaken)
Definition SpinLock.cs:94