I have a Unity game, implementing a gRPC client that is generated from the protobuf compiler. While the game is running, I can receive all the data I need from the streaming call, and interact with that data perfectly. The issue I have is in the shutdown of the game and cleanup of the gRPC client/channel (I believe).
When I attempt to exit play mode, it retains a connection to the server, and the Unity editor hangs (presumably waiting for the PlotManager.OnDisable
code ShutdownAsync.Wait()
call).
The only way to remedy this at the moment is to shutdown my server process, which causes the connection to be lost, and Unity editor returns to normal functionality.
How can I best manage the lifecycle of the Client / Channel to ensure the Unity game cleans up correctly?
public class PlotRenderer : MonoBehaviour
{
private bool _subscribed = false;
private CancellationTokenSource _cancellationToken;
public async void Subscribe()
{
this._subscribed = true;
this._cancellationToken = new CancellationTokenSource();
using (var call = PlotManager.s.Client.SubscribePlotCellUpdates(
new PrimaryKey { Id = this._plot.Id },
cancellationToken: this._cancellationToken.Token))
{
var responseReader = Task.Run(async () =>
{
while (this._subscribed
&& await call.ResponseStream.MoveNext(this._cancellationToken.Token))
{
var cell = call.ResponseStream.Current;
// Add cell to a list.
}
this.Unsubscribe();
});
await responseReader;
}
}
public void Unsubscribe()
{
if (this._cancellationToken != null)
this._cancellationToken.Cancel();
this._subscribed = false;
}
// Called when the object is being destroyed (game stopping)
private void OnDestroy()
{
this.Unsubscribe();
}
}
Relevant code from my PlotManager
class is:
public class PlotManager
{
public static PlotManager s;
public Plotty.PlottyClient Client;
public Channel Channel;
public string Address = "127.0.0.1:8075"
// Called when object is created and enabled in game
private void OnEnable()
{
this.Channel = new Channel(this.Address, ChannelCredentials.Insecure);
return new Plotty.PlottyClient(this.Channel);
}
// Called when the object is being destroyed (game stopping)
private void OnDisable()
{
this.Channel.ShutdownAsync().Wait();
this.Channel = null;
this.Client = null;
}
}