Skip to content

Commit c364721

Browse files
(GH-10818) Replace NoRunspaceAffinity docs (#10819)
In #10615, the section for the `NoRunspaceAfinnity` attribute was erroneously deleted as part of the reorganization of the about_Classes article. This change: - Adds back the documentation and examples for the `NoRunspaceAffinity` to the 7.4 and 7.5 versions of about_Classes, as well as a note about the default behavior in the general limitations section. - Adds a section and example showing the behavior for corrupted runspaces due to class runspace affinity in the 7.2 and 7.3 versions, and updates the limitations section. - Resolves #10818 - Fixes AB#202807
1 parent ad92a6b commit c364721

File tree

4 files changed

+286
-4
lines changed

4 files changed

+286
-4
lines changed

reference/7.2/Microsoft.PowerShell.Core/About/about_Classes.md

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: Describes how you can use classes to create your own custom types.
33
Locale: en-US
4-
ms.date: 01/19/2024
4+
ms.date: 01/23/2024
55
online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-7.2&WT.mc_id=ps-gethelp
66
schema: 2.0.0
77
title: about Classes
@@ -359,6 +359,37 @@ Line |
359359
| Book 'The Hobbit by J.R.R. Tolkien (1937)' already in list
360360
```
361361

362+
### Example 4 - Parallel execution corrupting a runspace
363+
364+
The `ShowRunspaceId()` method of `[UnsafeClass]` reports different thread Ids
365+
but the same runspace ID. Eventually, the session state is corrupted causing
366+
an error, such as `Global scope cannot be removed`.
367+
368+
```powershell
369+
# Class definition with Runspace affinity (default behavior)
370+
class UnsafeClass {
371+
static [object] ShowRunspaceId($val) {
372+
return [PSCustomObject]@{
373+
ThreadId = [Threading.Thread]::CurrentThread.ManagedThreadId
374+
RunspaceId = [runspace]::DefaultRunspace.Id
375+
}
376+
}
377+
}
378+
379+
$unsafe = [UnsafeClass]::new()
380+
381+
while ($true) {
382+
1..10 | ForEach-Object -Parallel {
383+
Start-Sleep -ms 100
384+
($using:unsafe)::ShowRunspaceId($_)
385+
}
386+
}
387+
```
388+
389+
> [!NOTE]
390+
> This example runs in an infinite loop. Enter <kbd>Ctrl</kbd>+<kbd>C</kbd> to
391+
> stop the execution.
392+
362393
## Class properties
363394

364395
Properties are variables declared in the class scope. A property can be of any
@@ -452,6 +483,21 @@ For more information about deriving classes that inherit from a base class or
452483
implement interfaces, see
453484
[about_Classes_Inheritance][11].
454485

486+
## Runspace affinity
487+
488+
A runspace is the operating environment for the commands invoked by PowerShell.
489+
This environment includes the commands and data that are currently present, and
490+
any language restrictions that currently apply.
491+
492+
A PowerShell class is affiliated with the **Runspace** where it's
493+
created. Using a PowerShell class in `ForEach-Object -Parallel` isn't safe.
494+
Method invocations on the class are marshalled back to the **Runspace** where
495+
it was created, which can corrupt the state of the **Runspace** or cause a
496+
deadlock.
497+
498+
For an illustration of a how runspace affinity can cause errors, see
499+
[Example 4](#example-4---parallel-execution-corrupting-a-runspace).
500+
455501
## Exporting classes with type accelerators
456502

457503
By default, PowerShell modules don't automatically export classes and
@@ -585,6 +631,12 @@ workaround for those limitations, if any.
585631
- The `hidden` and `static` keywords only apply to class members, not a class
586632
definition.
587633

634+
Workaround: None.
635+
- PowerShell classes aren't safe to use in parallel execution across runspaces.
636+
When you Invoke methods on a class, PowerShell marshalls the invocations back
637+
to the **Runspace** where the class was created, which can corrupt the state
638+
of the **Runspace** or cause a deadlock.
639+
588640
Workaround: None.
589641

590642
### Constructor limitations

reference/7.3/Microsoft.PowerShell.Core/About/about_Classes.md

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: Describes how you can use classes to create your own custom types.
33
Locale: en-US
4-
ms.date: 01/19/2024
4+
ms.date: 01/23/2024
55
online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-7.3&WT.mc_id=ps-gethelp
66
schema: 2.0.0
77
title: about Classes
@@ -359,6 +359,37 @@ Line |
359359
| Book 'The Hobbit by J.R.R. Tolkien (1937)' already in list
360360
```
361361

362+
### Example 4 - Parallel execution corrupting a runspace
363+
364+
The `ShowRunspaceId()` method of `[UnsafeClass]` reports different thread Ids
365+
but the same runspace ID. Eventually, the session state is corrupted causing
366+
an error, such as `Global scope cannot be removed`.
367+
368+
```powershell
369+
# Class definition with Runspace affinity (default behavior)
370+
class UnsafeClass {
371+
static [object] ShowRunspaceId($val) {
372+
return [PSCustomObject]@{
373+
ThreadId = [Threading.Thread]::CurrentThread.ManagedThreadId
374+
RunspaceId = [runspace]::DefaultRunspace.Id
375+
}
376+
}
377+
}
378+
379+
$unsafe = [UnsafeClass]::new()
380+
381+
while ($true) {
382+
1..10 | ForEach-Object -Parallel {
383+
Start-Sleep -ms 100
384+
($using:unsafe)::ShowRunspaceId($_)
385+
}
386+
}
387+
```
388+
389+
> [!NOTE]
390+
> This example runs in an infinite loop. Enter <kbd>Ctrl</kbd>+<kbd>C</kbd> to
391+
> stop the execution.
392+
362393
## Class properties
363394

364395
Properties are variables declared in the class scope. A property can be of any
@@ -452,6 +483,21 @@ For more information about deriving classes that inherit from a base class or
452483
implement interfaces, see
453484
[about_Classes_Inheritance][11].
454485

486+
## Runspace affinity
487+
488+
A runspace is the operating environment for the commands invoked by PowerShell.
489+
This environment includes the commands and data that are currently present, and
490+
any language restrictions that currently apply.
491+
492+
A PowerShell class is affiliated with the **Runspace** where it's
493+
created. Using a PowerShell class in `ForEach-Object -Parallel` isn't safe.
494+
Method invocations on the class are marshalled back to the **Runspace** where
495+
it was created, which can corrupt the state of the **Runspace** or cause a
496+
deadlock.
497+
498+
For an illustration of a how runspace affinity can cause errors, see
499+
[Example 4](#example-4---parallel-execution-corrupting-a-runspace).
500+
455501
## Exporting classes with type accelerators
456502

457503
By default, PowerShell modules don't automatically export classes and
@@ -585,6 +631,12 @@ workaround for those limitations, if any.
585631
- The `hidden` and `static` keywords only apply to class members, not a class
586632
definition.
587633

634+
Workaround: None.
635+
- PowerShell classes aren't safe to use in parallel execution across runspaces.
636+
When you Invoke methods on a class, PowerShell marshalls the invocations back
637+
to the **Runspace** where the class was created, which can corrupt the state
638+
of the **Runspace** or cause a deadlock.
639+
588640
Workaround: None.
589641

590642
### Constructor limitations

reference/7.4/Microsoft.PowerShell.Core/About/about_Classes.md

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: Describes how you can use classes to create your own custom types.
33
Locale: en-US
4-
ms.date: 01/19/2024
4+
ms.date: 01/23/2024
55
online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_classes?view=powershell-7.4&WT.mc_id=ps-gethelp
66
schema: 2.0.0
77
title: about Classes
@@ -359,6 +359,66 @@ Line |
359359
| Book 'The Hobbit by J.R.R. Tolkien (1937)' already in list
360360
```
361361

362+
### Example 4 - Class definition with and without Runspace affinity
363+
364+
The `ShowRunspaceId()` method of `[UnsafeClass]` reports different thread Ids
365+
but the same runspace ID. Eventually, the session state is corrupted causing
366+
an error, such as `Global scope cannot be removed`.
367+
368+
```powershell
369+
# Class definition with Runspace affinity (default behavior)
370+
class UnsafeClass {
371+
static [object] ShowRunspaceId($val) {
372+
return [PSCustomObject]@{
373+
ThreadId = [Threading.Thread]::CurrentThread.ManagedThreadId
374+
RunspaceId = [runspace]::DefaultRunspace.Id
375+
}
376+
}
377+
}
378+
379+
$unsafe = [UnsafeClass]::new()
380+
381+
while ($true) {
382+
1..10 | ForEach-Object -Parallel {
383+
Start-Sleep -ms 100
384+
($using:unsafe)::ShowRunspaceId($_)
385+
}
386+
}
387+
```
388+
389+
> [!NOTE]
390+
> This example runs in an infinite loop. Enter <kbd>Ctrl</kbd>+<kbd>C</kbd> to
391+
> stop the execution.
392+
393+
The `ShowRunspaceId()` method of `[SafeClass]` reports different thread and
394+
Runspace ids.
395+
396+
```powershell
397+
# Class definition with NoRunspaceAffinity attribute
398+
[NoRunspaceAffinity()]
399+
class SafeClass {
400+
static [object] ShowRunspaceId($val) {
401+
return [PSCustomObject]@{
402+
ThreadId = [Threading.Thread]::CurrentThread.ManagedThreadId
403+
RunspaceId = [runspace]::DefaultRunspace.Id
404+
}
405+
}
406+
}
407+
408+
$safe = [SafeClass]::new()
409+
410+
while ($true) {
411+
1..10 | ForEach-Object -Parallel {
412+
Start-Sleep -ms 100
413+
($using:safe)::ShowRunspaceId($_)
414+
}
415+
}
416+
```
417+
418+
> [!NOTE]
419+
> This example runs in an infinite loop. Enter <kbd>Ctrl</kbd>+<kbd>C</kbd> to
420+
> stop the execution.
421+
362422
## Class properties
363423

364424
Properties are variables declared in the class scope. A property can be of any
@@ -452,6 +512,29 @@ For more information about deriving classes that inherit from a base class or
452512
implement interfaces, see
453513
[about_Classes_Inheritance][11].
454514

515+
## NoRunspaceAffinity attribute
516+
517+
A runspace is the operating environment for the commands invoked by PowerShell.
518+
This environment includes the commands and data that are currently present, and
519+
any language restrictions that currently apply.
520+
521+
By default, a PowerShell class is affiliated with the **Runspace** where it's
522+
created. Using a PowerShell class in `ForEach-Object -Parallel` isn't safe.
523+
Method invocations on the class are marshalled back to the **Runspace** where
524+
it was created, which can corrupt the state of the **Runspace** or cause a
525+
deadlock.
526+
527+
Adding the `NoRunspaceAffinity` attribute to the class definition ensures that
528+
the PowerShell class isn't affiliated with a particular runspace. Method
529+
invocations, both instance and static, use the **Runspace** of the running
530+
thread and the thread's current session state.
531+
532+
The attribute was added in PowerShell 7.4.
533+
534+
For an illustration of the difference in behavior for classes with and without
535+
the `NoRunspaceAffinity` attribute, see
536+
[Example 4](#example-4---class-definition-with-and-without-runspace-affinity).
537+
455538
## Exporting classes with type accelerators
456539

457540
By default, PowerShell modules don't automatically export classes and
@@ -586,6 +669,12 @@ workaround for those limitations, if any.
586669
definition.
587670

588671
Workaround: None.
672+
- By default, PowerShell classes aren't safe to use in parallel execution
673+
across runspaces. When you Invoke methods on a class, PowerShell marshalls
674+
the invocations back to the **Runspace** where the class was created, which
675+
can corrupt the state of the **Runspace** or cause a deadlock.
676+
677+
Workaround: Add the `NoRunspaceAffinity` attribute to the class declaration.
589678

590679
### Constructor limitations
591680

0 commit comments

Comments
 (0)