@@ -78,6 +78,7 @@ public interface IExecutionContext : IRunnerService
7878 List < string > StepEnvironmentOverrides { get ; }
7979
8080 ExecutionContext Root { get ; }
81+ ExecutionContext Parent { get ; }
8182
8283 // Initialize
8384 void InitializeJob ( Pipelines . AgentJobRequestMessage message , CancellationToken token ) ;
@@ -264,6 +265,14 @@ public ExecutionContext Root
264265 }
265266 }
266267
268+ public ExecutionContext Parent
269+ {
270+ get
271+ {
272+ return _parentExecutionContext ;
273+ }
274+ }
275+
267276 public JobContext JobContext
268277 {
269278 get
@@ -406,7 +415,7 @@ public IExecutionContext CreateChild(
406415
407416 /// <summary>
408417 /// An embedded execution context shares the same record ID, record name, logger,
409- /// and a linked cancellation token.
418+ /// but NOT the cancellation token (just like workflow steps contexts - they don't share a token)
410419 /// </summary>
411420 public IExecutionContext CreateEmbeddedChild (
412421 string scopeName ,
@@ -416,7 +425,7 @@ public IExecutionContext CreateEmbeddedChild(
416425 Dictionary < string , string > intraActionState = null ,
417426 string siblingScopeName = null )
418427 {
419- return Root . CreateChild ( _record . Id , _record . Name , _record . Id . ToString ( "N" ) , scopeName , contextName , stage , logger : _logger , isEmbedded : true , cancellationTokenSource : CancellationTokenSource . CreateLinkedTokenSource ( _cancellationTokenSource . Token ) , intraActionState : intraActionState , embeddedId : embeddedId , siblingScopeName : siblingScopeName ) ;
428+ return Root . CreateChild ( _record . Id , _record . Name , _record . Id . ToString ( "N" ) , scopeName , contextName , stage , logger : _logger , isEmbedded : true , cancellationTokenSource : null , intraActionState : intraActionState , embeddedId : embeddedId , siblingScopeName : siblingScopeName ) ;
420429 }
421430
422431 public void Start ( string currentOperation = null )
@@ -597,7 +606,31 @@ public void SetTimeout(TimeSpan? timeout)
597606 if ( timeout != null )
598607 {
599608 _cancellationTokenSource . CancelAfter ( timeout . Value ) ;
609+ m_timeoutStartedAt = DateTime . UtcNow ;
610+ m_timeout = timeout . Value ;
611+ }
612+ }
613+
614+ DateTime ? m_timeoutStartedAt ;
615+ TimeSpan ? m_timeout ;
616+ public TimeSpan ? GetRemainingTimeout ( )
617+ {
618+ if ( m_timeoutStartedAt != null && m_timeout != null )
619+ {
620+ var elapsedSinceTimeoutSet = DateTime . UtcNow - m_timeoutStartedAt . Value ;
621+ var remainingTimeout = m_timeout . Value - elapsedSinceTimeoutSet ;
622+ if ( remainingTimeout . Ticks > 0 )
623+ {
624+ return remainingTimeout ;
625+ }
626+ else
627+ {
628+ // there was a timeout and it has expired
629+ return TimeSpan . Zero ;
630+ }
600631 }
632+ // no timeout was ever set
633+ return null ;
601634 }
602635
603636 public void Progress ( int percentage , string currentOperation = null )
@@ -1453,4 +1486,4 @@ public static class WellKnownTags
14531486 public static readonly string Notice = "##[notice]" ;
14541487 public static readonly string Debug = "##[debug]" ;
14551488 }
1456- }
1489+ }
0 commit comments