This repository was archived by the owner on Dec 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 461
Expand file tree
/
Copy pathThreadSynchronizationContext.cs
More file actions
139 lines (128 loc) · 4.22 KB
/
Copy pathThreadSynchronizationContext.cs
File metadata and controls
139 lines (128 loc) · 4.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
using GitHub.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace GitHub.Unity
{
class ThreadSynchronizationContext : SynchronizationContext
{
private readonly CancellationToken token;
private readonly ConcurrentQueue<PostData> queue = new ConcurrentQueue<PostData>();
private readonly ConcurrentQueue<PostData> priorityQueue = new ConcurrentQueue<PostData>();
private readonly JobSignal jobSignal = new JobSignal();
private long jobId;
private readonly Task task;
private int threadId;
public ThreadSynchronizationContext(CancellationToken token)
{
this.token = token;
task = new Task(Start, token, TaskCreationOptions.LongRunning);
task.Start();
}
public override void Post(SendOrPostCallback d, object state)
{
queue.Enqueue(new PostData { Callback = d, State = state });
}
public override void Send(SendOrPostCallback d, object state)
{
if (Thread.CurrentThread.ManagedThreadId == threadId)
{
d(state);
}
else
{
var id = Interlocked.Increment(ref jobId);
priorityQueue.Enqueue(new PostData { Id = id, Callback = d, State = state });
Wait(id);
}
}
private void Wait(long id)
{
jobSignal.Wait(id, token);
}
private void Start()
{
SetSynchronizationContext(this);
threadId = Thread.CurrentThread.ManagedThreadId;
var lastTime = DateTime.Now.Ticks;
var wait = new ManualResetEventSlim(false);
var ticksPerFrame = TimeSpan.TicksPerMillisecond * 10;
var count = 0;
var secondStart = DateTime.Now.Ticks;
while (!token.IsCancellationRequested)
{
var current = DateTime.Now.Ticks;
count++;
if (current - secondStart > TimeSpan.TicksPerMillisecond * 1000)
{
//Console.WriteLine(String.Format("FPS {0}", count));
count = 0;
secondStart = current;
}
Pump();
lastTime = DateTime.Now.Ticks;
long waitTime = (current + ticksPerFrame - lastTime) / TimeSpan.TicksPerMillisecond;
if (waitTime > 0 && waitTime < int.MaxValue)
{
try
{
wait.Wait((int)waitTime, token);
}
catch {}
}
}
}
public void Pump()
{
PostData data;
if (priorityQueue.TryDequeue(out data))
{
data.Run();
}
if (queue.TryDequeue(out data))
{
LogHelper.GetLogger<ThreadSynchronizationContext>().Trace($"Running {data.Id} on main thread");
data.Run();
}
}
struct PostData
{
public long Id;
public SendOrPostCallback Callback;
public object State;
public void Run()
{
Callback(State);
}
}
class JobSignal : ManualResetEventSlim
{
private readonly HashSet<long> signaledIds = new HashSet<long>();
public void Set(long id)
{
try
{
signaledIds.Add(id);
}
catch { } // it's already on the list
Set();
Reset();
}
public bool Wait(long id, CancellationToken token)
{
bool signaled = false;
do
{
signaled = signaledIds.Contains(id);
if (signaled)
break;
Wait(token);
}
while (!token.IsCancellationRequested && !signaled);
return signaled;
}
}
}
}