public async Task <long> CreateOrRestoreAsync(IPartitionErrorHandler errorHandler, long firstInputQueuePosition) { EventTraceContext.Clear(); this.ErrorHandler = errorHandler; this.TraceHelper.TraceProgress("Starting partition"); await MaxConcurrentStarts.WaitAsync(); // create or restore partition state from last snapshot try { // create the state this.State = ((IStorageProvider)this.host).CreatePartitionState(); // initialize timer for this partition this.PendingTimers = new BatchTimer <PartitionEvent>(this.ErrorHandler.Token, this.TimersFired); // goes to storage to create or restore the partition state this.TraceHelper.TraceProgress("Loading partition state"); var inputQueuePosition = await this.State.CreateOrRestoreAsync(this, this.ErrorHandler, firstInputQueuePosition).ConfigureAwait(false); // start processing the timers this.PendingTimers.Start($"Timer{this.PartitionId:D2}"); // start processing the worker queues this.State.StartProcessing(); this.TraceHelper.TraceProgress($"Started partition, nextInputQueuePosition={inputQueuePosition}"); return(inputQueuePosition); } catch (Exception e) when(!Utils.IsFatal(e)) { this.ErrorHandler.HandleError(nameof(CreateOrRestoreAsync), "Could not start partition", e, true, false); throw; } finally { MaxConcurrentStarts.Release(); } }
async Task Work() { this.Tracer?.Invoke("started"); EventTraceContext.Clear(); int?previousBatch = null; while (!this.cancellationToken.IsCancellationRequested) { int?nextBatch = this.GetNextBatch(); if (previousBatch.HasValue) { this.WorkLoopCompleted(previousBatch.Value, this.stopwatch.Elapsed.TotalMilliseconds, nextBatch); } if (!nextBatch.HasValue) { this.Tracer?.Invoke("no work, starting to shut down"); // no work found. Announce that we are planning to shut down. // Using an interlocked exchange to enforce store-load fence. int read = Interlocked.Exchange(ref this.state, SHUTTINGDOWN); Debug.Assert(read == RUNNING); // because that's what its set to before Work is called // but recheck so we don't miss work that was just added nextBatch = this.GetNextBatch(); if (!nextBatch.HasValue) { // still no work. Try to transition to idle but revert if state has been changed. read = Interlocked.CompareExchange(ref this.state, IDLE, SHUTTINGDOWN); if (read == SHUTTINGDOWN) { // shut down is complete this.Tracer?.Invoke("shut down"); return; } else { // shutdown was reverted by Notify() Debug.Assert(read == RUNNING); this.Tracer?.Invoke("was told to continue"); continue; } } else { // we found more work. So we do not shutdown but go back to running. this.Tracer?.Invoke("found more work after all"); this.state = RUNNING; } } this.Tracer?.Invoke("processing batch"); this.stopwatch.Restart(); try { // recheck for cancellation right before doing work this.cancellationToken.ThrowIfCancellationRequested(); // do the work, calling the virtual method await this.Process(this.batch).ConfigureAwait(false); this.Tracer?.Invoke("notifying waiters"); // notify anyone who is waiting for completion foreach (var w in this.waiters) { w.TrySetResult(true); } } catch (Exception e) { foreach (var w in this.waiters) { w.TrySetException(e); } } this.Tracer?.Invoke("done processing batch"); this.stopwatch.Stop(); previousBatch = this.batch.Count; } }