/// <summary>
        /// Called at the end of a prompt loop to take down any progress display that might have appeared and purge any
        /// outstanding progress activity state.
        /// </summary>
        internal void ResetProgress()
        {
            // If we have multiple runspaces on the host then any finished pipeline in any runspace will lead to call 'ResetProgress'
            // so we need the lock.
            lock (_instanceLock)
            {
                if (_progPaneUpdateTimer is not null)
                {
                    // Stop update a progress pane and destroy the timer.
                    _progPaneUpdateTimer.Dispose();
                    _progPaneUpdateTimer = null;
                }

                // We don't set 'progPaneUpdateFlag = ToNotRender' here, because:
                // 1. According to MSDN, the timer callback can occur after the Dispose() method has been called.
                //    So we cannot guarantee the flag is truly set to 'ToNotRender'.
                // 2. When creating a new timer in 'HandleIncomingProgressRecord', we will set the flag to 'ToRender' anyways.
                if (_progPane is not null)
                {
                    _progPane.Hide();
                    _progPane = null;
                }

                _pendingProgress = null;
            }
        }
        public override void WriteProgress(long sourceId, ProgressRecord record)
        {
            if (record is null)
            {
                throw new ArgumentNullException(nameof(record));
            }

            lock (_instanceLock)
            {
                if (_pendingProgress is null)
                {
                    Debug.Assert(_progPane is null, "If there is no data struct, there shouldn't be a pane, either.");
                    _pendingProgress = new PendingProgress();
                }

                _pendingProgress.Update(sourceId, record);

                if (_progPane is null)
                {
                    // This is the first time we've received a progress record, so
                    //  - create a progress pane,
                    //  - set up a update flag
                    //  - create a timer for updating the flag
                    _progPane = new ProgressPane(this);

                    if (_progPaneUpdateTimer is null)
                    {
                        // Show a progress pane at the first time we've received a progress record
                        progPaneUpdateFlag = ToRender;

                        // The timer will be auto restarted every 'UpdateTimerThreshold' ms
                        _progPaneUpdateTimer = new Timer(new TimerCallback(ProgressPaneUpdateTimerElapsed), null, UpdateTimerThreshold, UpdateTimerThreshold);
                    }
                }

                if (Interlocked.CompareExchange(ref progPaneUpdateFlag, ToNotRender, ToRender) == ToRender ||
                    record.RecordType == ProgressRecordType.Completed)
                {
                    // Update the progress pane only when the timer set up the update flag or WriteProgress is completed.
                    // As a result, we do not block WriteProgress and whole script and eliminate unnecessary console locks and updates.
                    _progPane.Show(_pendingProgress);
                }
            }
        }