public void WorkAndSetTcs(Func <CancellationToken, Task <IReadOnlyList <HierarchicalSymbol> > > asyncWork)
        {
            CancellationTokenSource currentCts;
            TaskCompletionSource <IReadOnlyList <HierarchicalSymbol> > currentTcs;

            lock (_syncObj) {
                switch (state)
                {
                case WorkQueueState.Working:
                    CancelExistingWork();
                    RenewTcs();
                    break;

                case WorkQueueState.WaitingForWork:
                    break;

                case WorkQueueState.FinishedWork:
                    RenewTcs();
                    break;

                default:
                    throw new InvalidOperationException();
                }
                state      = WorkQueueState.Working;
                currentCts = _fileCts;
                currentTcs = _fileTcs;
            }

            DoWork(currentCts, asyncWork).SetCompletionResultTo(currentTcs);
        }
        private async Task <IReadOnlyList <HierarchicalSymbol> > DoWork(CancellationTokenSource tcs, Func <CancellationToken, Task <IReadOnlyList <HierarchicalSymbol> > > asyncWork)
        {
            var token = tcs.Token;

            try {
                return(await asyncWork(token));
            } finally {
                lock (_syncObj) {
                    tcs.Dispose();
                    if (!token.IsCancellationRequested)
                    {
                        state = WorkQueueState.FinishedWork;
                    }
                }
            }
        }
        public void MarkAsPending()
        {
            lock (_syncObj) {
                switch (state)
                {
                case WorkQueueState.WaitingForWork:
                    break;

                case WorkQueueState.Working:
                    CancelExistingWork();
                    RenewTcs();
                    break;

                case WorkQueueState.FinishedWork:
                    RenewTcs();
                    break;

                default:
                    throw new InvalidOperationException();
                }
                state = WorkQueueState.WaitingForWork;
            }
        }
        public void Dispose()
        {
            lock (_syncObj) {
                switch (state)
                {
                case WorkQueueState.Working:
                    CancelExistingWork();
                    break;

                case WorkQueueState.WaitingForWork:
                    CancelExistingWork();
                    // Manually cancel tcs, in case any task is awaiting
                    _fileTcs.TrySetCanceled();
                    break;

                case WorkQueueState.FinishedWork:
                    break;

                default:
                    throw new InvalidOperationException();
                }
                state = WorkQueueState.FinishedWork;
            }
        }