§
    øžyjÇ?  ã                  ó²  — d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlmZmZ ddlmZ ddlmZmZmZmZ ddlmZ ddlmZ dd	lmZ  ej        e¦  «        Zd
ZdZdZdZ dZ!dZ"dZ#dZ$ G d„ d¦  «        Z% G d„ d¦  «        Z&efdJd„Z'e e!e"edœdKd"„Z(dLd$„Z)dMdNd'„Z*dMdNd(„Z+ddddd)œdOd0„Z,ed1œdPd4„Z-dQd9„Z.ddd:dd;d<œdRdD„Z/eddddEœdSdI„Z0dS )TuÑ  Out-of-loop shutdown and event-loop liveness backstops (#66892, #69089).

When the asyncio loop freezes mid-drain, every asyncio-based recovery path is
structurally unable to fire: the drain deadline, status rewrites, and forensics
all need the same loop that is stuck. launchd/systemd KeepAlive only restarts a
*dead* process, so a wedged-but-alive gateway sits as a zombie until manual
SIGKILL.

This module provides:

1. A plain OS-thread shutdown watchdog armed at ``stop()``. If shutdown has not
   completed within ``restart_drain_timeout + grace``, it dumps all-thread
   stacks via ``faulthandler`` plus a metadata snapshot, then ``os._exit`` so
   the service manager can revive the process.
2. An event-loop heartbeat file at ``<HERMES_HOME>/state/gateway.heartbeat`` so
   external supervision can distinguish "process alive" from "loop frozen"
   (``gateway_state.json`` alone can't â€” it only rewrites on transitions/turns).
3. A lifetime thread watchdog that can still diagnose and hard-exit when the
   event loop is too frozen to run its own heartbeat or timeout callbacks.
4. A self-rescheduling floor timer that keeps the loop selector's timeout
   finite, giving existing async recovery tasks a chance to resume.
é    )ÚannotationsN)ÚdatetimeÚtimezone)ÚPath)ÚAnyÚCallableÚDictÚOptional)Ú!GATEWAY_SERVICE_RESTART_EXIT_CODE)Úget_hermes_home)Úatomic_json_writeg      N@g      >@g      @g      $@é   )Ústatezgateway.heartbeat)Úlogszgateway-shutdown-watchdog.logc                  ó2   — e Zd ZdZdd„Zdd	„Zdd
„Zdd„ZdS )Ú_LoopFloorTimerHandlezBCancelable owner for the currently scheduled selector floor timer.Úloopúasyncio.AbstractEventLoopÚintervalÚfloatc                óf   — || _         || _        d| _        d | _        |                      ¦   «          d S )NF)Ú_loopÚ	_intervalÚ
_cancelledÚ_timerÚ	_schedule)Úselfr   r   s      ú?/home/ragecks/.hermes/hermes-agent/gateway/shutdown_watchdog.pyÚ__init__z_LoopFloorTimerHandle.__init__;   s4   € ØˆŒ
Ø!ˆŒØˆŒØ59ˆŒØ�ŠÑÔÐÐÐó    ÚreturnÚNonec                óZ   — | j                              | j        | j        ¦  «        | _        d S ©N)r   Ú
call_laterr   Ú_tickr   ©r   s    r   r   z_LoopFloorTimerHandle._scheduleB   s#   € Ø”j×+Ò+¨D¬N¸D¼JÑGÔGˆŒˆˆr    c                ó@   — | j         s|                      ¦   «          d S d S r$   )r   r   r'   s    r   r&   z_LoopFloorTimerHandle._tickE   s,   € ØŒð 	Ø�NŠNÑÔÐÐÐð	ð 	r    c                óX   — d| _         | j        �| j                             ¦   «          d S d S )NT)r   r   Úcancelr'   s    r   r*   z_LoopFloorTimerHandle.cancelI   s3   € ØˆŒØŒ;Ð"ØŒK×ÒÑ Ô Ð Ð Ð ð #Ð"r    N)r   r   r   r   ©r!   r"   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r&   r*   © r    r   r   r   8   sn   € € € € € ØLÐLðð ð ð ðHð Hð Hð Hðð ð ð ð!ð !ð !ð !ð !ð !r    r   c                  ó4   — e Zd ZdZdd„Zdd	„Zddd„Zdd„Zd
S )Ú_LoopLivenessWatchdogHandlez6Small lifecycle handle for the daemon liveness thread.Ú
stop_eventúthreading.EventÚthreadúthreading.Threadc                ó"   — || _         || _        d S r$   )Ú_stop_eventÚ_thread)r   r3   r5   s      r   r   z$_LoopLivenessWatchdogHandle.__init__R   s   € Ø%ˆÔØˆŒˆˆr    r!   r"   c                ó8   — | j                              ¦   «          d S r$   )r8   Úsetr'   s    r   Ústopz _LoopLivenessWatchdogHandle.stopV   s   € ØÔ×ÒÑÔÐÐÐr    NÚtimeoutúOptional[float]c                ó<   — | j                              |¬¦  «         d S )N©r=   )r9   Újoin)r   r=   s     r   rA   z _LoopLivenessWatchdogHandle.joinY   s!   € ØŒ×Ò 'ÐÑ*Ô*Ð*Ð*Ð*r    Úboolc                ó4   — | j                              ¦   «         S r$   )r9   Úis_aliver'   s    r   rD   z$_LoopLivenessWatchdogHandle.is_alive\   s   € ØŒ|×$Ò$Ñ&Ô&Ð&r    )r3   r4   r5   r6   r+   r$   )r=   r>   r!   r"   )r!   rB   )r,   r-   r.   r/   r   r<   rA   rD   r0   r    r   r2   r2   O   so   € € € € € Ø@Ð@ðð ð ð ðð ð ð ð+ð +ð +ð +ð +ð'ð 'ð 'ð 'ð 'ð 'r    r2   r   r   r   r   r!   c                óš   — 	 t          |¦  «        }|dk    rt          ‚n# t          t          f$ r
 t          }Y nw xY wt	          | |¦  «        S )zAKeep at least one timer pending so selector waits remain bounded.r   )r   Ú
