mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-17 18:40:33 +00:00
fix(ThreadedTransport): sleep detection (#3901)
This commit is contained in:
parent
a13a00215f
commit
2b0603f741
@ -5,10 +5,12 @@
|
||||
// note that ThreadLog.cs is required for Debug.Log from threads to work in builds.
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
@ -43,6 +45,9 @@ enum ThreadEventType
|
||||
DoClientSend,
|
||||
DoClientDisconnect,
|
||||
|
||||
Sleep,
|
||||
Wake,
|
||||
|
||||
DoShutdown
|
||||
}
|
||||
|
||||
@ -142,6 +147,10 @@ public abstract class ThreadedTransport : Transport
|
||||
// very large limit to prevent deadlocks.
|
||||
const int MaxProcessingPerTick = 10_000_000;
|
||||
|
||||
[Tooltip("Detect device sleep mode and automatically disconnect + hibernate the thread after 'sleepTimeout' seconds.\nFor example: on mobile / VR, we don't want to drain the battery after putting down the device.")]
|
||||
public bool sleepDetection = true;
|
||||
public float sleepTimeoutInSeconds = 30;
|
||||
|
||||
// communication between main & worker thread //////////////////////////
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void EnqueueClientMain(
|
||||
@ -173,10 +182,19 @@ protected virtual void Awake()
|
||||
{
|
||||
// start the thread.
|
||||
// if main application terminates, this thread needs to terminate too.
|
||||
EnsureThread();
|
||||
}
|
||||
|
||||
// starts the thread if not created or not active yet.
|
||||
void EnsureThread()
|
||||
{
|
||||
if (thread != null && thread.IsAlive) return;
|
||||
|
||||
thread = new WorkerThread(ToString());
|
||||
thread.Tick = ThreadTick;
|
||||
thread.Cleanup = ThreadedShutdown;
|
||||
thread.Start();
|
||||
Debug.Log($"ThreadedTransport: started worker thread!");
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
@ -188,6 +206,8 @@ protected virtual void OnDestroy()
|
||||
}
|
||||
|
||||
// worker thread ///////////////////////////////////////////////////////
|
||||
// sleep timeout to automatically end if the device was put to sleep.
|
||||
Stopwatch sleepTimer = null; // NOT THREAD SAFE: ONLY USE THIS IN WORKER THREAD!
|
||||
void ProcessThreadQueue()
|
||||
{
|
||||
// TODO deadlock protection. worker thread may be to slow to process all.
|
||||
@ -252,6 +272,28 @@ void ProcessThreadQueue()
|
||||
break;
|
||||
}
|
||||
|
||||
// SLEEP ////////////////////////////////////////////////
|
||||
case ThreadEventType.Sleep:
|
||||
{
|
||||
// start the sleep timer if not started yet
|
||||
if (sleepTimer == null)
|
||||
{
|
||||
Debug.Log($"ThreadedTransport: sleep detected, sleeping in {sleepTimeoutInSeconds:F0}s!");
|
||||
sleepTimer = Stopwatch.StartNew();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ThreadEventType.Wake:
|
||||
{
|
||||
// stop the sleep timer (if any)
|
||||
if (sleepTimer != null)
|
||||
{
|
||||
Debug.Log($"ThreadedTransport: Woke up, interrupting sleep timer!");
|
||||
sleepTimer = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// SHUTDOWN ////////////////////////////////////////////////
|
||||
case ThreadEventType.DoShutdown:
|
||||
{
|
||||
@ -267,6 +309,21 @@ void ProcessThreadQueue()
|
||||
// without needing to throw InterruptExceptions or similar.
|
||||
bool ThreadTick()
|
||||
{
|
||||
// was the device put to sleep?
|
||||
if (sleepTimer != null &&
|
||||
sleepTimer.Elapsed.TotalSeconds >= sleepTimeoutInSeconds)
|
||||
{
|
||||
Debug.Log("ThreadedTransport: entering sleep mode and stopping/disconnecting.");
|
||||
ThreadedServerStop();
|
||||
ThreadedClientDisconnect();
|
||||
sleepTimer = null;
|
||||
|
||||
// if the device was put to sleep, end the thread gracefully.
|
||||
// all threads must end, otherwise putting down the device would
|
||||
// slowly drain the battery after a day or more.
|
||||
return false;
|
||||
}
|
||||
|
||||
// early update the implementation first
|
||||
ThreadedClientEarlyUpdate();
|
||||
ThreadedServerEarlyUpdate();
|
||||
@ -279,7 +336,6 @@ bool ThreadTick()
|
||||
ThreadedServerLateUpdate();
|
||||
|
||||
// save some cpu power.
|
||||
// TODO update interval and sleep extra time would be ideal
|
||||
Thread.Sleep(1);
|
||||
return true;
|
||||
}
|
||||
@ -456,6 +512,9 @@ public override void ClientConnect(string address)
|
||||
return;
|
||||
}
|
||||
|
||||
// start worker thread if not started yet
|
||||
EnsureThread();
|
||||
|
||||
// enqueue to process in worker thread
|
||||
EnqueueThread(ThreadEventType.DoClientConnect, address, null, null);
|
||||
|
||||
@ -473,6 +532,9 @@ public override void ClientConnect(Uri uri)
|
||||
return;
|
||||
}
|
||||
|
||||
// start worker thread if not started yet
|
||||
EnsureThread();
|
||||
|
||||
// enqueue to process in worker thread
|
||||
EnqueueThread(ThreadEventType.DoClientConnect, uri, null, null);
|
||||
|
||||
@ -586,6 +648,9 @@ public override void ServerStart()
|
||||
return;
|
||||
}
|
||||
|
||||
// start worker thread if not started yet
|
||||
EnsureThread();
|
||||
|
||||
// enqueue to process in worker thread
|
||||
EnqueueThread(ThreadEventType.DoServerStart, null, null, null);
|
||||
|
||||
@ -631,6 +696,30 @@ public override void ServerStop()
|
||||
serverActive = false;
|
||||
}
|
||||
|
||||
// sleep ///////////////////////////////////////////////////////////////
|
||||
// when a device goes to sleep, we must end the worker thread after a while.
|
||||
// otherwise putting down the device would slowly drain the battery after a day or more.
|
||||
void OnApplicationPause(bool pauseStatus)
|
||||
{
|
||||
Debug.Log($"{GetType()}: OnApplicationPause={pauseStatus}");
|
||||
|
||||
// is sleep detection feature enabled?
|
||||
if (!sleepDetection) return;
|
||||
|
||||
// pause thread if application pauses
|
||||
if (pauseStatus)
|
||||
{
|
||||
// enqueue to process in worker thread
|
||||
EnqueueThread(ThreadEventType.Sleep, null, null, null);
|
||||
}
|
||||
// resume thread if application resumes
|
||||
else
|
||||
{
|
||||
// enqueue to process in worker thread
|
||||
EnqueueThread(ThreadEventType.Wake, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
// shutdown ////////////////////////////////////////////////////////////
|
||||
public override void Shutdown()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user