private async Task <Message> RunLoop(CancellationToken loopCt) { TaskUtilities.AssertIsOnBackgroundThread(); var cancelAllCtsLink = CancellationTokenSource.CreateLinkedTokenSource(loopCt, _cancelAllCts.Token); var ct = cancelAllCtsLink.Token; try { Log.EnterRLoop(_rLoopDepth++); while (!loopCt.IsCancellationRequested) { var message = await ReceiveMessageAsync(loopCt); if (message == null) { return(null); } else if (message.IsResponse) { Request request; if (!_requests.TryRemove(message.RequestId, out request)) { throw ProtocolError($"Mismatched response - no request with such ID:", message); } else if (message.Name != ":" + request.MessageName.Substring(1)) { throw ProtocolError($"Mismatched response - message name does not match request '{request.MessageName}':", message); } request.Handle(this, message); continue; } try { switch (message.Name) { case "!End": message.ExpectArguments(1); await _callbacks.Shutdown(message.GetBoolean(0, "rdataSaved")); break; case "!CanceledAll": CancelAll(); break; case "?YesNoCancel": ShowDialog(message, MessageButtons.YesNoCancel, ct) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?YesNo": ShowDialog(message, MessageButtons.YesNo, ct) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?OkCancel": ShowDialog(message, MessageButtons.OKCancel, ct) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?LocYesNoCancel": ShowLocalizedDialogFormat(message, MessageButtons.YesNoCancel, ct) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?LocYesNo": ShowLocalizedDialogFormat(message, MessageButtons.YesNo, ct) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?LocOkCancel": ShowLocalizedDialogFormat(message, MessageButtons.OKCancel, ct) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?>": ReadConsole(message, ct) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "!": case "!!": message.ExpectArguments(1); await _callbacks.WriteConsoleEx( message.GetString(0, "buf", allowNull: true), message.Name.Length == 1?OutputType.Output : OutputType.Error, ct); break; case "!ShowMessage": message.ExpectArguments(1); await _callbacks.ShowMessage(message.GetString(0, "s", allowNull: true), ct); break; case "!+": await _callbacks.Busy(true, ct); break; case "!-": await _callbacks.Busy(false, ct); break; case "!SetWD": _callbacks.DirectoryChanged(); break; case "!Library": await _callbacks.ViewLibrary(ct); break; case "!ShowFile": message.ExpectArguments(3); // Do not await since it blocks callback from calling the host again _callbacks.ShowFile( message.GetString(0, "file"), message.GetString(1, "tabName"), message.GetBoolean(2, "delete.file"), ct).DoNotWait(); break; case "?EditFile": message.ExpectArguments(2); // Opens file in editor and blocks until file is closed. var content = await _callbacks.EditFileAsync(message.GetString(0, "name", allowNull: true), message.GetString(1, "file", allowNull: true), ct); ct.ThrowIfCancellationRequested(); await RespondAsync(message, ct, content); break; case "!View": message.ExpectArguments(2); _callbacks.ViewObject(message.GetString(0, "x"), message.GetString(1, "title"), ct) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "!Plot": await _callbacks.Plot( new PlotMessage( Guid.Parse(message.GetString(0, "device_id")), Guid.Parse(message.GetString(1, "plot_id")), message.GetString(2, "file_path"), message.GetInt32(3, "device_num"), message.GetInt32(4, "active_plot_index"), message.GetInt32(5, "plot_count"), message.Blob), ct); break; case "?Locator": var locatorResult = await _callbacks.Locator(Guid.Parse(message.GetString(0, "device_id")), ct); ct.ThrowIfCancellationRequested(); await RespondAsync(message, ct, locatorResult.Clicked, locatorResult.X, locatorResult.Y); break; case "?PlotDeviceCreate": var plotDeviceResult = await _callbacks.PlotDeviceCreate(Guid.Parse(message.GetString(0, "device_id")), ct); ct.ThrowIfCancellationRequested(); await RespondAsync(message, ct, plotDeviceResult.Width, plotDeviceResult.Height, plotDeviceResult.Resolution); break; case "!PlotDeviceDestroy": await _callbacks.PlotDeviceDestroy(Guid.Parse(message.GetString(0, "device_id")), ct); break; case "!WebBrowser": _callbacks.WebBrowser(message.GetString(0, "url"), ct) .DoNotWait(); break; case "?BeforePackagesInstalled": await _callbacks.BeforePackagesInstalledAsync(ct); ct.ThrowIfCancellationRequested(); await RespondAsync(message, ct, true); break; case "?AfterPackagesInstalled": await _callbacks.AfterPackagesInstalledAsync(ct); ct.ThrowIfCancellationRequested(); await RespondAsync(message, ct, true); break; case "!PackagesRemoved": _callbacks.PackagesRemoved(); break; case "!FetchFile": var remoteFileName = message.GetString(0, "file_remote_name"); var remoteBlobId = message.GetUInt64(1, "blob_id"); var localPath = message.GetString(2, "file_local_path"); Task.Run(async() => { var destPath = await _callbacks.FetchFileAsync(remoteFileName, remoteBlobId, localPath, ct); if (!message.GetBoolean(3, "silent")) { await _callbacks.WriteConsoleEx(destPath, OutputType.Error, ct); } }).DoNotWait(); break; case "!LocMessage": _callbacks.WriteConsoleEx(GetLocalizedString(message) + Environment.NewLine, OutputType.Output, ct).DoNotWait(); break; case "!LocWarning": _callbacks.WriteConsoleEx(GetLocalizedString(message) + Environment.NewLine, OutputType.Error, ct).DoNotWait(); break; default: throw ProtocolError($"Unrecognized host message name:", message); } if (_cancelAllCts.IsCancellationRequested) { ct = UpdateCancelAllCtsLink(ref cancelAllCtsLink, loopCt); } } catch (OperationCanceledException) when(_cancelAllCts.IsCancellationRequested) { // Canceled via _cancelAllCts - update cancelAllCtsLink and move on ct = UpdateCancelAllCtsLink(ref cancelAllCtsLink, loopCt); } } } finally { // asyncronously-running handlers like ReadConsole and ShowDialog that were started in the loop should be canceled cancelAllCtsLink.Cancel(); cancelAllCtsLink.Dispose(); Log.ExitRLoop(--_rLoopDepth); } return(null); }
private async Task <Message> RunLoop(CancellationToken ct) { TaskUtilities.AssertIsOnBackgroundThread(); try { _log.EnterRLoop(_rLoopDepth++); while (!ct.IsCancellationRequested) { var message = await ReceiveMessageAsync(ct); if (message.IsResponse) { Request request; if (!_requests.TryRemove(message.RequestId, out request)) { throw ProtocolError($"Mismatched response - no request with such ID:", message); } else if (message.Name != ":" + request.MessageName.Substring(1)) { throw ProtocolError($"Mismatched response - message name does not match request '{request.MessageName}':", message); } request.Handle(this, message); continue; } try { switch (message.Name) { case "!End": return(null); case "!CanceledAll": CancelAll(); break; case "?YesNoCancel": ShowDialog(message, MessageButtons.YesNoCancel, CancellationTokenSource.CreateLinkedTokenSource(ct, _cancelAllCts.Token).Token) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?YesNo": ShowDialog(message, MessageButtons.YesNo, CancellationTokenSource.CreateLinkedTokenSource(ct, _cancelAllCts.Token).Token) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?OkCancel": ShowDialog(message, MessageButtons.OKCancel, CancellationTokenSource.CreateLinkedTokenSource(ct, _cancelAllCts.Token).Token) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "?>": ReadConsole(message, CancellationTokenSource.CreateLinkedTokenSource(ct, _cancelAllCts.Token).Token) .SilenceException <MessageTransportException>() .DoNotWait(); break; case "!": case "!!": message.ExpectArguments(1); await _callbacks.WriteConsoleEx( message.GetString(0, "buf", allowNull: true), message.Name.Length == 1?OutputType.Output : OutputType.Error, ct); break; case "!ShowMessage": message.ExpectArguments(1); await _callbacks.ShowMessage(message.GetString(0, "s", allowNull: true), ct); break; case "!+": await _callbacks.Busy(true, ct); break; case "!-": await _callbacks.Busy(false, ct); break; case "!SetWD": _callbacks.DirectoryChanged(); break; case "!Library": await _callbacks.ViewLibrary(); break; case "!ShowFile": message.ExpectArguments(3); await _callbacks.ShowFile( message.GetString(0, "file"), message.GetString(1, "tabName"), message.GetBoolean(2, "delete.file")); break; case "!View": message.ExpectArguments(2); _callbacks.ViewObject(message.GetString(0, "x"), message.GetString(1, "title")); break; case "!Plot": await _callbacks.Plot( new PlotMessage( message.GetString(0, "xaml_file_path"), message.GetInt32(1, "active_plot_index"), message.GetInt32(2, "plot_count"), message.Blob), ct); break; case "?Locator": var locatorResult = await _callbacks.Locator(ct); ct.ThrowIfCancellationRequested(); await RespondAsync(message, ct, locatorResult.Clicked, locatorResult.X, locatorResult.Y); break; case "!WebBrowser": await _callbacks.WebBrowser(message.GetString(0, "url")); break; case "!PackagesInstalled": _callbacks.PackagesInstalled(); break; case "!PackagesRemoved": _callbacks.PackagesRemoved(); break; case "!FetchFile": var destPath = await _callbacks.SaveFileAsync(message.GetString(0, "file_path"), message.Blob); await _callbacks.WriteConsoleEx(destPath, OutputType.Error, ct); break; default: throw ProtocolError($"Unrecognized host message name:", message); } } catch (OperationCanceledException) when(!ct.IsCancellationRequested) { // Canceled via _cancelAllCts - just move onto the next message. } } } finally { _log.ExitRLoop(--_rLoopDepth); } return(null); }