ValueErrorÚ	TypeErrorÚ#DEFAULT_LOOP_FLOOR_TIMER_INTERVAL_Sr   )r   r   Úresolved_intervals      r   Ú_arm_loop_floor_timerrJ   `   sk   € ð
@Ý! (™OœOÐØ Ò!Ð!ÝÐð "øå•zÐ"ð @ð @ð @Ý?ÐÐÐð@øøøå  Ð'8Ñ9Ô9Ð9s   ‚ Ÿ:¹:)Úprobe_intervalÚprobe_timeoutÚmax_strikesÚ	exit_coderK   rL   rM   ÚintrN   ú%Optional[_LoopLivenessWatchdogHandle]c               ó:  ‡ ‡‡‡‡	‡
‡— |Š|Š|Š
t          j        ¦   «         Š	dˆ	ˆfd„Šdˆˆˆˆ ˆ	ˆ
fd„}t          j        |dd	¬
¦  «        }	 |                     ¦   «          n-# t          $ r  t
                               dd¬¦  «         Y dS w xY wt          ‰	|¦  «        S )uS  Start an out-of-loop watchdog that hard-exits after missed probes.

    The guard is on by default; operators opt out with
    ``gateway.loop_watchdog: false`` in config.yaml (enforced by the caller,
    ``GatewayRunner._start_loop_liveness_guards`` â€” this module stays
    config-agnostic so bare-loop tests can drive it directly).
    Úprobe_eventr4   r!   úOptional[bool]c                ó  •— t          j        ¦   «         ‰z   }	 ‰                     ¦   «         rd S |t          j        ¦   «         z
  }|dk    r|                      ¦   «         S |                      t	          |d¦  «        ¬¦  «        rdS Œm)NTr   gš™™™™™©?r@   )ÚtimeÚ	monotonicÚis_setÚwaitÚmin)rR   ÚdeadlineÚ	remainingr3   r=   s      €€r   Ú_wait_for_probez5start_loop_liveness_watchdog.<locals>._wait_for_probe‚   s�   ø€ Ý”>Ñ#Ô# gÑ-ˆð	Ø× Ò Ñ"Ô"ð Ø�tØ ¥4¤>Ñ#3Ô#3Ñ3ˆIØ˜AŠ~ˆ~Ø"×)Ò)Ñ+Ô+Ð+Ø×Ò­¨I°tÑ(<Ô(<ÐÑ=Ô=ð Ø�tð	r    r"   c                 ó&  •— d} ‰                      ‰¬¦  «        �svt          j        ¦   «         }	 ‰                     |j        ¦  «         n9# t
          $ r Y d S t          $ r  t                               dd¬¦  «         Y d S w xY w ‰|¦  «        }|€d S |rd} Œ“‰ 	                    ¦   «         rd S | dz  } | ‰	k     rŒµ‰ 	                    ¦   «         rd S 	 t           
                    d| ‰¦  «         n# t          $ r Y nw xY w	 t          j        d¬¦  «         n,# t          $ r t                               d	d¬¦  «         Y nw xY w‰ 	                    ¦   «         rd S 	 dd
