Private/Timers.ps1

function Find-PodeTimer {
    param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Name
    )

    return $PodeContext.Timers.Items[$Name]
}

function Test-PodeTimersExist {
    return (($null -ne $PodeContext.Timers) -and (($PodeContext.Timers.Enabled) -or ($PodeContext.Timers.Items.Count -gt 0)))
}

function Start-PodeTimerRunspace {
    if (!(Test-PodeTimersExist)) {
        return
    }

    $script = {
        try {

            while (!$PodeContext.Tokens.Cancellation.IsCancellationRequested) {
                try {
                    $_now = [DateTime]::Now

                    # only run timers that haven't completed, and have a next trigger in the past
                    foreach ($timer in $PodeContext.Timers.Items.Values) {
                        if ($timer.Completed -or (!$timer.OnStart -and ($timer.NextTriggerTime -gt $_now))) {
                            continue
                        }

                        try {
                            $timer.OnStart = $false
                            $timer.Count++

                            # set last trigger to current next trigger
                            if ($null -ne $timer.NextTriggerTime) {
                                $timer.LastTriggerTime = $timer.NextTriggerTime
                            }
                            else {
                                $timer.LastTriggerTime = $_now
                            }

                            # has the timer completed?
                            if (($timer.Limit -gt 0) -and ($timer.Count -ge $timer.Limit)) {
                                $timer.Completed = $true
                            }

                            # next trigger
                            if (!$timer.Completed) {
                                $timer.NextTriggerTime = $_now.AddSeconds($timer.Interval)
                            }
                            else {
                                $timer.NextTriggerTime = $null
                            }

                            # run the timer
                            Invoke-PodeInternalTimer -Timer $timer
                        }
                        catch {
                            $_ | Write-PodeErrorLog
                        }
                    }

                    Start-Sleep -Seconds 1
                }
                catch {
                    $_ | Write-PodeErrorLog
                }
            }
        }
        catch [System.OperationCanceledException] {
            $_ | Write-PodeErrorLog -Level Debug
        }
        catch {
            $_ | Write-PodeErrorLog
            throw $_.Exception
        }
    }

    Add-PodeRunspace -Type Timers -Name 'Scheduler' -ScriptBlock $script
}

function Invoke-PodeInternalTimer {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
    param(
        [Parameter(Mandatory = $true)]
        $Timer,

        [Parameter()]
        [object[]]
        $ArgumentList = $null
    )

    try {
        $TimerEvent = @{
            Lockable  = $PodeContext.Threading.Lockables.Global
            Sender    = $Timer
            Timestamp = [DateTime]::UtcNow
            Metadata  = @{}
        }

        # add main timer args
        $_args = @()
        if (($null -ne $Timer.Arguments) -and ($Timer.Arguments.Length -gt 0)) {
            $_args += $Timer.Arguments
        }

        # add adhoc timer invoke args
        if (($null -ne $ArgumentList) -and ($ArgumentList.Length -gt 0)) {
            $_args += $ArgumentList
        }

        # invoke timer
        Invoke-PodeScriptBlock -ScriptBlock $Timer.Script.GetNewClosure() -Arguments $_args -UsingVariables $Timer.UsingVariables -Scoped -Splat -NoNewClosure
    }
    catch {
        $_ | Write-PodeErrorLog
    }
    finally {
        Invoke-PodeGC
    }
}