浏览代码

Fixing issue with an infinite loop if an enqueued operation continued to enqueue itself on execution.

/main/staging/rate_limit_data
nathaniel.buck@unity3d.com 3 年前
当前提交
abcdd61e
共有 2 个文件被更改,包括 20 次插入27 次删除
  1. 5
      Assets/Scripts/Game/GameManager.cs
  2. 42
      Assets/Scripts/Lobby/LobbyAsyncRequests.cs

5
Assets/Scripts/Game/GameManager.cs


}
else if (type == MessageType.QueryLobbies)
{
if (LobbyAsyncRequests.Instance.GetRateLimit(LobbyAsyncRequests.RequestType.Query).IsInCooldown)
{
//Debug.LogError("Cooldown!");
//return;
}
m_lobbyServiceData.State = LobbyQueryState.Fetching;
LobbyAsyncRequests.Instance.RetrieveLobbyListAsync(
qr => {

42
Assets/Scripts/Lobby/LobbyAsyncRequests.cs


public LobbyAsyncRequests()
{
Locator.Get.UpdateSlow.Subscribe(UpdateLobby, 0.5f); // Shouldn't need to unsubscribe since this instance won't be replaced. 0.5s is arbitrary; the rate limits are tracked later.
m_rateLimitQuery.onChanged += HandlePendingOperations;
}
private static bool IsSuccessful(Response response)

#region Once connected to a lobby, cache the local lobby object so we don't query for it for every lobby operation.
// (This assumes that the player will be actively in just one lobby at a time, though they could passively be in more.)
private Queue<Action> m_pendingOperations = new Queue<Action>();
private string m_currentLobbyId = null;
private Lobby m_lastKnownLobby;
public Lobby CurrentLobby => m_lastKnownLobby;

m_lastKnownLobby = lobby;
}
}
}
private void HandlePendingOperations(RateLimitCooldown rateLimit)
{
if (rateLimit.IsInCooldown)
return;
while (m_pendingOperations.Count > 0)
m_pendingOperations.Dequeue()?.Invoke(); // Note: If this ends up enqueuing a bunch of operations, we might need to batch them and/or ensure they don't all execute at once.
}
#endregion

/// Used for getting the list of all active lobbies, without needing full info for each.
/// </summary>
/// <param name="onListRetrieved">If called with null, retrieval was unsuccessful. Else, this will be given a list of contents to display, as pairs of a lobby code and a display string for that lobby.</param>
/// <param name="isRecursive">Just as a backup to effectively batch together multiple retrievals into one if they arrive in rapid succession.</param>
public void RetrieveLobbyListAsync(Action<QueryResponse> onListRetrieved, Action<Response<QueryResponse>> onError = null, LobbyColor limitToColor = LobbyColor.None, bool isRecursive = false)
public void RetrieveLobbyListAsync(Action<QueryResponse> onListRetrieved, Action<Response<QueryResponse>> onError = null, LobbyColor limitToColor = LobbyColor.None)
// We assume this can only occur when leaving a lobby and returning to the join menu; the refresh button is rate-limited, but we might have recently queried while still in the lobby.
if (!isRecursive)
{
m_pendingOperations.Enqueue(() => { RetrieveLobbyListAsync(onListRetrieved, onError, limitToColor, true); }); // With that assumption, retry the refresh after the limit, as though entering the join menu from the main menu.
UnityEngine.Debug.LogError("Enqueuing...");
}
else
UnityEngine.Debug.LogError("Blocking recursive call");
onListRetrieved?.Invoke(null);
m_rateLimitQuery.EnqueuePendingOperation(() => { RetrieveLobbyListAsync(onListRetrieved, onError, limitToColor); });
return;
}

private bool ShouldUpdateData(Action caller, Action onComplete, bool shouldRetryIfLobbyNull)
{
if (m_rateLimitQuery.IsInCooldown)
{ m_pendingOperations.Enqueue(caller);
{ m_rateLimitQuery.EnqueuePendingOperation(caller);
return false;
}
Lobby lobby = m_lastKnownLobby;

m_pendingOperations.Enqueue(caller);
m_rateLimitQuery.EnqueuePendingOperation(caller);
onComplete?.Invoke();
return false;
}

{
private float m_timeSinceLastCall = float.MaxValue;
private readonly float m_cooldownTime;
private Queue<Action> m_pendingOperations = new Queue<Action>();
private bool m_isHandlingPending = false; // Just in case a pending operation tries to enqueue itself again.
public void EnqueuePendingOperation(Action action)
{
if (!m_isHandlingPending)
m_pendingOperations.Enqueue(action);
}
private bool m_isInCooldown = false;
public bool IsInCooldown

private void OnUpdate(float dt)
{
m_timeSinceLastCall += dt;
m_isHandlingPending = false; // (Backup in case a pending operation hit an exception.)
{
m_isHandlingPending = true;
while (m_pendingOperations.Count > 0)
m_pendingOperations.Dequeue()?.Invoke(); // Note: If this ends up enqueuing many operations, we might need to batch them and/or ensure they don't all execute at once.
m_isHandlingPending = false;
}
}
}

正在加载...
取消
保存