lm}  |‰d¬¦  «         n# t          $ r Y nw xY wt          j        ‰¦  «         d S d S )Nr   r@   z.Failed to schedule gateway loop liveness probeT©Úexc_infoé   z–Gateway event loop missed %d consecutive liveness probes; dumping all thread stacks and exiting with code %d so the service supervisor can restart it.©Úall_threadsz&Loop liveness faulthandler dump failed©Úmark_exitedÚloop_liveness_watchdog©Úreason)rX   Ú	threadingÚEventÚcall_soon_threadsafer;   ÚRuntimeErrorÚ	ExceptionÚloggerÚdebugrW   ÚcriticalÚfaulthandlerÚdump_tracebackÚgateway.lifecycle_ledgerrd   ÚosÚ_exit)
ÚstrikesrR   Ú	respondedrd   r\   rN   r   r   r3   Ústrikes_limits
       €€€€€€r   Ú	_watchdogz/start_loop_liveness_watchdog.<locals>._watchdog�   sO  ø€ ØˆØ—/’/¨(�/Ñ3Ô3ñ 6	Ý#œ/Ñ+Ô+ˆKð
Ø×)Ò)¨+¬/Ñ:Ô:Ð:Ð:øÝð ð ð ð ��Ýð ð ð Ý—’ØDÈtð ñ ô ð ð ��ð	øøøð (˜¨Ñ4Ô4ˆIØÐ Ø�Øð Ø�Øà× Ò Ñ"Ô"ð Ø�Ø�q‰LˆGØ˜Ò&Ð&Øà× Ò Ñ"Ô"ð Ø�ð	Ý—’ð9ð Øñô ð ð øõ ð ð ð Ø�ðøøøðVÝÔ+¸Ð=Ñ=Ô=Ð=Ð=øÝð Vð Vð VÝ—’ÐEÐPT�ÑUÔUÐUÐUÐUðVøøøà× Ò Ñ"Ô"ð Ø�ðØ@Ð@Ð@Ð@Ð@Ð@Ø�˜IÐ.FÐGÑGÔGÐGÐGøÝð ð ð Ø�ðøøøåŒH�YÑÔÐØˆFðm6	ð 6	sM   ¯A
 Á

B Á%B Á?B ÃC- Ã-
C:Ã9C:Ã>D Ä&D=Ä<D=ÅE+ Å+
E8Å7E8Tzgateway-loop-liveness-watchdog©ÚtargetÚdaemonÚnamez.Failed to start gateway loop liveness watchdogr^   N)rR   r4   r!   rS   r+   )rh   ri   ÚThreadÚstartrl   rm   rn   r2   )r   rK   rL   rM   rN   rx   r5   r\   r   r3   rw   r=   s   `   `  @@@@@r   Ústart_loop_liveness_watchdogr   n   s  øøøøøøø€ ð €HØ€GØ€MÝ”Ñ"Ô"€Jð	ð 	ð 	ð 	ð 	ð 	ð 	ð8ð 8ð 8ð 8ð 8ð 8ð 8ð 8ð 8ð 8ð 8õt ÔØØØ-ðñ ô €Fð
Ø�Š‰ŒˆˆøÝð ð ð Ý�ŠÐEÐPTˆÑUÔUÐUØˆtˆtðøøøõ ' z°6Ñ:Ô:Ð:s   ÁA  Á &B
Â	B
r   c                 ó¤   — t           j                             dd¦  «                             ¦   «         } | rt	          | ¦  «        S t          ¦   «         S )zHHERMES_HOME for process-level identity files (ignore profile overrides).ÚHERMES_HOMEÚ )rs   ÚenvironÚgetÚstripr   r   )Úvals    r   Ú_process_hermes_homer‡   Ô   sC   € å
Œ*�.Š.˜¨Ñ
+Ô
+×
1Ò
1Ñ
3Ô
3€CØ
ð Ý�C‰yŒyÐÝÑÔÐr    ÚhomeúOptional[Path]c                óD   — | �| nt          ¦   «         } |j        t          Ž S )z1Return ``<HERMES_HOME>/state/gateway.heartbeat``.)r‡   ÚjoinpathÚ_HEARTBEAT_RELATIVE©rˆ   Úbases     r   Úget_loop_heartbeat_pathr�   Ü   s'   € àÐ#ˆ4ˆ4Õ)=Ñ)?Ô)?€DØˆ4Œ=Õ-Ð.Ð.r    c                óD   — | �| nt          ¦   «         } |j        t          Ž S )zBReturn the faulthandler / metadata dump path for a fired watchdog.)r‡   r‹   Ú_WATCHDOG_DUMP_RELATIVEr�   s     r   Úget_shutdown_watchdog_dump_pathr’   â   s'   € àÐ#ˆ4ˆ4Õ)=Ñ)?Ô)?€DØˆ4Œ=Õ1Ð2Ð2r    )ÚpidÚ
start_timerˆ   Úextrar“   úOptional[int]r”   r>   r•   úOptional[Dict[str, Any]]c                ó  — t          |¦  «        }t          | �| nt          j        ¦   «         ¦  «        t	          j        t          j        ¦  «                             ¦   «         t          j
        ¦   «         dœ}|�t          |¦  «        |d<   	 ddlm}  |¦   «         }|r||d<   n# t          $ r Y nw xY w|r|                     |¦  «         	 t!          ||d¬¦  «         n,# t          $ r t"                               dd	¬
