GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fix the NREs when writing to console from multiple threads by kborowinski · Pull Request #25440 · PowerShell/PowerShell

Conversation

@kborowinski

@kborowinski Kris Borowinski (kborowinski) commented Apr 25, 2025

Copy link
Copy Markdown
Contributor

PR Summary

Fix #21021

(Reopening this small PR on a new dev branch after closure due to the GitHub issue confirmed by support.)

This PR adds additional locks to prevent NullReferenceException when writing to the console from multiple threads. By ensuring proper synchronization, it improves the stability and reliability of concurrent console output in PowerShell.

PR Context

The issue #21021 was caused by race conditions where multiple threads attempted to write to the console simultaneously, leading to potential NullReferenceExceptions. This PR introduces additional locking mechanisms to prevent such occurrences and ensure thread-safe console access.

I would like to thank Ilya (@iSazonov) for finding the cause and proposing the solution.

Visuals

On the left with the fix, on the right current state:

Animation

PR Checklist

  • PR has a meaningful title
    • Use the present tense and imperative mood when describing your changes
  • Summarized changes
  • Make sure all .h, .cpp, .cs, .ps1 and .psm1 files have the correct copyright header
  • This PR is ready to merge and is not Work in Progress.
    • If the PR is work in progress, please add the prefix WIP: or [ WIP ] to the beginning of the title (the WIP bot will keep its status check at Pending while the prefix is present) and remove the prefix when the PR is ready.
  • Breaking changes
    • None
    • OR
    • Experimental feature(s) needed
      • Experimental feature name(s):
  • User-facing changes
    • Not Applicable
    • OR
    • Documentation needed
      - [ ] Issue filed:
  • Testing - New and feature
    • N/A or can only be tested interactively
    • OR
    • Make sure you've added a new test if existing tests do not effectively test the code changed
  • Tooling
    • I have considered the user experience from a tooling perspective and don't believe tooling will be impacted.
    • OR
    • I have considered the user experience from a tooling perspective and opened an issue in the relevant tool repository. This may include:
      • Impact on PowerShell Editor Services which is used in the PowerShell extension for VSCode
        (which runs in a different PS Host).
        • Issue filed:
      • Impact on Completions (both in the console and in editors) - one of PowerShell's most powerful features.
        • Issue filed:
      • Impact on PSScriptAnalyzer (which provides linting & formatting in the editor extensions).
        • Issue filed:
      • Impact on EditorSyntax (which provides syntax highlighting with in VSCode, GitHub, and many other editors).
        • Issue filed:

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

0xfeeddeadbeef reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@kborowinski
Fix the NREs when writing to console from multiple threads
7a2381d
@iSazonov Ilya (iSazonov) added the CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log label Apr 25, 2025
iSazonov
Ilya (iSazonov) approved these changes Apr 25, 2025
View reviewed changes
@kborowinski

Kris Borowinski (kborowinski) commented Apr 25, 2025

Copy link
Copy Markdown
Contributor Author

Ilya (@iSazonov) Could you re-add the backport consider labels? Or it does not matter.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@kborowinski Kris Borowinski (kborowinski) mentioned this pull request Apr 25, 2025
Remove NRE workaround and implement progress completion via event handlers thoemmi/7Zip4Powershell#94
Open
@iSazonov

This comment was marked as outdated.

Sign in to view
@azure-pipelines

This comment was marked as outdated.

Sign in to view
@iSazonov Ilya (iSazonov) added WG-Reviewed A Working Group has reviewed this and made a recommendation Backport-7.4.x-Consider Backport-7.5.x-Consider labels Apr 25, 2025
@iSazonov
Ilya (iSazonov) merged commit 658a8e4 into PowerShell:master Apr 25, 2025
@iSazonov

Ilya (iSazonov) commented Apr 25, 2025

Copy link
Copy Markdown
Collaborator

Kris Borowinski (@kborowinski) Thanks for your contribution!

kborowinski reacted with hooray emoji
All reactions
  • 🎉 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@TravisEz13

Travis Plunk (TravisEz13) commented Apr 28, 2025

Copy link
Copy Markdown
Member

@PowerShell/powershell-maintainers triage decision:
For 7.4, I don't think a fix that doesn't affect the actual script results makes the bar for an LTS. The regression risk would be higher than the potential gains here.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@TravisEz13 Travis Plunk (TravisEz13) removed the Backport-7.4.x-Consider label Apr 28, 2025
@kborowinski

