Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix the NREs when writing to console from multiple threads#25440

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
iSazonov merged 1 commit intoPowerShell:masterfromkborowinski:fix-console-nre
Apr 25, 2025

Conversation

@kborowinski
Copy link
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 preventNullReferenceException 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 potentialNullReferenceExceptions. This PR introduces additional locking mechanisms to prevent such occurrences and ensure thread-safe console access.

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

Visuals

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

Animation

PR Checklist

0xfeeddeadbeef reacted with thumbs up emoji
@iSazonoviSazonov added the CL-GeneralIndicates that a PR should be marked as a general cmdlet change in the Change Log labelApr 25, 2025
@kborowinski
Copy link
ContributorAuthor

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

@iSazonov

This comment was marked as outdated.

@azure-pipelines

This comment was marked as outdated.

@iSazonoviSazonov added WG-ReviewedA Working Group has reviewed this and made a recommendation BackPort-7.4.x-Consider BackPort-7.5.x-Consider labelsApr 25, 2025
@iSazonoviSazonov merged commit658a8e4 intoPowerShell:masterApr 25, 2025
39 of 40 checks passed
@iSazonov
Copy link
Collaborator

@kborowinski Thanks for your contribution!

kborowinski reacted with hooray emoji

@TravisEz13
Copy link
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.

@kborowinski
Copy link
ContributorAuthor

@TravisEz13

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

Ifirst reported this issue on the7Zip4PowerShell repo, where the unpacking process was crashing mid-task.
I proposed a PR that wasmerged 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.

pwshBot pushed a commit to pwshBot/PowerShell that referenced this pull requestMay 3, 2025
…l#25440)The WriteImpl() method should always be called within a lock on _instanceLock to ensure thread safety.
pwshBot pushed a commit to pwshBot/PowerShell that referenced this pull requestMay 3, 2025
…l#25440)The WriteImpl() method should always be called within a lock on _instanceLock to ensure thread safety.
@TravisEz13
Copy link
Member

@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

@TravisEz13
Copy link
Member

  1. @kborowinski It would be good if you know exactly when it was introduced, that will help us with our decision.

@kborowinski
Copy link
ContributorAuthor

@TravisEz13 In comment on the original issue@iSazonovpinpointed it to PR#13758PowerShell 7.2.0-preview.2 and#15864PowerShell 7.2.0-preview.9 whenPSAnsiRendering was made stable.

@daxian-dbw
Copy link
Member

daxian-dbw commentedMay 20, 2025
edited
Loading

Root Cause

I debugged the code and found that the race condition was caused byConsoleHostUserInterface.PreWrite calls_progPane?.Hide() without locking the_instanceLock.

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

Looking closely,Hide() has 5 call sites, and 3 of them are guarded bylock (_instanceLock). The other 2 un-guarded call sites areConsoleHostUserInterface.PreWrite() andConsoleHostUserInterface.PreRead(). ThePreWrite() method gets called inWriteToCosnole method, which is the most low-level method called by allWrite-* commands, includingWrite-Verbose.

The root cause becomes clear give the above -- callingWrite-Verbose in the middle of on-goingWrite-Progress potentially causes theHide() method to be called in parallel, which corrupts the state of theProgressPane.

So, the potential problem still exists inCosnoleHostUserInterface.PreRead(),ConsoleHostUserInterface.PostWrite(), andConsoleHostUserInterface.PostRead(). The latter 2 call_progPane?.Show() without locking_instanceLock.

Thoughts on this fix

The safest and complete fix would be addinglock (_instanceLock) toPreWrite(),PreRead(),PostWrite(), andPostRead(), so as to make all calls toHide andShow guarded by the same lock.

However, after discussing with@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 allWrite-* actions (including$host.UI.Write*) that can be triggered by a user go throughWriteImpl, which has been guarded by the original fix. Other calls toWriteToCosnole outside ofWriteImpl are for reading input, prompting user with choices, and etc., not related to aWrite-* action from user. So, it feels to me that guardWriteImpl is sufficient for the cases where a race condition could happen.

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

  3. RegardingPreRead andPostRead, 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

@iSazonov
Copy link
Collaborator

The safest and complete fix would be addinglock (_instanceLock) toPreWrite(),PreRead(),PostWrite(), andPostRead(), so as to make call calls toHide andShow guarded by the same lock.

@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.

@daxian-dbw
Copy link
Member

@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.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@iSazonoviSazonoviSazonov approved these changes

Assignees

No one assigned

Labels

Backport-7.4.x-MigratedBackport-7.5.x-MigratedCL-GeneralIndicates that a PR should be marked as a general cmdlet change in the Change LogWG-ReviewedA Working Group has reviewed this and made a recommendation

Projects

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

4 participants

@kborowinski@iSazonov@TravisEz13@daxian-dbw

[8]ページ先頭

©2009-2025 Movatter.jp