Address @ezhevita findings in regards to race conditions:
- NRE in this lambda function: 1a9f2a23c4/ArchiSteamFarm/Steam/Bot.cs (L1962)
- NRE in the ArchiSteamFarm.Steam.Bot.StopHandlingCallbacks (probably race condition?)
In general, both are caused by race conditions which can happen if user attempts to start/stop bot while critical section of handling callbacks loop is going. The code is overly complex unfortunately, so debugging it/guarantee of safety is problematic.
This commit therefore attempts to fix the underlying issue by synchronizing the code that starts/stops the underlying callbacks handling loop. While the loop itself is already thread-safe, the code that starts/stops it was not before. Now Start() as well as Stop() can not occur concurrently. On top of that, the only other place which has potential to stop the loop - final disconnect, is also guarded with additional condition that it can fire only if we're NOT set to KeepRunning at the time of calling, which should fix the situation where late disconnected callback could potentially stop already triggered new loop.
As usual in such complex situations, time will tell if this fixes all the issues we have.
Previously we've kept websocket connection open for as long as caller requested it. During graceful shutdown, ASP.NET normally waits for all pending requests to finish, while no longer accepting new ones - this is a very good approach. In our case however, since we didn't do anything with that event before, the graceful shutdown was timing out after 30 seconds before eventually forcefully killing any still-ongoing requests, websocket connection in our case.
Hook into application lifetime in order to be notified when the graceful shutdown happens. This way we can create linked cancellation token for request abort and graceful shutdown and close the websocket connection when any of those two happens.