Ë
    âß²jL  ã                  óX  — 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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mZ dd	lmZ  ej8                  e«      Zd
ZdZ dZ!dZ"dZ#dZ$dZ%dZ&d,d-d„Z' G d„ d«      Z( G d„ d«      Z)e!f	 	 	 	 	 d.d„Z*e"e#e$edœ	 	 	 	 	 	 	 	 	 	 	 d/d„Z+d0d„Z,d1d„Z-d2d„Z.d3d2d„Z/d4d5d„Z0d3d2d„Z1dddddœ	 	 	 	 	 	 	 	 	 d6d „Z2ed!œ	 	 	 	 	 d7d"„Z3	 	 	 	 d8d#„Z4ddd$dd%d&œ	 	 	 	 	 	 	 	 	 	 	 	 	 d9d'„Z5d:d(„Z6d;d)„Z7e dddd*œ	 	 	 	 	 	 	 	 	 d<d+„Z8y)=ag  Out-of-loop shutdown and event-loop liveness backstops.

A frozen asyncio loop takes every asyncio-based recovery path down with it, and launchd/systemd
KeepAlive only restarts a *dead* process. Hence: (1) an OS-thread shutdown watchdog that dumps
stacks and ``os._exit``s past ``restart_drain_timeout + grace``; (2) a heartbeat file at
``<HERMES_HOME>/state/gateway.heartbeat`` so supervisors can tell "process alive" from "loop
frozen"; (3) a lifetime thread watchdog that hard-exits when the loop is too frozen to run its
own callbacks; (4) a self-rescheduling floor timer that keeps the selector timeout finite.é    )ÚannotationsN)ÚdatetimeÚtimezone)ÚPath)ÚAnyÚCallableÚDictÚOptional)Ú!GATEWAY_SERVICE_RESTART_EXIT_CODE)Úget_hermes_homeÚget_process_hermes_home)Úatomic_json_writeg      N@g      >@g      @g      $@é   )Ústatezgateway.heartbeat)Úlogszgateway-shutdown-watchdog.logc                ó\   — 	 t        t        | «      |«      S # t        t        f$ r |cY S w xY w)z@``max(float(value), floor)``, or ``default`` when not coercible.)ÚmaxÚfloatÚ	TypeErrorÚ
ValueError)ÚvalueÚdefaultÚfloors      ú>/home/ubuntu/.hermes/hermes-agent/gateway/shutdown_watchdog.pyÚ_coerce_floatr   0   s1   € ðÜ”5˜“< Ó'Ð'øÜ”zÐ"ò ØŠðús   ‚ —+ª+c                  ó(   — e Zd ZdZdd„Zdd„Zdd„Zy)Ú_LoopFloorTimerHandlezBCancelable owner for the currently scheduled selector floor timer.c                ó^   — ||dc| _         | _        | _        d | _        | j	                  «        y )NF)Ú_loopÚ	_intervalÚ
_cancelledÚ_timerÚ_tick)ÚselfÚloopÚintervals      r   Ú__init__z_LoopFloorTimerHandle.__init__:   s(   € Ø6:¸HÀeÐ3ˆŒ
�D”N D¤OØ59ˆŒØ�
‰
�ó    c                óˆ   — | j                   s6| j                  j                  | j                  | j                  «      | _        y y ©N)r!   r   Ú
call_laterr    r#   r"   ©r$   s    r   r#   z_LoopFloorTimerHandle._tick?   s.   € Ø�ŠØŸ*™*×/Ñ/°·±ÀÇ
Á
ÓKˆD�Kð r(   c                ó`   — d| _         | j                  �| j                  j                  «        y y )NT)r!   r"   Úcancelr,   s    r   r.   z_LoopFloorTimerHandle.cancelC   s(   € ØˆŒØ�;‰;Ð"Ø�K‰K×ÑÕ ð #r(   N)r%   úasyncio.AbstractEventLoopr&   r   ©ÚreturnÚNone)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r'   r#   r.   © r(   r   r   r   8   s   „ ÙLóó
Lô!r(   r   c                  ó   — e Zd Zdd„Zy)Ú_LoopLivenessWatchdogHandlec                óz   — || _         |j                  |j                  |j                  c| _        | _        | _        y r*   )Ú_stop_eventÚsetÚjoinÚis_aliveÚstop)r$   Ú
stop_eventÚthreads      r   r'   z$_LoopLivenessWatchdogHandle.__init__J   s,   € Ø%ˆÔØ.8¯n©n¸f¿k¹kÈ6Ï?É?Ð+ˆŒ	�4”9˜d�mr(   N)r@   úthreading.EventrA   zthreading.Thread)r3   r4   r5   r'   r7   r(   r   r9   r9   I   s   „ ô[r(   r9   c                óP   — t        |d«      }t        | |dkD  r|«      S t        «      S )zAKeep at least one timer pending so selector waits remain bounded.ç        r   )r   r   Ú#DEFAULT_LOOP_FLOOR_TIMER_INTERVAL_S)r%   r&   Úivs      r   Ú_arm_loop_floor_timerrG   O   s,   € ô 
�x Ó	%€BÜ  ¨R°!ªV rÓ]Ð]Ô9\Ó]Ð]r(   )Úprobe_intervalÚprobe_timeoutÚmax_strikesÚ	exit_codec               ó  ‡ ‡‡‡‡‡— t        j                  «       Šdˆˆ ˆˆˆˆfd„}t        j                  |dd¬«      }	 |j                  «        t        ‰|«      S # t        $ r t
        j                  dd¬«       Y yw xY w)	z¼Start an out-of-loop watchdog that hard-exits after missed probes. The caller
    (``GatewayRunner._start_loop_liveness_guards``) enforces the ``gateway.loop_watchdog: false``
    opt-out.c                 óæ  •— d} ‰	j                  ‰¬«      �syt        j                  «       }	 ‰j                  |j                  «       t        j                  «       ‰z   }‰	j                  «       sJ|t        j                  «       z
  }|dk  s|j                  t        |d«      ¬«      rn‰	j                  «       sŒJy |j                  «       rd} ŒÈ‰	j                  «       ry | dz  } | ‰k  rŒä‰	j                  «       ry t        j                  t        «      5  t        j                  d| ‰«       d d d «       	 t!        j"                  d¬	«       ‰	j                  «       ry t%        ‰d«       t'        j(                  ‰«       ‰	j                  ‰¬«      s�Œxy y # t
        $ r Y y t        $ r t        j                  dd¬«       Y y w xY w# 1 sw Y   Œ˜xY w# t        $ r t        j                  d
d¬«       Y Œ¦w xY w)Nr   ©Útimeoutz.Failed to schedule gateway loop liveness probeT©Úexc_infogš™™™™™©?é   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Úloop_liveness_watchdog)ÚwaitÚ	threadingÚEventÚcall_soon_threadsafer<   ÚRuntimeErrorÚ	ExceptionÚloggerÚdebugÚtimeÚ	monotonicÚis_setÚminÚ
contextlibÚsuppressÚcriticalÚfaulthandlerÚdump_tracebackÚ_mark_exited_quietlyÚosÚ_exit)
ÚstrikesÚprobe_eventÚdeadlineÚ	remainingrK   r%   rJ   rH   rI   r@   s
       €€€€€€r   Ú	_watchdogz/start_loop_liveness_watchdog.<locals>._watchdogb   sÂ  ø€ ØˆØ—/‘/¨.�/Õ9Ü#Ÿ/™/Ó+ˆKðØ×)Ñ)¨+¯/©/Ô:ô —~‘~Ó'¨-Ñ7ˆHØ ×'Ñ'Ô)Ø$¤t§~¡~Ó'7Ñ7�	Ø ’> [×%5Ñ%5¼cÀ)ÈTÓ>RÐ%5Ô%SØð !×'Ñ'Õ)ð
 Ø×!Ñ!Ô#Ø�ØØ× Ñ Ô"ØØ�q‰LˆGØ˜Ò$ØØ× Ñ Ô"ØÜ×$Ñ$¤YÓ/ñ (Ü—‘ð`à˜Yô(÷(ð
VÜ×+Ñ+¸Õ=ð × Ñ Ô"ØÜ  Ð,DÔEÜ�H‰H�YÔðM —/‘/¨.�/×9Ð9øô  ò ÙÜò Ü—‘ÐMÐX\�Ô]Ùðú÷((ð (ûô ò VÜ—‘ÐEÐPT�ÖUðVús5   ¬F ÄGÄ3G Æ	F>ÆF>Æ=F>ÇG
Ç G0Ç/G0Tzgateway-loop-liveness-watchdog©ÚtargetÚdaemonÚnamez.Failed to start gateway loop liveness watchdogrP   Nr0   )rW   rX   ÚThreadÚstartr[   r\   r]   r9   )r%   rH   rI   rJ   rK   rn   rA   r@   s   `````  @r   Ústart_loop_liveness_watchdogru   W   sv   ý€ ô —‘Ó"€J÷( ò ( ôR ×Ñ Y°tÐBbÔc€FðØ�‰Œô ' z°6Ó:Ð:øô ò Ü�‰ÐEÐPTˆÔUÙðús   ¿A Á A>Á=A>c                ó  — t        j                  t        «      5  ddlm}  || |¬«       ddd«       t        j                  t        «      5  ddlm} | t        k(  rddini } |dd|d	d
œ|¤Ž ddd«       y# 1 sw Y   ŒMxY w# 1 sw Y   yxY w)uÕ  Best-effort terminal stamp on BOTH lifecycle records before ``os._exit`` skips teardown:
    the lifecycle ledger (so the next boot names the watchdog, not SIGKILL/OOM) and
    ``gateway_state.json`` (so ``hermes gateway status`` and every other reader of that file stop
    seeing ``running`` for a process the watchdog killed â€” #113372). The runtime-status write goes
    LAST: it is the record housekeeping refreshes, so nothing may overwrite it after we stamp it.r   )Úmark_exited)ÚreasonN)Úwrite_runtime_statusÚrestart_requestedTÚdegradedg      Ð?)Úgateway_stateÚexit_reasonÚwait_timeoutr7   )rb   rc   r[   Úgateway.lifecycle_ledgerrw   Úgateway.statusry   r   )rK   rx   rw   ry   Úrestarts        r   rg   rg   ”   s�   € ô 
×	Ñ	œYÓ	'ñ .Ý8Ù�I fÕ-÷.ô 
×	Ñ	œYÓ	'ñ XÝ7ð 2;Ô>_Ò1_Ð&¨Ñ-ÐegˆÙð 	XØ$°&Àtñ	XØOVò	X÷Xð X÷.ð .ú÷Xð Xús   šA7Á"BÁ7B ÂBc                 ó†   — t         j                  j                  dd«      j                  «       r
t	        «       S t        «       S )zHHERMES_HOME for process-level identity files (ignore profile overrides).ÚHERMES_HOMEÚ )rh   ÚenvironÚgetÚstripr   r   r7   r(   r   Ú_process_hermes_homerˆ   ¦   s/   € ä(*¯
©
¯©°}ÀbÓ(I×(OÑ(OÔ(QÔ"Ó$ÐhÔWfÓWhÐhr(   c                ó   — | �| S t        «       S r*   )rˆ   ©Úhomes    r   Ú_homerŒ   «   s   € ØÐ#ˆ4Ð?Ô)=Ó)?Ð?r(   c                ó:   —  t        | «      j                  t        Ž S r*   )rŒ   ÚjoinpathÚ_HEARTBEAT_RELATIVErŠ   s    r   Úget_loop_heartbeat_pathr�   ¯   s   € ØŒ5�‹;×ÑÔ!4Ð5Ð5r(   c                ón   — t        |�|nt        j                  «       «      }t        | «      dz  d|› d�z  S )uþ  ``<HERMES_HOME>/state/gateway.loop-tick.<pid>.sock`` â€” PID-suffixed so a stale node from a
    dead process is never mistaken for this gateway's witness. Served by the loop itself
    (``_tick_socket_handler``), so an answer proves the loop dispatches; the heartbeat cannot.

    Served by the gateway loop itself (see ``_tick_socket_handler``): an answer is direct proof that the
    loop is dispatching, which is exactly the property the heartbeat file lost when its write moved off-loop
    (#90502).
    r   zgateway.loop-tick.z.sock)Úintrh   ÚgetpidrŒ   )r‹   Úpids     r   Úget_loop_tick_socket_pathr•   ³   s8   € ô �S�_‰c¬"¯)©)«+Ó
6€CÜ�‹;˜Ñ Ð%7¸°u¸EÐ#BÑBÐBr(   c                ó:   —  t        | «      j                  t        Ž S r*   )rŒ   rŽ   Ú_WATCHDOG_DUMP_RELATIVErŠ   s    r   Úget_shutdown_watchdog_dump_pathr˜   À   s   € ØŒ5�‹;×ÑÔ!8Ð9Ð9r(   )r”   Ú
start_timer‹   Úextrac                ó  — t        |«      }t        | �| nt        j                  «       «      t	        j
                  t        j                  «      j                  «       t        j                  «       dœ}|�t        |«      |d<   t        j                  t        «      5  ddlm}  |«       x}r||d<   ddd«       |r|j#                  |«       	 t%        ||d¬«       |S # 1 sw Y   Œ-xY w# t        $ r t&        j)                  dd	¬
«       Y |S w xY w)z—Atomically rewrite the loop-liveness heartbeat file; never raises.
    ``start_time`` (process start, epoch seconds) lets supervisors detect PID reuse.N)r”   Ú
updated_atr_   r™   r   )Úsample_memoryÚmem)Úindentz&Failed to write gateway loop heartbeatTrP   )r�   r’   rh   r“   r   Únowr   ÚutcÚ	isoformatr^   r_   r   rb   rc   r[   r   r�   Úupdater   r\   r]   )r”   r™   r‹   rš   ÚpathÚpayloadr�   rž   s           r   Úwrite_loop_heartbeatr¦   Ä   sñ   € ô
 # 4Ó(€DÜ&)°°©#ÄbÇiÁiÃkÓ&RÜ-5¯\©\¼(¿,¹,Ó-G×-QÑ-QÓ-SÜ,0¯N©NÓ,<ñ>€Gð ÐÜ % jÓ 1ˆ�ÑÜ	×	Ñ	œYÓ	'ñ !Ý:Ù“/Ð!ˆ3Ð!Ø ˆG�E‰N÷!ñ Ø�‰�uÔðNÜ˜$ °Õ5ð €K÷!ð !ûô ò NÜ�‰Ð=ÈˆÕMØ€KðNús   ÂCÃC( ÃC%Ã( DÄD)Úgrace_sc               ó@   — t        |t        «      }t        | d«      |z   S )z=Return the wall-clock leash for the shutdown watchdog thread.rD   )r   Ú!DEFAULT_SHUTDOWN_WATCHDOG_GRACE_S)Údrain_timeoutr§   Úgraces      r   Úresolve_shutdown_watchdog_delayr¬   Ü   s#   € ô ˜'Ô#DÓE€EÜ˜¨Ó,¨uÑ4Ð4r(   c               óü  — 	 | j                   j                  dd¬«       dt        j                  «       |t        j                  t        j                  «      j                  «       |xs i dœ}t        j                  t        «      5  t        | dd¬«      5 }|j                  t        j                   |t"        ¬	«      d
z   «       |j%                  «        	 t'        j(                  |d¬«       |j                  d«       |j%                  «        ddd«       ddd«       t        j                  t        «      5  t*        j,                  j                  d|d›dt        j                  «       › d�«       t*        j,                  j%                  «        t'        j(                  d¬«       ddd«       y# t        $ r Y yw xY w# t        $ r |j                  d«       Y Œìw xY w# 1 sw Y   ŒÐxY w# 1 sw Y   ŒÔxY w# 1 sw Y   yxY w)z:Best-effort faulthandler + metadata dump before hard-exit.T©ÚparentsÚexist_okNÚshutdown_watchdog_fired)Úeventr”   Údelay_sÚfired_atÚsnapshotÚazutf-8)Úencoding)r   z)
--- faulthandler dump (all threads) ---
)ÚfilerT   z%(faulthandler.dump_traceback failed)
z--- end dump ---
z&Gateway shutdown watchdog fired after z.0fzs (pid=z); dumping all thread stacks.
rS   )ÚparentÚmkdirÚOSErrorrh   r“   r   r    r   r¡   r¢   rb   rc   r[   ÚopenÚwriteÚjsonÚdumpsÚstrÚflushre   rf   ÚsysÚstderr)Ú	dump_pathr³   rµ   ÚheaderÚfhs        r   Ú_write_watchdog_dumprÇ   ã   s   € ðØ×Ñ×Ñ t°dÐÔ;ð 1¼¿¹»ÐQXÜ"Ÿ,™,¤x§|¡|Ó4×>Ñ>Ó@ÈhÊnÐZ\ñ^€Fä	×	Ñ	œYÓ	'ñ ¬¨i¸ÀwÔ)Oð ÐSUØ
�‰”—‘˜F¬CÔ0Ð3`Ñ`ÔaØ
�‰Œ
ð	?Ü×'Ñ'¨R¸TÕBð 	�‰Ð%Ô&Ø
�‰Œ
÷÷ ô 
×	Ñ	œYÓ	'ñ 6Ü�
‰
×ÑÐAÀ'È#Àð O!Ü!#§¡£ Ð-LðNô 	Oä�
‰
×ÑÔÜ×#Ñ#°Õ5÷	6ð 6øô ò Ùðûô ò 	?Ø�H‰HÐ=Ö>ð	?ú÷ð ú÷ ð ú÷6ð 6úsf   ‚F+ ÂG&Â>GÃF:Ã*!GÄG&Ä4A.G2Æ+	F7Æ6F7Æ:GÇGÇGÇGÇG#	ÇG&Ç&G/Ç2G;rR   zgateway-shutdown-watchdog)Ú
done_eventÚsnapshot_fnrK   rÄ   rr   c               ó  ‡‡‡‡‡— |�|nt        j                  «       Št        | t        «      Š‰dk  r‰S dˆˆˆˆˆfd„}	 t        j                  |d|¬«      j                  «        ‰S # t        $ r t        j                  dd¬«       Y ‰S w xY w)zíArm a daemon-thread hard-exit backstop for a wedged shutdown path: exits quietly if
    ``done_event`` is set within ``delay_s``, else dumps diagnostics and ``os._exit(exit_code)``.
    Never raises; returns ``done_event`` for disarming.r   c                 óF  •— t        j                  «       ‰z   } t        j                  «       | k  rJ‰	j                  t        | t        j                  «       z
  d«      ¬«      ry t        j                  «       | k  rŒJ‰	j	                  «       ry 	 ‰� ‰«       nd }‰
�‰
n	t        «       }t        |‰|¬«       t        j                  t
        «      5  t        j                  d‰|«       d d d «       t        j                  t        j                  fD ]4  }t        j                  t
        «      5  |j!                  «        d d d «       Œ6 t        j                  t
        «      5  ddlm}m}  |«         |«        d d d «       t        j                  t
        «      5  ddlm}  |d¬«       d d d «       t-        ‰d	«       t/        j0                  ‰«       y # t
        $ r}dt        |«      i}Y d }~�ŒSd }~ww xY w# 1 sw Y   �ŒxY w# 1 sw Y   ŒüxY w# 1 sw Y   Œ™xY w# 1 sw Y   ŒtxY w)
Nç      ð?rN   Ú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_queueÚshutdown_watchdog)r^   r_   rV   ra   r`   r[   Úreprr˜   rÇ   rb   rc   r\   rd   rÂ   ÚstdoutrÃ   rÁ   r€   rÎ   rÏ   Úhermes_loggingrÐ   rg   rh   ri   )rl   rµ   Úexcrp   ÚstreamrÎ   rÏ   rÐ   ÚdelayÚdonerÄ   rK   rÉ   s           €€€€€r   rn   z(arm_shutdown_watchdog.<locals>._watchdog  sÆ  ø€ Ü—>‘>Ó# eÑ+ˆÜ�n‰nÓ Ò)Ø�y‰y¤ X´·±Ó0@Ñ%@À#Ó!FˆyÔGØô �n‰nÓ Ó)ð �;‰;Œ=Øð	5Ø(3Ð(?‘{”}ÀTˆHð (Ð3‘Ô9XÓ9ZˆÜ˜V¨U¸XÕFÜ× Ñ ¤Ó+ñ 	ZÜ�O‰Oð JØKPÐRXôZ÷	Zô —z‘z¤3§:¡:Ð.ò 	ˆFÜ×$Ñ$¤YÓ/ñ Ø—‘”÷ð ð	ô
 × Ñ ¤Ó+ñ 	+÷ UÙÔÙ(Ô*÷	+ô × Ñ ¤Ó+ñ 	)Ý6Ù CÕ(÷	)ô 	˜YÐ(;Ô<Ü
�‰�Õøô/ ò 	5Ø(¬$¨s«)Ð4ŽHûð	5ú÷	Zñ 	Zú÷ð ú÷	+ð 	+ú÷	)ð 	)úsH   ÂG ÃG2Ä(G?ÅHÆHÇ	G/ÇG*Ç*G/Ç2G<Ç?H	ÈHÈH Tro   zFailed to arm shutdown watchdogrP   r0   )	rW   rX   r   r©   rs   rt   r[   r\   r]   )	r³   rÈ   rÉ   rK   rÄ   rr   rn   r×   rØ   s	     ```  @@r   Úarm_shutdown_watchdogrÙ   ü   s�   ü€ ð $Ð/‰:´Y·_±_Ó5F€DÜ˜'Ô#DÓE€EØ�‚zØˆ÷ ñ  ðBGÜ×Ñ 	°$¸TÔB×HÑHÔJð €Køô ò GÜ�‰Ð6ÀˆÕFØ€KðGús   Á &A( Á( BÂBc              ƒ  ó„  K  — 	 |j                  d«       |j                  «       ƒ d{  –—†  t        j                  t        «      5  |j                  «        ddd«       y7 Œ7# t        $ r Y Œ@w xY w# 1 sw Y   yxY w# t        j                  t        «      5  |j                  «        ddd«       w # 1 sw Y   w xY wxY w­w)zÚAnswer a liveness ping with one byte; never raises. Runs on the gateway loop, so a reply
    witnesses loop schedulability; the write is a socket-buffer copy (no fsync), immune to the
    stalls that age the heartbeat.ó   1N)r½   Údrainr[   rb   rc   Úclose)ÚreaderÚwriters     r   Ú_tick_socket_handlerrà   0  sš   è ø€ ðØ�‰�TÔØ�l‰l‹n×Ðô × Ñ ¤Ó+ñ 	Ø�L‰LŒN÷	ð 	ð	 	ùÜò Ùðú÷	ð 	ûŒZ× Ñ ¤Ó+ñ 	Ø�L‰LŒN÷	÷ 	ñ 	ÿss   ‚C „$A" ¨A ©A" ­C ÁA1Á	C Á A" Á"	A.Á+A= Á-A.Á.A= Á1A:Á6C Á=B=ÂB1Â(	B=Â1B:Â6B=Â=C c                óv  ‡ — 	 ˆ fd„‰ j                   j                  d«      D «       D ]>  }	 t        j                  t	        |j
                  j                  d«      d   «      d«       Œ@ y
# t        t        t        f$ r |j                  d¬«       Y Œjw xY w# t        $ r t        j                  dd¬	«       Y y
w xY w)a;  Unlink loop-tick socket nodes left by dead PIDs (POSIX only; never raises).
    create_unix_server removes a leftover node at OUR path (os._exit / SIGKILL skip the
    finally-unlink) but not SIBLING nodes from other dead PIDs. os.kill(pid, 0) is a liveness
    probe only on POSIX (Windows would TerminateProcess).c              3  ó.   •K  — | ]  }|‰k7  sŒ	|–— Œ y ­wr*   r7   )Ú.0ÚpÚown_paths     €r   ú	<genexpr>z,_sweep_stale_tick_sockets.<locals>.<genexpr>D  s   øè ø€ Òc˜AÐUVÐZbÓUb”aÑcùs   ƒ
Žzgateway.loop-tick.*.sockú.éþÿÿÿr   T©Ú
missing_okz#stale loop-tick socket sweep failedrP   N)r¹   Úglobrh   Úkillr’   rr   Úsplitr   Ú
IndexErrorr»   Úunlinkr[   r\   r]   )rå   Ústales   ` r   Ú_sweep_stale_tick_socketsrñ   >  s    ø€ ð
KÛc §¡×!5Ñ!5Ð6PÓ!QÔcò 	.ˆEð.Ü—‘œ˜EŸJ™J×,Ñ,¨SÓ1°"Ñ5Ó6¸Õ:ñ	.øô ¤
¬GÐ4ò .Ø—‘¨�Ö-ð.ûäò KÜ�‰Ð:ÀTˆÖJðKús4   ƒ'B «;A)Á&B Á)&BÂB ÂBÂB Â B8Â7B8)Ú
interval_sr™   r‹   Úshould_continuec              ƒ  óÆ  K  — t        | t        d¬«      }dx}x}}	 t        j                  dk(  r`t	        |«      }|j
                  j                  dd¬«       t        |«       t        j                  t        t        |«      ¬«      ƒ d{  –—† }n¢t        j                  t        dd	¬
«      ƒ d{  –—† }|j                  xs g D ]k  }t        j                  t         «      5  t#        |j%                  «       x}	t&        «      r't)        |	«      dk\  rt+        |	d   «      }	 ddd«        n
ddd«       Œm |du|dœ}
	 	 	 t        j0                  t2        |||
¬«      ƒ d{  –—†  |�� |«       s†	 |�‚|j7                  «        t        j                  t         «      5  |j9                  «       ƒ d{  –—†  ddd«       |�5t        j                  t         «      5  |j;                  d¬«       ddd«       yyyt        j<                  |«      ƒ d{  –—†  |�� |«       s†	 |�‚|j7                  «        t        j                  t         «      5  |j9                  «       ƒ d{  –—†  ddd«       |�5t        j                  t         «      5  |j;                  d¬«       ddd«       yyy�Œc7 �Œ7 �Œò# 1 sw Y   �ŒêxY w# t         $ r dx}}t,        j/                  dd¬«       Y �Œ¦w xY w7 �Œ€# t         $ r t,        j5                  dd¬«       Y �Œ¡w xY w7 �Œ\# 1 sw Y   �Œ\xY w# 1 sw Y   yxY w7 �Œ7 ŒÎ# 1 sw Y   ŒÍxY w# 1 sw Y   yxY w# |�œ|j7                  «        t        j                  t         «      5  |j9                  «       ƒ d{  –—†7   ddd«       n# 1 sw Y   nxY w|�At        j                  t         «      5  |j;                  d¬«       ddd«       w # 1 sw Y   w xY ww w xY w­w)a6  Rewrite the loop heartbeat file on a cadence until cancelled / gated off. Runs on the
    gateway loop so a frozen loop lets the file age for monitors. The fsync write goes to a thread
    (inline, a stalled filesystem blocked the loop inside its own heartbeat and the liveness
    watchdog killed it: WSL2 fsync stalls p99 31s / max 112s vs a ~90-120s budget) but is *awaited*,
    never fire-and-forget: an unawaited task would keep the file fresh while the loop was wedged,
    and one in-flight write at a time keeps a long stall from queuing a thread per interval.
    Off-loop, file freshness no longer proves loop schedulability, so this task also arms a
    loop-scheduling witness (``_tick_socket_handler``, flagged as ``loop_tick_socket``); probes must
    require the witness to agree before classifying WEDGED.rÌ   )r   NÚposixTr®   )r¤   z	127.0.0.1r   )ÚhostÚporté   rR   u€   Loop tick socket unavailable â€” liveness probes will have no loop-scheduling witness and will not escalate on a stale heartbeatrP   )Úloop_tick_socketÚloop_tick_tcp_port)r™   r‹   rš   z$Loop heartbeat write failed off-loopré   )r   ÚDEFAULT_HEARTBEAT_INTERVAL_Srh   rr   r•   r¹   rº   rñ   ÚasyncioÚstart_unix_serverrà   rÀ   Ústart_serverÚsocketsrb   rc   r[   Ú
isinstanceÚgetsocknameÚtupleÚlenr’   r\   ÚwarningÚ	to_threadr¦   r]   rÝ   Úwait_closedrï   Úsleep)rò   r™   r‹   ró   r&   Útick_serverÚtick_socket_pathÚtick_tcp_portÚ_sÚ_snamerš   s              r   Úloop_heartbeat_foreverr  M  sÃ  è ø€ ô ˜ZÔ)EÈSÔQ€Hð
 6:Ð9€KÐ9Ð" ]ð&Ü�7‰7�gÒÜ8¸Ó>ÐØ×#Ñ#×)Ñ)°$ÀÐ)ÔFÜ%Ð&6Ô7Ü '× 9Ñ 9Ô:NÜ?BÐCSÓ?Tô!V÷ V‰Kô !(× 4Ñ 4Ô5IÐP[ÐbcÔ d×dˆKØ!×)Ñ)Ò/¨Rò �Ü×(Ñ(¬Ó3ñ Ü!¨B¯N©NÓ,<Ð"< &¼eÔDÌÈVËÐXYÒIYÜ(+¨F°1©I«˜Ø÷÷ ò ðð "-°DÐ!8ÐP]Ñ^€Eð=ØðTÜ×'Ñ'Ô(<ÈÐZ^Ø.3ô5÷ 5ð 5ð Ð*±?Ô3DØð
 Ð"Ø×ÑÔÜ×$Ñ$¤YÓ/ñ 0Ø!×-Ñ-Ó/×/Ð/÷0àÐ+Ü×(Ñ(¬Ó3ñ =Ø$×+Ñ+°tÐ+Ô<÷=ð =ð ,ð	 #ô	 —-‘- Ó)×)Ð)ØÐ*±?Ô3DØàÐ"Ø×ÑÔÜ×$Ñ$¤YÓ/ñ 0Ø!×-Ñ-Ó/×/Ð/÷0àÐ+Ü×(Ñ(¬Ó3ñ =Ø$×+Ñ+°tÐ+Ô<÷=ð =ð ,ð	 #ñ ð!Vùð eù÷ñ ûô ò &Ø&*Ð*ˆ�mÜ�‰ð \à $ð 	÷ 	&ð&úð5úäò TÜ—‘ÐCÈd�×SðTúð 0ù÷0ñ 0ú÷=ð =úð *ùð 0ø÷0ð 0ú÷=ð =ûð Ð"Ø×ÑÔÜ×$Ñ$¤YÓ/ñ 0Ø!×-Ñ-Ó/×/Ñ/÷0÷ 0ñ 0úàÐ+Ü×(Ñ(¬Ó3ñ =Ø$×+Ñ+°tÐ+Ô<÷=÷ =ñ =úð ,ð	 #üs—  ‚O!œA-J0 Â	JÂ
$J0 Â.J Â/0J0 Ã>J#ÄJ0 Ä1O!Ä9L> Ä; K ÅKÅK Å 	L> Å*+O!ÆLÆ)LÆ*LÆ.#O!ÇLÇ$O!Ç/L> ÈL!ÈL> È+O!ÉL&ÉL$ÉL&É#O!É=L2ÊO!ÊL> ÊJ0 Ê J0 Ê#J-	Ê(J0 Ê0$KËO!ËKËO!ËK Ë LË>L> ÌLÌL> ÌLÌLÌO!ÌLÌO!Ì!L> Ì$L&Ì&L/Ì+O!Ì2L;Ì7O!Ì>,OÍ*NÍ>N
Í?NÎ	OÎNÎ"OÎ4OÏ	OÏOÏ	OÏO!)rD   )r   r   r   r   r   r   r1   r   )r%   r/   r&   r   r1   r   )r%   r/   rH   r   rI   r   rJ   r’   rK   r’   r1   z%Optional[_LoopLivenessWatchdogHandle])rK   r’   rx   rÀ   r1   r2   )r1   r   )r‹   úOptional[Path]r1   r   r*   )NN)r‹   r  r”   úOptional[int]r1   r   )
r”   r  r™   úOptional[float]r‹   r  rš   úOptional[Dict[str, Any]]r1   r   )rª   r   r§   r   r1   r   )rÄ   r   r³   r   rµ   r  r1   r2   )r³   r   rÈ   zOptional[threading.Event]rÉ   z&Optional[Callable[[], Dict[str, Any]]]rK   r’   rÄ   r  rr   rÀ   r1   rB   )rÞ   zasyncio.StreamReaderrß   zasyncio.StreamWriterr1   r2   )rå   r   r1   r2   )
rò   r   r™   r  r‹   r  ró   zOptional[Callable[[], bool]]r1   r2   )9r6   Ú
__future__r   rü   rb   re   r¾   Úloggingrh   rÂ   rW   r^   r   r   Úpathlibr   Útypingr   r   r	   r
   Úgateway.restartr   Úhermes_constantsr   r   Úutilsr   Ú	getLoggerr3   r\   r©   rû   rE   Ú DEFAULT_LOOP_WATCHDOG_INTERVAL_SÚDEFAULT_LOOP_WATCHDOG_TIMEOUT_SÚ!DEFAULT_LOOP_WATCHDOG_MAX_STRIKESr�   r—   r   r   r9   rG   ru   rg   rˆ   rŒ   r�   r•   r˜   r¦   r¬   rÇ   rÙ   rà   rñ   r  r7   r(   r   ú<module>r     sF  ðñ^õ #ã Û Û Û Û Û 	Û 
Û Û ß 'Ý ß 0Ó 0å =ß EÝ #à	ˆ×	Ñ	˜8Ó	$€ð %)Ð !Ø#Ð Ø&)Ð #Ø#'Ð  Ø"&Ð ð %&Ð !Ø4Ð ØCÐ ô÷!ñ !÷"[ñ [ð 8[ð^Ø
#ð^Ø/4ð^àó^ð AaØ:Ø8Ø6ñ	:;Ø
#ð:;Ø8=ð:;àð:;ð ð:;ð ð	:;ð
 +ó:;ózXó$ió
@ô6ô
Cô:ð
 !ÀØÀ4ñØðØ.=ðà
ðà(@ðàLPóð2 /Pñ5Øð5Ø&+ð5ØTYó5ð6Ø#;ð6Ø@Dó6ð4 @DØ:>ÐQRØ $Ð2Mñ1Øð1Ø#<ð1à7ð1àKNð1ð ð1ð -0ð1ð Sbó1óhóKð  8ÐW[ØÐQUñ9=Øð9=ØETð9=à
ð9=à2Nð9=àZ^ô9=r(   