init project
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
using UnityEditor;
|
||||
|
||||
using Codice.LogWrapper;
|
||||
using PlasticGui;
|
||||
using PlasticPipe.Client;
|
||||
using Unity.PlasticSCM.Editor.UI;
|
||||
|
||||
namespace Unity.PlasticSCM.Editor
|
||||
{
|
||||
internal static class WaitForPendingOperations
|
||||
{
|
||||
internal static void ClearProgressBarAndEnterPlayModeIfNeeded()
|
||||
{
|
||||
// If play mode is pending (we were waiting for operations when domain reload hit),
|
||||
// clear the progress bar and resume entering play mode.
|
||||
if (!SessionState.GetBool(PLAY_MODE_PENDING_KEY, false))
|
||||
return;
|
||||
|
||||
mLog.Debug("Clear progress bar and enter play mode after domain reload");
|
||||
|
||||
// Wait for editor to be ready to clear the progress bar and enter play mode
|
||||
Execute.WhenEditorIsReady(ClearProgressBarAndEnterPlayMode);
|
||||
}
|
||||
|
||||
internal static void BeforeAssemblyReload()
|
||||
{
|
||||
mLog.Debug("BeforeAssemblyReload started");
|
||||
|
||||
CancelPlayModeWaitIfNeeded();
|
||||
|
||||
// If we already timed out waiting for play mode, skip the wait loop entirely,
|
||||
// to avoid waiting and showing a progress bar again.
|
||||
if (mPlayModeWaitTimeoutReached)
|
||||
{
|
||||
mPlayModeWaitTimeoutReached = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mLastLoggedSecond = -1;
|
||||
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
bool progressBarShown = false;
|
||||
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Tick manually the EditorDispatcher to allow UVCS operations
|
||||
// to report completion in the main thread through the afterOperationDelegate.
|
||||
EditorDispatcher.Update();
|
||||
|
||||
if (!HasRunningOperations())
|
||||
{
|
||||
mLog.DebugFormat(
|
||||
"All operations finished after {0}ms",
|
||||
stopwatch.ElapsedMilliseconds);
|
||||
break;
|
||||
}
|
||||
|
||||
bool hasLongRunningOperations = HasLongRunningOperations();
|
||||
|
||||
// Always wait for UVCS operations without timeout.
|
||||
// Only apply timeout to ThreadWaiters and InUseConnections.
|
||||
bool timeoutReached = stopwatch.ElapsedMilliseconds >= MAX_WAIT_TIMEOUT_MS;
|
||||
if (timeoutReached && !hasLongRunningOperations)
|
||||
{
|
||||
LogTimeoutReached(stopwatch.ElapsedMilliseconds, "Forcing shutdown.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Show progress bar after the delay to avoid flicker for quick operations
|
||||
if (!progressBarShown && stopwatch.ElapsedMilliseconds >= PROGRESS_BAR_DELAY_MS)
|
||||
{
|
||||
DisplayProgressBar();
|
||||
progressBarShown = true;
|
||||
}
|
||||
|
||||
LogWaitingForOperations(stopwatch.ElapsedMilliseconds);
|
||||
|
||||
Thread.Sleep(POLL_INTERVAL_MS);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (progressBarShown)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
mLog.DebugFormat(
|
||||
"WaitForPendingOperations completed in {0}ms",
|
||||
stopwatch.ElapsedMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void OnPlayModeStateChanged(PlayModeStateChange change)
|
||||
{
|
||||
if (change != PlayModeStateChange.ExitingEditMode)
|
||||
return;
|
||||
|
||||
bool isDomainReloadDisabled = EditorSettings.enterPlayModeOptionsEnabled &&
|
||||
(EditorSettings.enterPlayModeOptions &
|
||||
EnterPlayModeOptions.DisableDomainReload) != 0;
|
||||
|
||||
if (isDomainReloadDisabled)
|
||||
return;
|
||||
|
||||
if (!HasRunningOperations())
|
||||
return;
|
||||
|
||||
// If we already timed out, don't delay play mode again to avoid infinite loop.
|
||||
// This happens when FinishWaitAndEnterPlayMode() calls EnterPlaymode() after timeout,
|
||||
// which triggers this callback again while operations are still running.
|
||||
// Note: mPlayModeWaitTimeoutReached is reset in BeforeAssemblyReload().
|
||||
if (mPlayModeWaitTimeoutReached)
|
||||
return;
|
||||
|
||||
mLog.Debug("OnPlayModeStateChanged delaying play mode");
|
||||
|
||||
// Cancel entering play mode to wait for operations to finish
|
||||
EditorApplication.ExitPlaymode();
|
||||
|
||||
// Prevent reentrancy: the ProgressBar doesn't lock the UI.
|
||||
if (mPlayModeWaitStopwatch != null)
|
||||
return;
|
||||
|
||||
// Mark play mode as pending. Cleared on success, or used to resume after reload.
|
||||
SessionState.SetBool(PLAY_MODE_PENDING_KEY, true);
|
||||
|
||||
mLastLoggedSecond = -1;
|
||||
mPlayModeWaitStopwatch = Stopwatch.StartNew();
|
||||
EditorApplication.update += EnterPlayModeWhenOperationsFinish;
|
||||
}
|
||||
|
||||
static void EnterPlayModeWhenOperationsFinish()
|
||||
{
|
||||
if (mPlayModeWaitStopwatch == null)
|
||||
return;
|
||||
|
||||
// Show the progress bar on every frame to ensure it stays displayed regardless of any
|
||||
// interruptions (like a popup on Editor Wants To Quit, or taking a screenshot!)
|
||||
DisplayProgressBar();
|
||||
|
||||
bool hasLongRunningOperations = HasLongRunningOperations();
|
||||
|
||||
// Always wait for UVCS operations without timeout.
|
||||
// Only apply timeout to ThreadWaiters and InUseConnections.
|
||||
mPlayModeWaitTimeoutReached = mPlayModeWaitStopwatch.ElapsedMilliseconds >= MAX_WAIT_TIMEOUT_MS;
|
||||
if (mPlayModeWaitTimeoutReached && !hasLongRunningOperations)
|
||||
{
|
||||
LogTimeoutReached(
|
||||
mPlayModeWaitStopwatch.ElapsedMilliseconds,
|
||||
"Forcing play mode.");
|
||||
FinishWaitAndEnterPlayMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasRunningOperations())
|
||||
{
|
||||
LogWaitingForOperations(mPlayModeWaitStopwatch.ElapsedMilliseconds);
|
||||
return;
|
||||
}
|
||||
|
||||
FinishWaitAndEnterPlayMode();
|
||||
}
|
||||
|
||||
static void FinishWaitAndEnterPlayMode()
|
||||
{
|
||||
mLog.Debug("FinishWaitAndEnterPlayMode");
|
||||
|
||||
EditorApplication.update -= EnterPlayModeWhenOperationsFinish;
|
||||
mPlayModeWaitStopwatch = null;
|
||||
|
||||
ClearProgressBarAndEnterPlayMode();
|
||||
}
|
||||
|
||||
static void ClearProgressBarAndEnterPlayMode()
|
||||
{
|
||||
ClearProgressBar();
|
||||
|
||||
mLog.Debug("Enter play mode");
|
||||
EditorApplication.EnterPlaymode();
|
||||
|
||||
SessionState.SetBool(PLAY_MODE_PENDING_KEY, false);
|
||||
}
|
||||
|
||||
static void CancelPlayModeWaitIfNeeded()
|
||||
{
|
||||
if (mPlayModeWaitStopwatch == null)
|
||||
return;
|
||||
|
||||
mLog.Debug("CancelPlayModeWait");
|
||||
|
||||
EditorApplication.update -= EnterPlayModeWhenOperationsFinish;
|
||||
mPlayModeWaitStopwatch = null;
|
||||
}
|
||||
|
||||
static bool HasRunningOperations()
|
||||
{
|
||||
return HasLowLevelRunningOperations() || HasLongRunningOperations();
|
||||
}
|
||||
|
||||
static bool HasLowLevelRunningOperations()
|
||||
{
|
||||
return ThreadWaiterRegistry.HasRunningOperations() ||
|
||||
ClientConnectionPool.HasInUseConnections();
|
||||
}
|
||||
|
||||
static bool HasLongRunningOperations()
|
||||
{
|
||||
return UVCSPlugin.Instance.HasRunningOperation();
|
||||
}
|
||||
|
||||
static void DisplayProgressBar()
|
||||
{
|
||||
EditorUtility.DisplayProgressBar(
|
||||
UnityConstants.UVCS_WINDOW_TITLE,
|
||||
PlasticLocalization.Name.WaitingForPendingOperationsToFinish.GetString(),
|
||||
1f);
|
||||
}
|
||||
|
||||
static void ClearProgressBar()
|
||||
{
|
||||
mLog.Debug("ClearProgressBar");
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
static void LogTimeoutReached(long elapsedMs, string action)
|
||||
{
|
||||
mLog.WarnFormat(
|
||||
"Timeout reached after {0}ms. {1} " +
|
||||
"Remaining: ThreadWaiters={2}, InUseConnections={3}",
|
||||
elapsedMs,
|
||||
action,
|
||||
ThreadWaiterRegistry.GetRunningOperationsCount(),
|
||||
ClientConnectionPool.GetInUseConnectionsCount());
|
||||
}
|
||||
|
||||
static void LogWaitingForOperations(long elapsedMs)
|
||||
{
|
||||
// Only log once per second to avoid excessive log spam
|
||||
long currentSecond = elapsedMs / 1000;
|
||||
if (currentSecond == mLastLoggedSecond)
|
||||
return;
|
||||
|
||||
mLastLoggedSecond = currentSecond;
|
||||
|
||||
mLog.DebugFormat(
|
||||
"Waiting: ThreadWaiters={0}, InUseConnections={1}, " +
|
||||
"UVCSOperations={2}, elapsed={3}ms",
|
||||
ThreadWaiterRegistry.GetRunningOperationsCount(),
|
||||
ClientConnectionPool.GetInUseConnectionsCount(),
|
||||
UVCSPlugin.Instance.HasRunningOperation(),
|
||||
elapsedMs);
|
||||
}
|
||||
|
||||
const int MAX_WAIT_TIMEOUT_MS = 10000;
|
||||
const int PROGRESS_BAR_DELAY_MS = 1000;
|
||||
const int POLL_INTERVAL_MS = 50;
|
||||
const string PLAY_MODE_PENDING_KEY = "WaitForPendingOperations.PlayModePending";
|
||||
|
||||
static long mLastLoggedSecond = -1;
|
||||
static bool mPlayModeWaitTimeoutReached;
|
||||
static Stopwatch mPlayModeWaitStopwatch;
|
||||
|
||||
static readonly ILog mLog = PlasticApp.GetLogger("WaitForPendingOperations");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user