Skip to content

RunspacePool mutates the PSModulePath #9921

Closed

Description

@rjmholt

From PowerShell/vscode-powershell#2008.

Related to #6850.

When using a RunspacePool (common in hosted and remote contexts), the PSModulePath gets reset whenever a runspace is opened.

This occurs because ModuleIntrinsics sets the module path at constructor time:

internal ModuleIntrinsics(ExecutionContext context)
{
_context = context;
// And initialize the module path...
SetModulePath();
}

as if it were a process-singleton.

Every time a runspace is opened in PowerShell, the whole process gets its PSModulePath reset, which is a problem for things like EditorServices where we use a runspace to run analysis in the background, but users expect to be able to modify the PSModulePath in the IntegratedConsole.

Activity

  1. added
    Issue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a
    on Jun 18, 2019
  2. alx9r commented on Jun 28, 2019

    @alx9r

    I just ran into this too. Here is a repro:

    Repro

    $userPath = '\some\path'
    $ENV:PSModulePath = $ENV:PSModulePath+";$userPath"
    $ENV:PSModulePath.Split(';')[-1] -eq $userPath
    $runspace = [runspacefactory]::CreateRunspace()
    $ENV:PSModulePath.Split(';')[-1] -eq $userPath
    $runspace.Open()
    $ENV:PSModulePath.Split(';')[-1] -eq $userPath

    Expected Behavior

    true
    true
    true
    

    Actual Behavior

    true
    true
    false
    
  3. alx9r commented on Jun 28, 2019

    @alx9r

    Rob Holt (@rjmholt) Do you think I should expect that, in the long term, each runspace will have its own instance of PSModulePath? Or is there some competing interest that favors a single process-wide PSModulePath?

  4. rjmholt commented on Jun 28, 2019

    @rjmholt
    CollaboratorAuthor

    in the long term, each runspace will have its own instance of PSModulePath? Or is there some competing interest that favors a single process-wide PSModulePath?

    That's a very good question, and one I think needs more design thought.

    My thinking is:

    • The PSModulePath has always been an env var and in PS 6+ can be configured in the powershell.config.json file. The first is process-wide, the second is PowerShell-install-wide (i.e. even wider, since all PowerShell processes started from that pwsh.exe will have that PSModulePath)
    • Changing that would be a pretty big breaking change, so undesirable
    • But as an env var, different runspaces shouldn't reset it -- it should be initialized once, possibly by the default runspace
    • Runspace pool implementors should only mutate PSModulePath with care, and ideally just load modules directly anyway
  5. alx9r commented on Jun 28, 2019

    @alx9r

    Those are all good points. I'll advocate for each runspace having its own PSModulePath for a bit:

    • user code can mutate $PSModulePath
    • such user code could be invoked from any runspace within the process
    • when mutation of PSModulePath occurs in one runspace it affects the PSModulePath visible from other runspaces
    • runspaces can run concurrently so errors resulting from the mutation will be difficult to reason about

    This suggests to me that the process-wide PSModulePath singleton should continue to exist, but access to it from each runspace should be copy-on-write. That way each runspace sees the same, correct, initial value, but mutation from that runspace does not affect other runspaces.

  6. vexx32 commented on Jun 28, 2019

    @vexx32
    Collaborator

    That does sound like the best solution, and would be mimicking PS's own scope behaviour of variables as well, so would generally be intuitively understood reasonably well I think.

  7. iSazonov commented on Jun 29, 2019

    @iSazonov
    Collaborator

    The Issue will be resolver with #9957. In short PowerShell Committee conclusion is that PowerShell Core will do not modify $env:PSModulePath.

  8. alx9r commented on Jun 29, 2019

    @alx9r

    @isazanov Can you elaborate on how #9957 will resolve this issue? #9957 seems to only contemplate the management of PSModulePath across process boundaries whereas this issue is about the management of PSModulePath across runspace boundaries within a process.

  9. alx9r commented on Jun 29, 2019

    @alx9r

    I'm seeing this problem manifest in my implementation of Invoke-Parallel. For others facing this, here is a summary of the least-bad still-a-half-measure workaround I've found:

    • Cache the value of PSModulePath prior to opening any other runspaces (henceforth startingPSModulePath). Note that at least the Runspace, RunspacePool, and PowerShell classes can lead to opening a runspace, so it can be a bit of an undertaking to root out all such calls.
    • Immediately overwrite PSModulePath with startingPSModulePath each time a runspace is opened.
    • There will still be a (usually short) period when PSModulePath is in the reset state while the runspace is being opened, but before the overwrite has a chance to occur.
    • If a call to Import-Module (or a second call to Runspace.Open() for that matter) coincides with that period then module importing can fail.
    • The failed module importing manifests intermittently because it depends on the coincidental timing of opening runspaces and importing modules.
    • This dramatically reduces the occurrence of failed module imports in multiple runspace scenarios which means development on such projects can continue. But such failures are still a statistical certainty with a likelihood that is probably higher than what most organizations can tolerate for production.
  10. iSazonov commented on Jun 29, 2019

    @iSazonov
    Collaborator

    alx9r My current plan is to implement a global cache s_psModulePath. We will only read $env:PSModulePath, not reset. I think it will resolve the issue and some others.
    There can be an edge case - runspace needs to add/set a private path(s). I guess this is incredible. But if it appears, there are several ways to solve it.

  11. alx9r commented on Jun 29, 2019

    @alx9r

    My current plan is to implement a global cache s_psModulePath. We will only read $env:PSModulePath, not reset. I think it will resolve the issue and some others.

    Ilya (@iSazonov) OK I think understand now. Thank you.

    There can be an edge case - runspace needs to add/set a private path(s). I guess this is incredible. But if it appears, there are several ways to solve it.

    I expect that issue will appear. Currently $ENV:PSModulePath is mutable, so any user code could mutate it. I've certainly encountered such code. It will be very easy to unknowingly invoke such code in a multi-threaded runspace scenario. But I suppose that's a different issue from this one. ☺️

  12. rjmholt commented on Dec 10, 2019

    @rjmholt
    CollaboratorAuthor

    I'm not sure why this issue was closed in #11057, since the code still indicates that instantiating a new ModuleInstrinsics object resets the PSModulePath:

    internal ModuleIntrinsics(ExecutionContext context)
    {
    _context = context;
    // And initialize the module path...
    SetModulePath();
    }

  13. 8 remaining items

  14. daxian-dbw commented on Dec 12, 2019

    @daxian-dbw
    Member

    Ilya (@iSazonov) What you proposed was a too big breaking change that we cannot afford to accept.

  15. iSazonov commented on Dec 13, 2019

    @iSazonov
    Collaborator

    Dongbo Wang (@daxian-dbw) I did not understand why this is "a too big breaking change" because no one indicated which applications will not work. It looks like a habit :-).
    I see only one real scenario where we could populate PSModulePath env - every time we run subprocess with Start.Process() we can add actual PSModulePath env by ProcessInfo.

  16. ghost closed this as completedon Dec 14, 2019
  17. iSazonov commented on Feb 1, 2020

    @iSazonov
    Collaborator

    The issue from vscode-powershell repo obviously says that we shouldn't write PSModulePath back, PowerShell must only read PSModulePath but not change.

  18. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  19. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  20. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

  21. microsoft-github-policy-service commented on Nov 23, 2023

    @microsoft-github-policy-service
    Contributor

    This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get aResolution-No ActivityIssue has had no activity for 6 months or moreWG-Enginecore PowerShell engine, interpreter, and runtime

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions