public async ValueTask <ImmutableArray <(Checksum, object)> > GetAssetsAsync(int scopeId, ISet <Checksum> checksums, ISerializerService serializerService, CancellationToken cancellationToken) { using var provider = await _client.GetProxyAsync <ISolutionAssetProvider>(SolutionAssetProvider.ServiceDescriptor, cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(provider.Proxy); return(await new RemoteCallback <ISolutionAssetProvider>(provider.Proxy, _clientDisconnectedSource).InvokeAsync( (proxy, stream, cancellationToken) => proxy.GetAssetsAsync(stream, scopeId, checksums.ToArray(), cancellationToken), (stream, cancellationToken) => RemoteHostAssetSerialization.ReadDataAsync(stream, scopeId, checksums, serializerService, cancellationToken), cancellationToken).ConfigureAwait(false)); }
public async ValueTask <ImmutableArray <(Checksum, object)> > GetAssetsAsync(Checksum solutionChecksum, ISet <Checksum> checksums, ISerializerService serializerService, CancellationToken cancellationToken) { // Make sure we are on the thread pool to avoid UI thread dependencies if external code uses ConfigureAwait(true) await TaskScheduler.Default; return(await RemoteCallback <ISolutionAssetProvider> .InvokeServiceAsync( _client, SolutionAssetProvider.ServiceDescriptor, (callback, cancellationToken) => callback.InvokeAsync( (proxy, pipeWriter, cancellationToken) => proxy.GetAssetsAsync(pipeWriter, solutionChecksum, checksums.ToArray(), cancellationToken), (pipeReader, cancellationToken) => RemoteHostAssetSerialization.ReadDataAsync(pipeReader, solutionChecksum, checksums, serializerService, cancellationToken), cancellationToken), cancellationToken).ConfigureAwait(false)); }
public async ValueTask <ImmutableArray <(Checksum, object)> > GetAssetsAsync(int scopeId, ISet <Checksum> checksums, ISerializerService serializerService, CancellationToken cancellationToken) { // Make sure we are on the thread pool to avoid UI thread dependencies if external code uses ConfigureAwait(true) await TaskScheduler.Default; using var provider = await _client.GetProxyAsync <ISolutionAssetProvider>(SolutionAssetProvider.ServiceDescriptor, cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(provider.Proxy); return(await new RemoteCallback <ISolutionAssetProvider>(provider.Proxy, _clientDisconnectedSource).InvokeAsync( (proxy, pipeWriter, cancellationToken) => proxy.GetAssetsAsync(pipeWriter, scopeId, checksums.ToArray(), cancellationToken), (pipeReader, cancellationToken) => RemoteHostAssetSerialization.ReadDataAsync(pipeReader, scopeId, checksums, serializerService, cancellationToken), cancellationToken).ConfigureAwait(false)); }
public ValueTask GetAssetsAsync(Stream outputStream, int scopeId, Checksum[] checksums, CancellationToken cancellationToken) { // Complete RPC right away so the client can start reading from the stream. // The fire-and forget task starts writing to the output stream and the client will read it until it reads all expected data. _ = Task.Run(async() => { using var writer = new ObjectWriter(outputStream, leaveOpen: false, cancellationToken); var assetStorage = _services.GetRequiredService <ISolutionAssetStorageProvider>().AssetStorage; var serializer = _services.GetRequiredService <ISerializerService>(); await RemoteHostAssetSerialization.WriteDataAsync(writer, assetStorage, serializer, scopeId, checksums, cancellationToken).ConfigureAwait(false); }, cancellationToken); return(default);
public async ValueTask GetAssetsAsync(PipeWriter pipeWriter, int scopeId, Checksum[] checksums, CancellationToken cancellationToken) { var assetStorage = _services.GetRequiredService <ISolutionAssetStorageProvider>().AssetStorage; var serializer = _services.GetRequiredService <ISerializerService>(); SolutionAsset?singleAsset = null; IReadOnlyDictionary <Checksum, SolutionAsset>?assetMap = null; if (checksums.Length == 1) { singleAsset = (await assetStorage.GetAssetAsync(scopeId, checksums[0], cancellationToken).ConfigureAwait(false)) ?? SolutionAsset.Null; } else { assetMap = await assetStorage.GetAssetsAsync(scopeId, checksums, cancellationToken).ConfigureAwait(false); } // We can cancel early, but once the pipe operations are scheduled we rely on both operations running to // avoid deadlocks (the exception handler in 'task1' ensures progress is made in 'task2'). cancellationToken.ThrowIfCancellationRequested(); var mustNotCancelToken = CancellationToken.None; // Work around the lack of async stream writing in ObjectWriter, which is required when writing to the RPC pipe. // Run two tasks - the first synchronously writes to a local pipe and the second asynchronosly transfers the data to the RPC pipe. // // Configure the pipe to never block on write (waiting for the reader to read). This prevents deadlocks but might result in more // (non-contiguous) memory allocated for the underlying buffers. The amount of memory is bounded by the total size of the serialized assets. var localPipe = new Pipe(RemoteHostAssetSerialization.PipeOptionsWithUnlimitedWriterBuffer); var task1 = Task.Run(() => { try { var stream = localPipe.Writer.AsStream(leaveOpen: false); using var writer = new ObjectWriter(stream, leaveOpen: false, cancellationToken); RemoteHostAssetSerialization.WriteData(writer, singleAsset, assetMap, serializer, scopeId, checksums, cancellationToken); } catch (Exception e) when(FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { // no-op } }, mustNotCancelToken); // Complete RPC once we send the initial piece of data and start waiting for the writer to send more, // so the client can start reading from the stream. Once CopyPipeDataAsync completes the pipeWriter // the corresponding client-side pipeReader will complete and the data transfer will be finished. var task2 = CopyPipeDataAsync(); await Task.WhenAll(task1, task2).ConfigureAwait(false); async Task CopyPipeDataAsync() { Exception?exception = null; try { await localPipe.Reader.CopyToAsync(pipeWriter, cancellationToken).ConfigureAwait(false); } catch (Exception e) { FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken); exception = e; } finally { await localPipe.Reader.CompleteAsync(exception).ConfigureAwait(false); await pipeWriter.CompleteAsync(exception).ConfigureAwait(false); } } }