¦  «         Y nw xY w|S )uÑ   Atomically rewrite the loop-liveness heartbeat file.

    ``start_time`` is the gateway process start (``time.time()`` epoch seconds)
    so supervisors can detect PID reuse. Best-effort â€” never raises.
    N)r“   Ú
updated_atrV   r”   r   )Úsample_memoryÚmem)Úindentz&Failed to write gateway loop heartbeatTr^   )r�   rO   rs   Úgetpidr   Únowr   ÚutcÚ	isoformatrU   rV   r   rr   rš   rl   Úupdater   rm   rn   )r“   r”   rˆ   r•   ÚpathÚpayloadrš   r›   s           r   Úwrite_loop_heartbeatr¤   è   sC  € õ # 4Ñ(Ô(€Då˜#˜/�3�3­r¬y©{¬{Ñ;Ô;Ý”l¥8¤<Ñ0Ô0×:Ò:Ñ<Ô<Ý”^Ñ%Ô%ðð €Gð
 ÐÝ % jÑ 1Ô 1ˆ�ÑðØ:Ð:Ð:Ð:Ð:Ð:àˆm‰oŒoˆØð 	!Ø ˆG�E‰NøøÝð ð ð Øˆðøøøàð Ø�Š�uÑÔÐðNÝ˜$ °Ð5Ñ5Ô5Ð5Ð5øÝð Nð Nð NÝ�ŠÐ=ÈˆÑMÔMÐMÐMÐMðNøøøà€Ks$   ÂB$ Â$
