Example #1
0
        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));
        }
Example #2
0
        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));
        }
Example #3
0
        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);
                }
            }
        }