mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
feature(WorkerThread): Tick() returns bool to allow the thread function to stop the thread gracefully
This commit is contained in:
parent
1e94e12150
commit
927bdccf6e
@ -21,8 +21,10 @@ public class WorkerThread
|
|||||||
// callbacks need to be set after constructor.
|
// callbacks need to be set after constructor.
|
||||||
// inheriting classes can't pass their member funcs to base ctor.
|
// inheriting classes can't pass their member funcs to base ctor.
|
||||||
// don't set them while the thread is running!
|
// don't set them while the thread is running!
|
||||||
|
// -> Tick() returns a bool so it can easily stop the thread
|
||||||
|
// without needing to throw InterruptExceptions or similar.
|
||||||
public Action Init;
|
public Action Init;
|
||||||
public Action Tick;
|
public Func<bool> Tick;
|
||||||
public Action Cleanup;
|
public Action Cleanup;
|
||||||
|
|
||||||
public WorkerThread(string identifier)
|
public WorkerThread(string identifier)
|
||||||
@ -104,7 +106,7 @@ public bool StopBlocking(float timeout)
|
|||||||
// always define them, and make them call actions.
|
// always define them, and make them call actions.
|
||||||
// those can be set at any time.
|
// those can be set at any time.
|
||||||
void OnInit() => Init?.Invoke();
|
void OnInit() => Init?.Invoke();
|
||||||
void OnTick() => Tick?.Invoke();
|
bool OnTick() => Tick?.Invoke() ?? false;
|
||||||
void OnCleanup() => Cleanup?.Invoke();
|
void OnCleanup() => Cleanup?.Invoke();
|
||||||
|
|
||||||
// guarded wrapper for thread code.
|
// guarded wrapper for thread code.
|
||||||
@ -128,7 +130,9 @@ public void Guard(string identifier)
|
|||||||
// run thread func while active
|
// run thread func while active
|
||||||
while (active)
|
while (active)
|
||||||
{
|
{
|
||||||
OnTick();
|
// Tick() returns a bool so it can easily stop the thread
|
||||||
|
// without needing to throw InterruptExceptions or similar.
|
||||||
|
if (!OnTick()) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Thread.Interrupt() will gracefully raise a InterruptedException.
|
// Thread.Interrupt() will gracefully raise a InterruptedException.
|
||||||
|
@ -35,7 +35,7 @@ public void Callbacks()
|
|||||||
int tickCalled = 0;
|
int tickCalled = 0;
|
||||||
int cleanupCalled = 0;
|
int cleanupCalled = 0;
|
||||||
void Init() => Interlocked.Increment(ref initCalled);
|
void Init() => Interlocked.Increment(ref initCalled);
|
||||||
void Tick() => Interlocked.Increment(ref tickCalled);
|
bool Tick() { Interlocked.Increment(ref tickCalled); return true; }
|
||||||
void Cleanup() => Interlocked.Increment(ref cleanupCalled);
|
void Cleanup() => Interlocked.Increment(ref cleanupCalled);
|
||||||
|
|
||||||
// ctor runs thread and calls callback immediately
|
// ctor runs thread and calls callback immediately
|
||||||
@ -82,7 +82,7 @@ public void ExceptionInTick()
|
|||||||
// exceptions in threads are silent by default.
|
// exceptions in threads are silent by default.
|
||||||
// guarantee we try/catch them.
|
// guarantee we try/catch them.
|
||||||
int cleanupCalled = 0;
|
int cleanupCalled = 0;
|
||||||
void Tick() => throw new Exception("Test Exception");
|
bool Tick() => throw new Exception("Test Exception");
|
||||||
void Cleanup() => Interlocked.Increment(ref cleanupCalled);
|
void Cleanup() => Interlocked.Increment(ref cleanupCalled);
|
||||||
|
|
||||||
// ctor runs thread and calls callback immediately
|
// ctor runs thread and calls callback immediately
|
||||||
@ -103,6 +103,7 @@ public void IsAlive()
|
|||||||
{
|
{
|
||||||
thread = new WorkerThread("WorkerThreadTests");
|
thread = new WorkerThread("WorkerThreadTests");
|
||||||
Assert.False(thread.IsAlive);
|
Assert.False(thread.IsAlive);
|
||||||
|
thread.Tick = () => true;
|
||||||
thread.Start();
|
thread.Start();
|
||||||
|
|
||||||
Thread.Sleep(10);
|
Thread.Sleep(10);
|
||||||
@ -111,13 +112,26 @@ public void IsAlive()
|
|||||||
Assert.False(thread.IsAlive);
|
Assert.False(thread.IsAlive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TickIndicatesEnd()
|
||||||
|
{
|
||||||
|
thread = new WorkerThread("WorkerThreadTests");
|
||||||
|
Assert.False(thread.IsAlive);
|
||||||
|
// returning false should automatically stop the thread
|
||||||
|
thread.Tick = () => false;
|
||||||
|
thread.Start();
|
||||||
|
|
||||||
|
Thread.Sleep(10);
|
||||||
|
Assert.False(thread.IsAlive);
|
||||||
|
}
|
||||||
|
|
||||||
// make sure stop returns immediately, but does stop it eventually
|
// make sure stop returns immediately, but does stop it eventually
|
||||||
[Test]
|
[Test]
|
||||||
public void SignalStop()
|
public void SignalStop()
|
||||||
{
|
{
|
||||||
thread = new WorkerThread("WorkerThreadTests");
|
thread = new WorkerThread("WorkerThreadTests");
|
||||||
Assert.False(thread.IsAlive);
|
Assert.False(thread.IsAlive);
|
||||||
thread.Tick = () => Thread.Sleep(50);
|
thread.Tick = () => { Thread.Sleep(50); return true; };
|
||||||
thread.Start();
|
thread.Start();
|
||||||
|
|
||||||
// stop should return immediately, while thread is shutting down
|
// stop should return immediately, while thread is shutting down
|
||||||
@ -135,7 +149,7 @@ public void StopBlocking()
|
|||||||
{
|
{
|
||||||
thread = new WorkerThread("WorkerThreadTests");
|
thread = new WorkerThread("WorkerThreadTests");
|
||||||
Assert.False(thread.IsAlive);
|
Assert.False(thread.IsAlive);
|
||||||
thread.Tick = () => Thread.Sleep(50);
|
thread.Tick = () => { Thread.Sleep(50); return true; };
|
||||||
thread.Start();
|
thread.Start();
|
||||||
|
|
||||||
// stop should wait until fully stopped
|
// stop should wait until fully stopped
|
||||||
@ -149,7 +163,7 @@ public void StopBlocking_Deadlocked()
|
|||||||
{
|
{
|
||||||
thread = new WorkerThread("WorkerThreadTests");
|
thread = new WorkerThread("WorkerThreadTests");
|
||||||
Assert.That(thread.IsAlive, Is.False);
|
Assert.That(thread.IsAlive, Is.False);
|
||||||
thread.Tick = () => Thread.Sleep(5000);
|
thread.Tick = () => { Thread.Sleep(5000); return true; };
|
||||||
thread.Start();
|
thread.Start();
|
||||||
|
|
||||||
// wait for it to start
|
// wait for it to start
|
||||||
|
@ -263,7 +263,9 @@ void ProcessThreadQueue()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadTick()
|
// Tick() returns a bool so it can easily stop the thread
|
||||||
|
// without needing to throw InterruptExceptions or similar.
|
||||||
|
bool ThreadTick()
|
||||||
{
|
{
|
||||||
// early update the implementation first
|
// early update the implementation first
|
||||||
ThreadedClientEarlyUpdate();
|
ThreadedClientEarlyUpdate();
|
||||||
@ -279,6 +281,7 @@ void ThreadTick()
|
|||||||
// save some cpu power.
|
// save some cpu power.
|
||||||
// TODO update interval and sleep extra time would be ideal
|
// TODO update interval and sleep extra time would be ideal
|
||||||
Thread.Sleep(1);
|
Thread.Sleep(1);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// threaded callbacks to call from transport thread.
|
// threaded callbacks to call from transport thread.
|
||||||
|
Loading…
Reference in New Issue
Block a user