B1Â0B1ÃC Ã&DÄD)Úgrace_sÚdrain_timeoutr¥   c               óö   — 	 t          t          | ¦  «        d¦  «        }n# t          t          f$ r d}Y nw xY w	 t          t          |¦  «        d¦  «        }n# t          t          f$ r
 t          }Y nw xY w||z   S )z=Return the wall-clock leash for the shutdown watchdog thread.ç        )Úmaxr   rG   rF   Ú!DEFAULT_SHUTDOWN_WATCHDOG_GRACE_S)r¦   r¥   ÚdrainÚgraces       r   Úresolve_shutdown_watchdog_delayr­     s˜   € ðÝ•E˜-Ñ(Ô(¨#Ñ.Ô.ˆˆøÝ•zÐ"ð ð ð Øˆˆˆðøøøð2Ý•E˜'‘N”N CÑ(Ô(ˆˆøÝ•zÐ"ð 2ð 2ð 2Ý1ˆˆˆð2øøøà�5‰=Ðs   ‚   6µ6ºA ÁA3Á2A3Ú	dump_pathÚdelay_sÚsnapshotr"   c               óè  — 	 | j                              dd¬¦  «         n# t          $ r Y dS w xY wdt          j        ¦   «         |t          j        t          j        ¦  «         	                    ¦   «         |pi dœ}	 t          | dd¬¦  «        5 }|                     t          j        |t          ¬	¦  «        d
z   ¦  «         |                     d¦  «         |                     ¦   «          	 t!          j        |d¬¦  «         n%# t$          $ r |                     d¦  «         Y nw xY w|                     d¦  «         |                     ¦   «          ddd¦  «         n# 1 swxY w Y   n# t$          $ r Y nw xY w	 t&          j                             d|d›dt          j        ¦   «         › d�¦  «         t&          j                             ¦   «          t!          j        d¬¦  «         dS # t$          $ r Y dS w xY w)z:Best-effort faulthandler + metadata dump before hard-exit.T)ÚparentsÚexist_okNÚshutdown_watchdog_fired)Úeventr“   r¯   Úfired_atr°   Úazutf-8)Úencoding)ÚdefaultÚ
z(--- faulthandler dump (all threads) ---
)Úfilerb   z%(faulthandler.dump_traceback failed)
z--- end dump ---
z&Gateway shutdown watchdog fired after z.0fzs (pid=z); dumping all thread stacks.
ra   )ÚparentÚmkdirÚOSErrorrs   r�   r   rž   r   rŸ   r    ÚopenÚwriteÚjsonÚdumpsÚstrÚflushrp   rq   rl   ÚsysÚstderr)r®   r¯   r°   ÚheaderÚfhs        r   Ú_write_watchdog_dumprÉ   #  sv  € ðØÔ×Ò t°dÐÑ;Ô;Ð;Ð;øÝð ð ð Øˆˆðøøøð +ÝŒy‰{Œ{ØÝ”L¥¤Ñ.Ô.×8Ò8Ñ:Ô:Ø�N ðð €FðÝ�)˜S¨7Ð3Ñ3Ô3ð 		°rØ�HŠH•T”Z µÐ4Ñ4Ô4°tÑ;Ñ<Ô<Ð<Ø�HŠHÐ@ÑAÔAÐAØ�HŠH‰JŒJˆJðCÝÔ+°ÀÐFÑFÔFÐFÐFøÝð Cð Cð CØ—’ÐAÑBÔBÐBÐBÐBðCøøøà�HŠHÐ)Ñ*Ô*Ð*Ø�HŠH‰JŒJˆJð		ð 		ð 		ñ 		ô 		ð 		ð 		ð 		ð 		ð 		ð 		øøøð 		ð 		ð 		ð 		øøõ ð ð ð Øˆðøøøð
ÝŒ
×ÒðA°WÐDð Að AÝ”I‘K”KðAð Að Añ	
ô 	
ð 	
õ 	Œ
×ÒÑÔÐÝÔ#°Ð5Ñ5Ô5Ð5Ð5Ð5øÝð ð ð Øˆˆðøøøsz   ‚ Ÿ
-¬-Á:E% ÂAEÃ(C?Ã>EÃ?D!ÄEÄ D!Ä!,EÅE% ÅEÅE% Å EÅ!E% Å%
E2Å1E2Å6A+G# Ç#
G1Ç0G1r`   zgateway-shutdown-watchdog)Ú
done_eventÚsnapshot_fnrN   r®   r|   rÊ   úOptional[threading.Event]rË   ú&Optional[Callable[[], Dict[str, Any]]]r|   rÃ   r4   c               óŠ  ‡‡‡‡‡— |�|nt          j        ¦   «         Š	 t          t          | ¦  «        d¦  «        Šn# t          t
          f$ r
 t          ŠY nw xY w‰dk    r‰S dˆˆˆˆˆfd„}	 t          j        |d|¬¦  «                             ¦   «          n,# t          $ r t                               d	d¬
¦  «         Y nw xY w‰S )a�  Arm a daemon-thread hard-exit backstop for a wedged shutdown path.

    If ``done_event`` is set before ``delay_s`` elapses, the thread exits
    quietly (normal / progressing shutdown completed). Otherwise it dumps
    diagnostics and calls ``os._exit(exit_code)``.

    Never raises. Returns the ``done_event`` (creating one when omitted) so
    the caller can disarm on successful completion.
    Nr¨   r   r!   r"   c                 óÞ  •— t          j        ¦   «         ‰
z   } t          j        ¦   «         | k     rS| t          j        ¦   «         z
  }‰                     t          |d¦  «        ¬¦  «        rd S t          j        ¦   «         | k     °S‰                     ¦   «         rd S d }‰�4	  ‰¦   «         }n(# t
          $ r}dt          |¦  «        i}Y d }~nd }~ww xY w‰�‰nt          ¦   «         }t          |‰
|¬¦  «         	 t           
                    d‰
|¦  «         n# t
          $ r Y nw xY wt          j        t          j        fD ]'}	 |                     ¦   «          Œ# t
          $ r Y Œ$w xY w	 ddlm}m}  |¦   «           |¦   «          n# t
          $ r Y nw xY w	 ddlm}  |d¬¦  «         n# t
          $ r Y nw xY w	 dd	lm}	  |	‰d
¬¦  «         n# t
          $ r Y nw xY wt-          j        ‰¦  «         d S )Nç      ð?r@   Úsnapshot_error)r¯   r°   uh   Shutdown watchdog fired after %.0fs â€” forcing process exit (asyncio drain path appears wedged; see %s)r   )Úremove_pid_fileÚrelease_gateway_runtime_lock)Údrain_log_queuerc   Úshutdown_watchdogrf   )rU   rV   rX   rY   rW   rl   Úreprr’   rÉ   rm   ro   rÅ   ÚstdoutrÆ   rÄ   Úgateway.statusrÒ   rÓ   Úhermes_loggingrÔ   rr   rd   rs   rt   )rZ   r[   r°   Úexcrz   ÚstreamrÒ   rÓ   rÔ   rd   ÚdelayÚdoner®   rN   rË   s             €€€€€r   rx   z(arm_shutdown_watchdog.<locals>._watchdogl  sº  ø€ õ ”>Ñ#Ô# eÑ+ˆÝŒnÑÔ Ò)Ð)Ø ¥4¤>Ñ#3Ô#3Ñ3ˆIØ�yŠy¥ Y°Ñ!4Ô!4ˆyÑ5Ô5ð Ø�õ ŒnÑÔ Ò)Ð)ð �;Š;‰=Œ=ð 	ØˆFà-1ˆØÐ"ð9Ø&˜;™=œ=��øÝð 9ð 9ð 9Ø,­d°3©i¬iÐ8������øøøøð9øøøð (Ð3��Õ9XÑ9ZÔ9ZˆÝ˜V¨U¸XÐFÑFÔFÐFð	Ý�OŠOð>àØñ	ô ð ð øõ ð 	ð 	ð 	ØˆDð	øøøõ ”z¥3¤:Ð.ð 	ð 	ˆFðØ—’‘”��øÝð ð ð Ø�ðøøøð	ØTÐTÐTÐTÐTÐTÐTÐTØˆOÑÔÐØ(Ð(Ñ*Ô*Ð*Ð*øÝð 	ð 	ð 	ØˆDð	øøøð	Ø6Ð6Ð6Ð6Ð6Ð6ØˆO CÐ(Ñ(Ô(Ð(Ð(øÝð 	ð 	ð 	ØˆDð	øøøð	Ø<Ð<Ð<Ð<Ð<Ð<ØˆK˜	Ð*=Ð>Ñ>Ô>Ð>Ð>øÝð 	ð 	ð 	ØˆDð	øøøå
Œ�ÑÔÐÐÐsr   Â
B( Â(
CÂ2CÃCÃ5D Ä
DÄDÄ=EÅ
EÅEÅ#F  Æ 
FÆFÆF$ Æ$
F1Æ0F1Æ5G	 Ç	
GÇGTry   zFailed to arm shutdown watchdogr^   r+   )rh   ri   r©   r   rG   rF   rª   r}   r~   rl   rm   rn   )	r¯   rÊ   rË   rN   r®   r|   rx   rÜ   rÝ   s	     ```  @@r   Úarm_shutdown_watchdogrÞ   Q  s  øøøøø€ ð$ $Ð/ˆ:ˆ:µY´_Ñ5FÔ5F€Dð2Ý•E˜'‘N”N CÑ(Ô(ˆˆøÝ•zÐ"ð 2ð 2ð 2Ý1ˆˆˆð2øøøð �‚z€zØˆð:ð :ð :ð :ð :ð :ð :ð :ð :ð :ðxGÝÔ 	°$¸TÐBÑBÔB×HÒHÑJÔJÐJÐJøÝð Gð Gð GÝ�ŠÐ6ÀˆÑFÔFÐFÐFÐFðGøøøà€Ks!   ž< ¼AÁAÁ-)B Â&C Â?C )Ú
interval_sr”   rˆ   Úshould_continuerß   rà   úOptional[Callable[[], bool]]c              ƒ  ó4  K  — 	 t          t          | ¦  «        d¦  «        }n# t          t          f$ r
 t          }Y nw xY wt          ||¬¦  «         	 |� |¦   «         sdS t          j        |¦  «        ƒ d{V —† |� |¦   «         sdS t          ||¬¦  «         ŒH)uì   Rewrite the loop heartbeat file on a cadence until cancelled / gated off.

    Runs as an asyncio task on the gateway loop â€” if the loop freezes, this
    task stops and the file mtime/updated_at goes stale for external monitors.
    rÐ   )r”   rˆ   TN)r©   r   rG   rF   ÚDEFAULT_HEARTBEAT_INTERVAL_Sr¤   ÚasyncioÚsleep)rß   r”   rˆ   rà   r   s        r   Úloop_heartbeat_foreverræ   ¯  sÑ   è è € ð0Ý•u˜ZÑ(Ô(¨#Ñ.Ô.ˆˆøÝ•zÐ"ð 0ð 0ð 0Ý/ˆˆˆð0øøøõ
  J°TÐ:Ñ:Ô:Ð:ð?ØÐ&¨¨Ñ/@Ô/@Ð&ØˆFÝŒm˜HÑ%Ô%Ð%Ð%Ð%Ð%Ð%Ð%Ð%ØÐ&¨¨Ñ/@Ô/@Ð&ØˆFÝ¨
¸Ð>Ñ>Ô>Ð>ð?s   „" ¢=¼=)r   r   r   r   r!   r   )r   r   rK   r   rL   r   rM   rO   rN   rO   r!   rP   )r!   r   r$   )rˆ   r‰   r!   r   )
r“   r–   r”   r>   rˆ   r‰   r•   r—   r!   r   )r¦   r   r¥   r   r!   r   )r®   r   r¯   r   r°   r—   r!   r"   )r¯   r   rÊ   rÌ   rË   rÍ   rN   rO   r®   r‰   r|   rÃ   r!   r4   )
rß   r   r”   r>   rˆ   r‰   rà   rá   r!   r"   )1r/   Ú
__future__r   rä   rp   rÁ   Úloggingrs   rÅ   rh   rU   r   r   Úpathlibr   Útypingr   r   r	   r
   Úgateway.restartr   Úhermes_constantsr   Úutilsr   Ú	getLoggerr,   rm   rª   rã   rH   Ú DEFAULT_LOOP_WATCHDOG_INTERVAL_SÚDEFAULT_LOOP_WATCHDOG_TIMEOUT_SÚ!DEFAULT_LOOP_WATCHDOG_MAX_STRIKESrŒ   r‘   r   r2   rJ   r   r‡   r�   r’   r¤   r­   rÉ   rÞ   ræ   r0   r    r   ú<module>rò      sñ  ððð ð. #Ð "Ð "Ð "Ð "Ð "à €€€Ø Ð Ð Ð Ø €€€Ø €€€Ø 	€	€	€	Ø 
€
€
€
Ø Ð Ð Ð Ø €€€Ø 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ø Ð Ð Ð Ð Ð Ø 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0Ð 0à =Ð =Ð =Ð =Ð =Ð =Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ø #Ð #Ð #Ð #Ð #Ð #à	ˆÔ	˜8Ñ	$Ô	$€ð %)Ð !Ø#Ð Ø&)Ð #Ø#'Ð  Ø"&Ð Ø$%Ð !Ø4Ð ØCÐ ð!ð !ð !ð !ð !ñ !ô !ð !ð.'ð 'ð 'ð 'ð 'ñ 'ô 'ð 'ð& :ð:ð :ð :ð :ð :ð" =Ø:Ø8Ø6ðc;ð c;ð c;ð c;ð c;ð c;ðLð ð ð ð/ð /ð /ð /ð /ð3ð 3ð 3ð 3ð 3ð Ø"&ØØ&*ð'ð 'ð 'ð 'ð 'ð 'ðZ 7ðð ð ð ð ð ð"+ð +ð +ð +ðb -1Ø:>ØØ $Ø+ð[ð [ð [ð [ð [ð [ð@ 5Ø"&ØØ48ð?ð ?ð ?ð ?ð ?ð ?ð ?ð ?r    