Kris Borowinski (kborowinski) commented Apr 28, 2025

Copy link
Copy Markdown
Contributor Author

Travis Plunk (@TravisEz13)

I understand the decision not to backport this PR to the LTS version, but I would like to clarify that the actual script results are affected in PS 7.4.X and up.

I first reported this issue on the 7Zip4PowerShell repo, where the unpacking process was crashing mid-task.
I proposed a PR that was merged to implement a workaround that swallows NREs coming from progress pane:

try {
    worker.Progress.StatusDescription = "Finished";
    worker.Progress.RecordType = ProgressRecordType.Completed;
    WriteProgress(worker.Progress);
} catch (NullReferenceException) {
    // Possible bug in PowerShell 7.4.0 leading to a null reference exception being thrown on ProgressPane completion
    // This is not happening on PowerShell 5.1
}

This issue affects all scripts that write to the console from multiple threads, leading to potential crashes in the LTS version of PowerShell. Affected users are forced to disable the progress bar either globally or per script.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

PowerShell Team Bot (pwshBot) pushed a commit to pwshBot/PowerShell that referenced this pull request May 3, 2025
@kborowinski @pwshBot
Fix the NREs when writing to console from multiple threads (PowerShel…
7c74967
…l#25440)

The WriteImpl() method should always be called within a lock on _instanceLock to ensure thread safety.
@pwshBot PowerShell Team Bot (pwshBot) mentioned this pull request May 3, 2025
[release/v7.5] Fix the NREs when writing to console from multiple threads #25502
Closed
3 of 9 tasks
PowerShell Team Bot (pwshBot) pushed a commit to pwshBot/PowerShell that referenced this pull request May 3, 2025
@kborowinski @pwshBot
Fix the NREs when writing to console from multiple threads (PowerShel…
9a6a638
…l#25440)

The WriteImpl() method should always be called within a lock on _instanceLock to ensure thread safety.
@pwshBot PowerShell Team Bot (pwshBot) mentioned this pull request May 3, 2025
[release/v7.4] Fix the NREs when writing to console from multiple threads #25503
Closed
9 tasks
@TravisEz13

Travis Plunk (TravisEz13) commented May 4, 2025

Copy link
Copy Markdown
Member

Kris Borowinski (@kborowinski) I had the bot open PRs for the backports and added your comments. These still need to be triaged, but we are moving over to a new process that helps us track all the information we need.

kborowinski reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@TravisEz13 Travis Plunk (TravisEz13) added Backport-7.4.x-Migrated Backport-7.5.x-Migrated and removed Backport-7.5.x-Consider labels May 4, 2025
@TravisEz13

Travis Plunk (TravisEz13) commented May 4, 2025

Copy link
Copy Markdown
Member
  1. Kris Borowinski (@kborowinski) It would be good if you know exactly when it was introduced, that will help us with our decision.
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@kborowinski

Kris Borowinski (kborowinski) commented May 4, 2025

Copy link
Copy Markdown
Contributor Author

Travis Plunk (@TravisEz13) In comment on the original issue Ilya (@iSazonov) pinpointed it to PR #13758 PowerShell 7.2.0-preview.2 and #15864 PowerShell 7.2.0-preview.9 when PSAnsiRendering was made stable.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@daxian-dbw

Dongbo Wang (daxian-dbw) commented May 20, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Root Cause

I debugged the code and found that the race condition was caused by ConsoleHostUserInterface.PreWrite calls _progPane?.Hide() without locking the _instanceLock.

The NRE happens because _savedRegion gets set to null while the Hide() method is running. However, _savedRegion is set to null only in the Hide() method, and that means at least two Hide() are running in parallel, which is unexpected.

Looking closely, Hide() has 5 call sites, and 3 of them are guarded by lock (_instanceLock). The other 2 un-guarded call sites are ConsoleHostUserInterface.PreWrite() and ConsoleHostUserInterface.PreRead(). The PreWrite() method gets called in WriteToCosnole method, which is the most low-level method called by all Write-* commands, including Write-Verbose.

The root cause becomes clear give the above -- calling Write-Verbose in the middle of on-going Write-Progress potentially causes the Hide() method to be called in parallel, which corrupts the state of the ProgressPane.

So, the potential problem still exists in CosnoleHostUserInterface.PreRead(), ConsoleHostUserInterface.PostWrite(), and ConsoleHostUserInterface.PostRead(). The latter 2 call _progPane?.Show() without locking _instanceLock.

Thoughts on this fix

The safest and complete fix would be adding lock (_instanceLock) to PreWrite(), PreRead(), PostWrite(), and PostRead(), so as to make all calls to Hide and Show guarded by the same lock.

However, after discussing with Patrick Meinecke (@SeeminglyScience) and thinking about this more, we decide to not make further changes to the original fix. Here is why:

  1. By looking into the code more closely, it seems all Write-* actions (including $host.UI.Write*) that can be triggered by a user go through WriteImpl, which has been guarded by the original fix. Other calls to WriteToCosnole outside of WriteImpl are for reading input, prompting user with choices, and etc., not related to a Write-* action from user. So, it feels to me that guard WriteImpl is sufficient for the cases where a race condition could happen.

  2. Comparatively, even though adding locks to PreWrite and PostWrite would be a safer and more complete fix, it's not really needed practically in cases where WriteToConsole is called because of reading input or prompting users. Furthermore, it will result in acquiring locks twice in the WriteToConsole method, which will make it less performant in general.

  3. Regarding PreRead and PostRead, they are used only when reading input, and it's unlikely to happen while progress rendering is ongoing. So, again, even though adding locks there would be a complete fix, it seems unnecessary practically and will degrade the performance for sure.

kborowinski reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@iSazonov

Ilya (iSazonov) commented May 20, 2025

Copy link
Copy Markdown
Collaborator

The safest and complete fix would be adding lock (_instanceLock) to PreWrite(), PreRead(), PostWrite(), and PostRead(), so as to make call calls to Hide and Show guarded by the same lock.

Dongbo Wang (@daxian-dbw) Thank you for your investigations! The reason I didn't dare to do this was for fear of a deadlock. There are too many combinations. Even a change in one place in the future could potentially open up scenarios impossible today.
Steve and I had a short exchange about this a few years ago with the conclusion to wait for feedback. Now that such report has been received, we have corrected the specific scenario. If there are more complex scenarios with problems, then it may be more correct not to saturate the code with locks, but to change the approach more fundamentally.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@daxian-dbw Dongbo Wang (daxian-dbw) moved this to Done in PowerShell Team Reviews/Investigations May 20, 2025
@daxian-dbw Dongbo Wang (daxian-dbw) added this to PowerShell Team Reviews/Investigations May 20, 2025
@daxian-dbw

Dongbo Wang (daxian-dbw) commented May 20, 2025

Copy link
Copy Markdown
Member

Patrick Meinecke (@SeeminglyScience) did look into the feasibility to make the progress rendering lock-less, using atomic primitives. But it turned out to be a too big effort and too low priority for the team to commit on.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

SIRMARGIN pushed a commit to SIRMARGIN/PowerShell that referenced this pull request Dec 12, 2025
@kborowinski @SIRMARGIN
Fix the NREs when writing to console from multiple threads (PowerShel…
248ac5b
…l#25440)

The WriteImpl() method should always be called within a lock on _instanceLock to ensure thread safety.
@daxian-dbw Dongbo Wang (daxian-dbw) added Backport-7.4.x-Done Backport-7.5.x-Done and removed Backport-7.4.x-Migrated Backport-7.5.x-Migrated labels Apr 9, 2026
Thatgfsj (Thatgfsj) pushed a commit to Thatgfsj-contribs/PowerShell that referenced this pull request Aug 6, 2026
@kborowinski
Fix the NREs when writing to console from multiple threads (PowerShel…
a61025b
…l#25440)

The WriteImpl() method should always be called within a lock on _instanceLock to ensure thread safety.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@iSazonov Ilya (iSazonov) iSazonov approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

Backport-7.4.x-Done Backport-7.5.x-Done CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log WG-Reviewed A Working Group has reviewed this and made a recommendation

Projects

PowerShell Team Reviews/Investigations
Status: Done

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Occasional NullReferenceException in Microsoft.PowerShell.ProgresPane.Hide() when updating multiple progress bars on PS 7.4.0

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@kborowinski @iSazonov @TravisEz13 @daxian-dbw
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.