Skip to content

Instantly share code, notes, and snippets.

@alx9r
Last active March 6, 2018 23:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alx9r/f81cf64f50a016edda4e92968bdb9c3b to your computer and use it in GitHub Desktop.
Save alx9r/f81cf64f50a016edda4e92968bdb9c3b to your computer and use it in GitHub Desktop.
Attempt at ensuring Dispose() is called on an advanced function's local variable on stop signal.
# Attempt at answering https://stackoverflow.com/questions/46714132
# "How can I ensure Dispose() is called on an advanced function's local variable on stop signal?"
# an IDisposable stub object for testing
class f : System.IDisposable {
Dispose() { Write-Host 'disposed' }
}
function g {
param
(
# The most complex case is when the function is in a pipeline,
# so we accept pipeline input.
[Parameter(ValueFromPipeline)]
$InputObject
)
begin { $f = [f]::new() } # The local IDisposable is created when the pipeline is established.
process {
try
{
# flags to keep track of why finally was run
$success = $false
$caught = $false
$InputObject # output an object to exercise the pipeline downstream
# if we get here, nothing unusual happened downstream
$success = $true
}
catch
{
# we get here if an exception was thrown
$caught = $true
# !!!
# This is bad news. It's possible the exception will be
# handled by an upstream process{} block. The pipeline would
# survive and the next invocation of process{} would occur
# after $f is disposed.
# !!!
$f.Dispose()
# rethrow the exception
throw
}
finally
{
# !!!
# This finally block is not invoked when the PowerShell instance receives
# a stop signal while executing code upstream in the pipeline. In that
# situation cleanup $f.Dispose() is not invoked.
# !!!
if ( -not $success -and -not $caught )
{
# dispose only if finally{} is the only block remaining to run
$f.Dispose()
}
}
}
end {$f.Dispose()}
}
function throws {
param ( [Parameter(ValueFromPipeline)] $InputObject )
process { throw 'something' }
}
function throwsAfter {
param ( [Parameter(ValueFromPipeline)] $InputObject )
process { $InputObject; throw 'something' }
}
function breaks {
param ( [Parameter(ValueFromPipeline)] $InputObject )
process { break }
}
function breaksAfter {
param ( [Parameter(ValueFromPipeline)] $InputObject )
process { $InputObject; break }
}
function continues {
param ( [Parameter(ValueFromPipeline)] $InputObject )
process { continue }
}
function continuesAfter {
param ( [Parameter(ValueFromPipeline)] $InputObject )
process { $InputObject; continue }
}
function TerminateStatement {
[CmdletBinding()]
param
(
[Parameter(ValueFromPipeline)]$x
)
process
{
$PSCmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
'Statement-Terminating Error',
'id',
0,
$null
)
)
}
}
function TerminateStatementAfter {
[CmdletBinding()]
param
(
[Parameter(ValueFromPipeline)]$x
)
process
{
$InputObject
$PSCmdlet.ThrowTerminatingError(
[System.Management.Automation.ErrorRecord]::new(
'Statement-Terminating Error',
'id',
0,
$null
)
)
}
}
function blocks {
param ( [Parameter(ValueFromPipeline)] $InputObject )
process { Wait-Event 'bogus' }
}
function blocksAfter {
param ( [Parameter(ValueFromPipeline)] $InputObject )
process { $InputObject; Wait-Event 'bogus' }
}
'===multiple inputs'
1,2 | g
'===throws'
try
{
1 | g | throws
}
catch{}
'===throwsAfter'
try
{
1 | throwsAfter | g # !!! Dispose never runs
}
catch{}
'===breaks'
do {
1 | g | breaks
} while (1)
'===breakAfter'
do {
1 | breaksAfter | g # !!! Dispose never runs
} while (1)
'===continues'
foreach ( $i in 1)
{
1 | g | continues
Write-Host 'this should get skipped'
}
'===continuesAfter'
foreach ( $i in 1)
{
1 | continuesAfter | g # !!! Dispose never runs
Write-Host 'this should get skipped'
}
'===TerminateStatement'
1 | g | TerminateStatement
'===TerminateStatementAfter'
1 | g | TerminateStatementAfter
'===Ctrl-C'
#1 | g | blocks
'===Ctrl-C after'
1 | blocksAfter | g # !!! Dispose never runs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment