/// <summary> /// Returns a task representing the result of the given task which will run continuations asynchronously. /// </summary> public static Task <T> RunContinuationsAsync <T>(this Task <T> task) { var taskSource = TaskSourceSlim.Create <T>(); taskSource.LinkToTask(task); return(taskSource.Task); }
/// <inheritdoc /> public IEnumerable <Task <Possible <StrongFingerprint, Failure> > > EnumerateStrongFingerprints( WeakFingerprintHash weak, UrgencyHint urgencyHint, Guid activityId) { // TODO: Extend IAsyncEnumerable up through EnumerateStrongFingerprints var tcs = TaskSourceSlim.Create <IEnumerable <GetSelectorResult> >(); yield return(Task.Run( async() => { try { var results = await ReadOnlyCacheSession.GetSelectors(new Context(Logger), weak.ToMemoization(), CancellationToken.None).ToList(); tcs.SetResult(results); return results.Any() ? results.First().FromMemoization(weak, CacheId) : StrongFingerprintSentinel.Instance; } catch (Exception ex) { tcs.SetException(ex); throw; } })); // For now, callers should always await the first task before enumerating the rest Contract.Assert(tcs.Task.IsCompleted); IEnumerable <GetSelectorResult> otherResults = tcs.Task.GetAwaiter().GetResult(); foreach (var otherResult in otherResults.Skip(1)) { yield return(Task.FromResult(otherResult.FromMemoization(weak, CacheId))); } }
public Task PurgeIsCanceledForShutdown() { var tcsBlockEnumeration = TaskSourceSlim.Create <bool>(); return(FillSoNextPutTriggersSoftPurgeThenRunTest( async list => { Output.WriteLine("Awaiting tsc..."); await tcsBlockEnumeration.Task; Output.WriteLine("Tsc is done"); return list; }, async(store, hash1, hash2, stream) => { var hash = await PutAsync(store, stream); Task shutdownTask = Task.Run(() => store.ShutdownAsync(Context)); while (!store.ShutdownStarted) { await Task.Yield(); } tcsBlockEnumeration.SetResult(true); await shutdownTask; Assert.True(FileSystem.FileExists(store.GetReplicaPathForTest(hash1, 0))); Assert.True(FileSystem.FileExists(store.GetReplicaPathForTest(hash2, 0))); Assert.True(FileSystem.FileExists(store.GetReplicaPathForTest(hash, 0))); })); }
private void DeactivateConnection(TrackedConnection connection) { lock (m_syncLock) { if (m_isActiveCompletionSource.Task.IsCompleted) { if (m_isActiveCompletionSource.Task.Result) { m_isActiveCompletionSource = TaskSourceSlim.Create <bool>(); } } if (m_exceededInactivityTimeout) { m_isActiveCompletionSource.TrySetResult(false); } } OnDeactivateConnection?.Invoke(this, EventArgs.Empty); // TODO: Do nothing to actual connection for now. // Consider recycling connection (ie create new BondTcpConnection) if // connection fails too many times or hasn't had a successful call for a significant // interval. // NOTE: connection may be null Analysis.IgnoreArgument(connection); }
internal AddFileItem(IDropItem item) { TaskSource = TaskSourceSlim.Create <AddFileResult>(); m_dropItem = item; m_fileBlobDescriptorForUpload = null; m_fileBlobDescriptorForAssociate = null; }
/// <summary> /// Class constructor /// </summary> /// <param name="client">the bond tcp client used to create the proxy</param> /// <param name="createProxyCallback">callback used to create a bond proxy</param> /// <param name="loggingContext">the logging context</param> /// <param name="services">shared services for distribution</param> /// <param name="maxConnectionConcurrency">the maximum number of connections</param> public BondProxyConnectionManager( BondTcpClient <TProxy> client, BondTcpClient <TProxy> .CreateProxyCallback createProxyCallback, LoggingContext loggingContext, DistributionServices services, int maxConnectionConcurrency = 1) { m_client = client; m_createProxyCallback = createProxyCallback; m_loggingContext = loggingContext; m_maxConnectionConcurrency = maxConnectionConcurrency; m_services = services; m_bufferManager = services.BufferManager; m_proxySemaphore = new NullSemaphore(); // m_proxySemaphore = new SemaphoreSlim(m_maxConnectionConcurrency); m_stopwatch = Stopwatch.StartNew(); m_heartbeatTimer = new Timer(HeartbeatTimerCallback); m_connections = new TrackedConnection[m_maxConnectionConcurrency]; for (int i = 0; i < m_maxConnectionConcurrency; i++) { m_connections[i] = new TrackedConnection(this); } m_isActiveCompletionSource = TaskSourceSlim.Create <bool>(); }
public async Task NoAddsOnceStopping() { var context = new Context(Logger); using (var runner = CreateBackgroundTaskTracker()) { var completion = TaskSourceSlim.Create <int>(); runner.Add(completion.Task); Task stopTask = runner.ShutdownAsync(context); Exception exception = null; try { runner.Add(Task.FromResult(0)); } catch (Exception caughtException) { Assert.True(runner.InShutdown); exception = caughtException; } Assert.NotNull(exception); Task.Run(() => completion.SetResult(0)).Should().NotBeNull(); await stopTask; } }
/// <inheritdoc /> public Task <IIpcResult> Send(IIpcOperation operation) { Contract.Requires(operation != null); var request = new Request(operation); // Must add the request to the m_pendingRequest dictionary before posting it to m_sendRequestBlock // Otherwise, the following can happen: // 1) the request is posted // 2) the request is picked up from the queue and processed // 3) the request handler looks up corresponding completionSource in the dictionary which is not there yet (ERROR) // 4) the TaskCompletionSource is added to the dictionary var completionSource = TaskSourceSlim.Create <IIpcResult>(); m_pendingRequests[request.Id] = completionSource; operation.Timestamp.Request_BeforePostTime = DateTime.UtcNow; bool posted = m_sendRequestBlock.Post(request); if (!posted) { // if the request was not posted: // (1) remove it from the dictionary TaskSourceSlim <IIpcResult> src; m_pendingRequests.TryRemove(request.Id, out src); // (2) complete it (with TransmissionError) completionSource.TrySetResult(new IpcResult( IpcResultStatus.TransmissionError, "Could not post IPC request: the client has already terminated.")); } return(completionSource.Task); }
/// <summary> /// Ensures that two locks are mutually exclusive by trying to acquire one lock (on another thread) while the other lock is held /// and waiting the specified amount of time /// </summary> private void TestMutuallyExclusive <TLock1, TLock2>(Func <TLock1> lockCreator1, Func <TLock2> lockCreator2, int waitTimeInMilliseconds = 500, bool assertExclusive = true) where TLock1 : IDisposable where TLock2 : IDisposable { // Try with first lock held TestMutuallyExclusiveHelper(lockCreator1, lockCreator2, 1, 2, waitTimeInMilliseconds, assertExclusive); // Now try with second lock held TestMutuallyExclusiveHelper(lockCreator2, lockCreator1, 2, 1, waitTimeInMilliseconds, assertExclusive); if (assertExclusive) { Task lockCreator1StartedEvent; Task lockCreator2StartedEvent; TaskSourceSlim <int[]> accessCheckTaskProvider = TaskSourceSlim.Create <int[]>(); var loop1Completion = TryAcquireLoop(lockCreator1, accessCheckTaskProvider.Task, out lockCreator1StartedEvent); var loop2Completion = TryAcquireLoop(lockCreator2, accessCheckTaskProvider.Task, out lockCreator2StartedEvent); // Wait for task bodies to be entered lockCreator1StartedEvent.Wait(); lockCreator2StartedEvent.Wait(); // Start the loops and provide the access check array accessCheckTaskProvider.SetResult(new int[1]); // Wait for the loops to complete loop1Completion.GetAwaiter().GetResult(); loop2Completion.GetAwaiter().GetResult(); } }
public PipCompletionTask(OperationContext operationContext, RunnablePip pip) { RunnablePip = pip; OperationContext = operationContext; Completion = TaskSourceSlim.Create <Lazy <ExecutionResult> >(); QueuedTime = DateTime.UtcNow; }
/// <summary> /// Class constructor /// </summary> /// <param name="appLoggingContext">Application-level logging context</param> /// <param name="maxProcesses">the maximum number of concurrent pips on the worker</param> /// <param name="config">Distribution config</param>\ /// <param name="buildId">the build id</param> public WorkerService(LoggingContext appLoggingContext, int maxProcesses, IDistributionConfiguration config, string buildId) { m_isGrpcEnabled = config.IsGrpcEnabled; m_appLoggingContext = appLoggingContext; m_maxProcesses = maxProcesses; m_port = config.BuildServicePort; m_services = new DistributionServices(buildId); if (m_isGrpcEnabled) { m_workerServer = new Grpc.GrpcWorkerServer(this, appLoggingContext, buildId); } else { #if !DISABLE_FEATURE_BOND_RPC m_bondWorkerService = new InternalBond.BondWorkerServer(appLoggingContext, this, m_port, m_services); m_workerServer = m_bondWorkerService; #endif } m_attachCompletionSource = TaskSourceSlim.Create <bool>(); m_exitCompletionSource = TaskSourceSlim.Create <bool>(); m_workerRunnablePipObserver = new WorkerRunnablePipObserver(this); m_sendThread = new Thread(SendBuildResults); }
/// <summary> /// Constructor /// </summary> public RemoteWorker( bool isGrpcEnabled, LoggingContext appLoggingContext, uint workerId, MasterService masterService, ServiceLocation serviceLocation) : base(workerId, name: I($"#{workerId} ({serviceLocation.IpAddress}::{serviceLocation.Port})")) { m_isGrpcEnabled = isGrpcEnabled; m_appLoggingContext = appLoggingContext; m_masterService = masterService; m_buildRequests = new BlockingCollection <ValueTuple <PipCompletionTask, SinglePipBuildRequest> >(); m_attachCompletion = TaskSourceSlim.Create <bool>(); m_executionBlobCompletion = TaskSourceSlim.Create <bool>(); m_serviceLocation = serviceLocation; if (isGrpcEnabled) { m_workerClient = new Grpc.GrpcWorkerClient(m_appLoggingContext, masterService.DistributionServices.BuildId, serviceLocation.IpAddress, serviceLocation.Port); } else { #if !DISABLE_FEATURE_BOND_RPC m_workerClient = new InternalBond.BondWorkerClient(m_appLoggingContext, Name, serviceLocation.IpAddress, serviceLocation.Port, masterService.DistributionServices, OnActivateConnection, OnDeactivateConnection, OnConnectionTimeOut); #endif } // Depending on how long send requests take. It might make sense to use the same thread between all workers. m_sendThread = new Thread(SendBuildRequests); }
public async Task IncreaseConcurrency() { int count = 0; var waitForFirstTwoItems = TaskSourceSlim.Create <object>(); // Event that will hold first two workers. var mre = new ManualResetEventSlim(false); var actionBlock = new ActionBlockSlim <int>( 2, n => { var currentCount = Interlocked.Increment(ref count); if (currentCount == 2) { // Notify the test that 2 items are processed. waitForFirstTwoItems.SetResult(null); } if (currentCount <= 2) { // This is the first or the second thread that should be blocked before we increase the number of threads. mre.Wait(TimeSpan.FromSeconds(100)); } Thread.Sleep(1); }); // Schedule work actionBlock.Post(1); actionBlock.Post(2); await waitForFirstTwoItems.Task; // The first 2 threads should be blocked in the callback in the action block, // but the count should be incremented Assert.Equal(2, count); var task = actionBlock.CompletionAsync(); // The task should not be completed yet! Assert.NotEqual(TaskStatus.RanToCompletion, task.Status); // This will cause another thread to spawn actionBlock.IncreaseConcurrencyTo(3); // Add more work actionBlock.Post(3); actionBlock.Complete(); // Release the first 2 threads mre.Set(); // Waiting for completion await task; // The new thread should run and increment the count Assert.Equal(3, count); }
/// <summary> /// Waits for all currently added background tasks to complete. /// </summary> public Task Synchronize(string tag = null) { var syncCompletion = TaskSourceSlim.Create <ValueUnit>(); Enqueue(new QueueItem(syncCompletion, tag), "Synchronize", syncCompletion.Task.Id, tag); return(syncCompletion.Task); }
internal AddFileItem(IDropItem item) { DropResultTaskSource = TaskSourceSlim.Create <AddFileResult>(); m_dropItem = item; m_fileBlobDescriptorForUpload = null; m_fileBlobDescriptorForAssociate = null; BuildManifestTaskSource = TaskSourceSlim.Create <RegisterFileForBuildManifestResult>(); }
/// <nodoc /> public MultiplexingClient(IClientConfig config, Stream stream) { Contract.Requires(config != null); Contract.Requires(stream != null); Config = config; Logger = config.Logger ?? VoidLogger.Instance; m_stream = stream; m_pendingRequests = new ConcurrentDictionary <int, TaskSourceSlim <IIpcResult> >(); m_disconnectRequestedByServer = false; // Receiving Dataflow: // This generic server will call 'SetResponse' for every received Response. 'SetResponse' // could be done in parallel, but there is no point, since they all access a shared concurrent // dictionary and all they do is lookup task completion source and set a result for it. m_responseListener = new GenericServer <Response>( name: "MultiplexingClient.ResponseListener", config: new ServerConfig { Logger = Logger, MaxConcurrentClients = 1 }, listener: ReceiveResponseAsync, clientFailuresAreFatal: true); // Sending Dataflow: // All 'Send' requests are concurrently queued up here. Processing of this block // ('SendRequestAsync') must be sequential, because uses the shared connection. m_sendRequestBlock = new ActionBlock <Request>( SendRequestAsync, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1, }); // set continuations that handle errors (unblock pending requests and set failure as completion of this client) var continuationDone = TaskSourceSlim.Create <Unit>(); m_responseListener.Completion.ContinueWith(_ => m_sendRequestBlock.Complete()); m_sendRequestBlock.Completion.ContinueWith(async _ => { m_responseListener.Complete(); UnblockPendingRequests(new IpcResult(IpcResultStatus.GenericError, "MultiplexingClient completed before all requests were handled")); if (!m_disconnectRequestedByServer) { var succeeded = await Request.StopRequest.TrySerializeAsync(m_stream); Logger.Verbose("Sending StopRequest {0}.", succeeded ? "Succeeded" : "Failed"); } continuationDone.SetResult(Unit.Void); }); // start listening for responses m_responseListener.Start(SetResponseAsync); // set the completion task m_completion = TaskUtilities.SafeWhenAll(new[] { m_sendRequestBlock.Completion, m_responseListener.Completion, continuationDone.Task }); }
/// <summary> /// Waits until all processes have terminated. /// </summary> /// <remarks> /// This function may safely be invoked concurrently / multiple times. /// </remarks> /// <returns>Boolean indicating whether all processes have terminated (false indicates that a timeout occurred instead)</returns> public Task <bool> WaitAsync(TimeSpan timeout) { Contract.Requires(timeout >= TimeSpan.Zero); Contract.Requires(timeout.TotalMilliseconds < uint.MaxValue); ManualResetEvent doneEvent; lock (m_lock) { Contract.Assume(!m_disposed, "WaitAsync called on a disposed JobObject"); doneEvent = m_doneEvent; if (m_done || !HasAnyProcesses()) { // Fast path: no need to wait // Note that we check HasAnyProcesses() in addition to m_done so as to more likely avoid pathological behavior in which calls to WaitAsync // happen close to job exit (e.g. because the job had one process, and the process handle already signalled); in that case we are // racing with the completion port drainer to set m_done, so creating an event and possibly sleeping the current thread would be wasteful. return(BoolTask.True); } else if (doneEvent == null) { // Slower path: need to wait as the first waiter (no event yet). doneEvent = m_doneEvent = new ManualResetEvent(initialState: false); } else { // Slower path: need to wait on an existing event (with a previous waiter). } } Contract.Assert(doneEvent != null); // doneEvent is now an event that was (at one point) m_doneEvent. // It may be set already, but RegisterWaitForSingleObject is robust to that case. var waiter = TaskSourceSlim.Create <bool>(); RegisteredWaitHandle waitPoolHandle = null; waitPoolHandle = ThreadPool.RegisterWaitForSingleObject( doneEvent, (state, timedOut) => { waiter.SetResult(!timedOut); // Note that the assignment of waitPoolHandle races with the callback. // Worst case, we'll let the garbage collector get it. if (waitPoolHandle != null) { waitPoolHandle.Unregister(null); } }, state: null, timeout: timeout, executeOnlyOnce: true); return(waiter.Task); }
/// <nodoc /> public PluginManager(LoggingContext loggingContext, string logDirectory, IEnumerable <string> pluginLocations) { m_plugins = new ConcurrentDictionary <string, Task <Possible <IPlugin> > >(); m_pluginHandlers = new PluginHandlers(); m_pluginStopTask = TaskSourceSlim.Create <Unit>(); m_loggingContext = loggingContext; m_logDirectory = logDirectory; m_pluginPaths = pluginLocations.ToList(); }
/// <nodoc/> public ListenerSourceBlock(CancellableListener listener, string name = "unknown", IIpcLogger logger = null) { Contract.Requires(listener != null); m_logger = logger ?? VoidLogger.Instance; m_name = name ?? "ListenerSourceBlock"; m_listener = listener; m_stopTask = TaskSourceSlim.Create <Unit>(); m_startCounter = 0; }
/// <summary> /// Initializes a new instance of the <see cref="LockHandle" /> struct for the given collection/key. /// </summary> public LockHandle(LockSet <TKey> locks, TKey key) { Contract.Requires(locks != null); Contract.Requires(key != null); TaskCompletionSource = TaskSourceSlim.Create <ValueUnit>(); _locks = locks; Key = key; _handleId = Interlocked.Increment(ref _currentHandleId); }
public Task TestHasherNotCorruptedByDelayedWrite() { return(TestHasherAsync( async hasher => { var content = ThreadSafeRandom.GetBytes(1000); HashAlgorithm algorithm; using (var token = hasher.CreateToken()) { // Capture hash algorithm from pool // WHAT??????? algorithm = token.Hasher; } if (algorithm is IHashAlgorithmInputLength setInputLength) { setInputLength.SetInputLength(content.Length); } var startTaskSource = TaskSourceSlim.Create <bool>(); var completeTaskSource = TaskSourceSlim.Create <bool>(); Task writeTask = null; using (var destinationStream = new PausedMemoryStream(startTaskSource, completeTaskSource)) using (var hashingDestinationStream = hasher.CreateWriteHashingStream(content.Length, destinationStream)) { writeTask = hashingDestinationStream.WriteAsync(content, 0, content.Length); // Wait for write operation to reach inner paused stream await startTaskSource.Task; } // Stream is now disposed (meaning hasher is returned to pool) // Allow write operation to complete which could attempt to write to the underlying hash algorithm // if the operation is not properly guarded completeTaskSource.SetResult(true); // Wait for write task to complete await writeTask; // This should be the same HashAlgorithm which was used by the hashing stream // in the pool. We detect if the HashAlgorithm is corrupted by attempting to hash with // empty content which should give the empty hash. if (algorithm is IHashAlgorithmInputLength setInputLength2) { setInputLength2.SetInputLength(0); } var hash = algorithm.ComputeHash(new byte[0], 0, 0); Assert.Equal(hasher.Info.EmptyHash, new ContentHash(hasher.Info.HashType, hash)); })); }
/// <summary> /// Constructor /// </summary> protected Worker(uint workerId, string name) { WorkerId = workerId; Name = name; m_workerSemaphores = new SemaphoreSet <StringId>(); m_workerPipStateManager = new WorkerPipStateManager(); PipStateSnapshot = m_workerPipStateManager.GetSnapshot(); m_workerOperationKind = OperationKind.Create("Worker " + Name); DrainCompletion = TaskSourceSlim.Create <bool>(); }
public async Task <BondTcpConnection <TProxy> > ConnectAsync( string server, int port, CreateProxyCallback createProxyCallback) { Contract.Requires(server != null); Contract.Requires(server.Length > 0); Contract.Requires(port > 0); Contract.Requires(createProxyCallback != null); m_logger.Debug( "Connecting to {0} with timeout {1} ms", GetServerDescription(server, port), m_options.TimeoutInMs); NetlibConnectionConfig clientConfig = NetlibConnectionConfig.Default; clientConfig.Timeout = m_options.TimeoutInMs == 0 ? SocketConfiguration.InfiniteTimeout : TimeSpan.FromMilliseconds(m_options.TimeoutInMs); var connection = new NetlibConnection(clientConfig) { AlwaysReconnect = m_options.ReconnectAutomatically, }; try { var completionSource = TaskSourceSlim.Create <SocketError>(); connection.ConnectComplete += (sender, status) => completionSource.TrySetResult(status); connection.Disconnected += (sender, status) => m_logger.Debug("Disconnected from {0} ({1})", GetServerDescription(server, port), status); // NetlibConnection uses Dns.GetDnsEntry to resolve even if the server string contains an IP address. // CB uses IPs in the production environment and reverse lookup is not available there. // IP address can also belong to a machine behind a VIP where reverse lookup doesn't make sense. IPAddress ip; if (IPAddress.TryParse(server, out ip)) { connection.ConnectAsync(new IPEndPoint(ip, port)); } else { connection.ConnectAsync(server, port); } var result = await completionSource.Task; return(OnConnectComplete(server, port, createProxyCallback, connection, result)); } catch (Exception) { connection.Dispose(); throw; } }
public async Task <bool> WaitAsync(TimeSpan timeout, CancellationToken token) { if (_order == SemaphoreOrder.NonDeterministic) { // Forcing the same exception to be propagated for non-deterministic case as well. // SemaphoreSlim.WaitAsync throws OperationCanceledException but this method // should throw TaskCanceledException instead. try { return(await _semaphore.WaitAsync(timeout, token)); } catch (OperationCanceledException) when(token.IsCancellationRequested) { throw new TaskCanceledException(); } } var item = TaskSourceSlim.Create <bool>(); var added = _collection.TryAdd(item); Contract.Assert(added, "Since collection is unbounded, it should always be able to add more items."); // Increment collection count _collectionCount.Release(); using var delayCancellation = CancellationTokenSource.CreateLinkedTokenSource(token); var delay = Task.Delay(timeout, delayCancellation.Token); var task = item.Task; if (delay == await Task.WhenAny(delay, task)) { if (token.IsCancellationRequested) { // Was unable to complete because the timeout was actually cancelled via the token passed in to WaitAsync, // not because we actually timed out. item.TrySetCanceled(); } else { // Timed out. item.TrySetResult(false); } } else { // Clean up the delay task. delayCancellation.Cancel(); } return(await task); }
/// <summary> /// Class constructor /// </summary> /// <param name="appLoggingContext">Application-level logging context</param> /// <param name="maxProcesses">the maximum number of concurrent pips on the worker</param> /// <param name="config">Distribution config</param>\ /// <param name="buildId">the build id</param> public WorkerService(LoggingContext appLoggingContext, int maxProcesses, IDistributionConfiguration config, string buildId) { m_appLoggingContext = appLoggingContext; m_maxProcesses = maxProcesses; m_port = config.BuildServicePort; m_services = new DistributionServices(buildId); m_workerServer = new Grpc.GrpcWorkerServer(this, appLoggingContext, buildId); m_attachCompletionSource = TaskSourceSlim.Create <bool>(); m_exitCompletionSource = TaskSourceSlim.Create <bool>(); m_workerRunnablePipObserver = new WorkerRunnablePipObserver(this); m_sendThread = new Thread(SendBuildResults); }
/// <nodoc /> public Plugin(string id, string path, Task pluginTask, CancellationTokenSource cancellationTokenSource, IPluginClient pluginClient) { Contract.RequiresNotNullOrEmpty(id, "plugin id is null"); Contract.RequiresNotNullOrEmpty(path, "plugin path is null"); Contract.RequiresNotNull(pluginTask, "plugin task is null"); Contract.RequiresNotNull(pluginClient, "pluginclient is null"); Id = id; FilePath = path; Name = Path.GetFileName(path); PluginTask = pluginTask; PluginTaskCancellationTokenSource = cancellationTokenSource; PluginClient = pluginClient; Status = PluginStatus.Initialized; m_startCompletionTaskSoure = TaskSourceSlim.Create <Unit>(); }
/// <nodoc /> public Plugin(string id, string path, Process process, IPluginClient pluginClient) { Contract.RequiresNotNullOrEmpty(id, "plugin id is null"); Contract.RequiresNotNullOrEmpty(path, "plugin path is null"); Contract.RequiresNotNull(process, "plugin process is null"); Contract.RequiresNotNull(pluginClient, "pluginclient is null"); Id = id; FilePath = path; Name = Path.GetFileName(path); PluginProcess = process; PluginClient = pluginClient; Status = PluginStatus.Initialized; PluginProcess.Exited += HandleProcessExisted; m_startCompletionTaskSoure = TaskSourceSlim.Create <Unit>(); }
public Task PutIsNotBlockedByPurgeAsync(bool useFile) { var tcs = TaskSourceSlim.Create <bool>(); return(FillSoNextPutTriggersSoftPurgeThenRunTest( async list => { await tcs.Task; return list; }, async(store, hash1, hash2, stream) => { await Task.Run(async() => { await PutAsync(store, stream, useFile); tcs.SetResult(true); }); })); }
/// <nodoc/> public RequestHandler(MultiplexingServer <TClient> parent, TClient client) { m_parent = parent; m_client = client; m_stream = parent.m_connectivityProvider.GetStreamForClient(client); Name = I($"{parent.Name}.RequestHandler({parent.m_connectivityProvider.Describe(client)})"); m_stopRequestedByClient = false; m_requestListener = new GenericServer <Request>( name: Name, config: m_parent.m_requestHandlingConfig, listener: AcceptRequestAsync); m_sendResponseBlock = new ActionBlock <Response>( SendResponseAsync, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1 }); // streaming must be sequential // chain block completions: either one completes the other var continuationDone = TaskSourceSlim.Create <Unit>(); m_requestListener.Completion.ContinueWith(_ => m_sendResponseBlock.Complete()); m_sendResponseBlock.Completion.ContinueWith(async _ => { m_requestListener.Complete(); if (!m_stopRequestedByClient) { var succeeded = await Response.DisconnectResponse.TrySerializeAsync(m_stream); Logger.Verbose("({0}) Sending DisconnectResponse {1}.", Name, succeeded ? "Succeeded" : "Failed"); } Logger.Verbose("({0}) Disconnecting client...", Name); bool ok = TryDisconnectClient(m_client); Logger.Verbose("({0}) Disconnecting client {1}.", Name, ok ? "Succeeded" : "Failed"); continuationDone.SetResult(Unit.Void); }); // set the completion task to be the completion of both listener and sender blocks, as well as the cleanup continuation m_completion = TaskUtilities.SafeWhenAll(new[] { m_requestListener.Completion, m_sendResponseBlock.Completion, continuationDone.Task }); }
/// <summary> /// Stores a serialized AST for future retrieval /// </summary> public async Task SaveAstAsync(AbsolutePath path, ByteContent content) { var rootPath = path.ToString(m_pathTable); var hash = await m_engine.GetFileContentHashAsync(rootPath); var pathToAst = Path.Combine(rootPath, AstFilenameFragment); var fileWithContent = new FileContentWithHash(content, hash, pathToAst); // Need to store a task completion source to be able to await for the save operation to finish var tcs = TaskSourceSlim.Create <object>(); m_saveCompletionTasks[fileWithContent] = tcs; m_filesToSaveQueue.Post(fileWithContent); await tcs.Task; // Now we can remove the item from the dictionary. m_saveCompletionTasks.TryRemove(fileWithContent, out var _); }