Ë
    âß²j¹   ã            	      óÆ  — U 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m	Z	m
Z
mZmZ  ej                  d«      ZdZedz   ZdZd	Zej&                  d
k(  rdZdZded<   dxZxZZnx	 ddlZ eeddddœj7                  ej8                  j;                  d«      «      «      Zej<                  ej>                  ej@                  cZZZ e! eedd«      «      s e"d«      ‚	 ej8                  dk(  sdej8                  v rdndZ$ee%e%f   Z&e	e&ee%e%f   f   Z' ejP                  «       Z)i Z*ded<   d"d„Z+d#d„Z,d$d„Z-d%d„Z.d&d„Z/d'd„Z0d(d)d „Z1d*d!„Z2y# e"e#f$ r dZdZdxZxZZY Œ�w xY w)+uÒ  Hold a state.db writer's WAL-mode file locks in a form a stray ``close()`` cannot cancel.

SQLite protects a live WAL generation with two POSIX advisory locks: a SHARED lock on the main
file's lock range and a shared lock on the DMS byte of ``state.db-shm``. A sibling process may
checkpoint and unlink ``-wal``/``-shm`` at its close only after taking both EXCLUSIVE. POSIX locks
are per process, so any ``open()``/``close()`` of those two files inside the holder â€” a raw probe,
a plugin, a tool reading ``~/.hermes`` â€” cancels both (sqlite.org/howtocorrupt.html Â§2.2) and the
next foreign close strands the holder on a deleted generation (``DeletedWalGenerationError``).

This module adds the same two ranges as *open file description* locks (``F_OFD_SETLK``) on the
descriptors SQLite itself holds. OFD locks belong to the description, not the process: a stray
``close()`` elsewhere cannot cancel them, they die with the connection's own descriptor (nothing
extra to track or retire), and they conflict with a foreign EXCLUSIVE exactly like SQLite's own,
so the sibling's close-time unlink is refused while a guarded handle is open. The guard is
lifted before the handle's own close so a true last close still ends the generation normally.
No-op on Windows and on runtimes without OFD locks.

Ownership model: the guard is a property of the *descriptor*, and a descriptor number is
reusable. Each ``hold()`` therefore locks every matching descriptor unconditionally (an OFD
re-lock on an already-locked description is idempotent) and ``release()`` unlocks only while
another handle in this process still needs the range â€” tracked by handle count per INODE, not
per fd, so a recycled fd number can never be mistaken for a surviving lock.
é    )ÚannotationsN)ÚDictÚOptionalÚSetÚTupleÚhermes_statei   @é   iþ  é€   ÚntzOptional[int]Ú_F_OFD_SETLKÚF_OFD_SETLKé%   éZ   )ÚlinuxÚdarwinÚ
0123456789Úfcntlz$fcntl module has no fcntl() callabler   Úbsdú@qqihhz@hhqqizDict[Identity, int]Ú_HANDLESc                 ó   — t         d uS ©N)r   © ó    ú;/home/ubuntu/.hermes/hermes-agent/hermes_state_lockguard.pyÚ	supportedr   S   s   € Ü˜tÐ#Ð#r   c                óœ   — t         dk(  r"t        j                  t         ||d| t        «      S t        j                  t         | t        ||d«      S )Nr   r   )Ú_FLOCK_FORMATÚstructÚpackÚ	_SEEK_SET)Ú	lock_typeÚstartÚlengths      r   Ú_flockr%   W   s<   € Ü˜Ò Ü�{‰{œ=¨%°¸¸IÄyÓQÐQÜ�;‰;”} i´¸EÀ6È1ÓMÐMr   c           	     óŒ   — t         �t        €J ‚	 t        j                   | t        t        |||«      «       y# t        $ r Y yw xY w)zPApply a non-blocking OFD lock; False when the range is held EXCLUSIVE elsewhere.FT)r   r   r%   ÚBlockingIOError)Úfdr"   r#   r$   s       r   Ú	_ofd_lockr)   ]   sJ   € äÐ¤Ð!9Ð9Ð9ðÜ�‰�Bœ¤f¨Y¸¸vÓ&FÔGð øô ò Ùðús   �&7 ·	AÁAc                ó|   — 	 t        j                  | «      }|j                  |j                  fS # t        $ r Y y w xY wr   )ÚosÚstatÚOSErrorÚst_devÚst_ino)ÚpathÚsts     r   Ú	_identityr2   g   s=   € ðÜ�W‰W�T‹]ˆð �I‰I�r—y‘yÐ!Ð!øô ò Ùðús   ‚/ ¯	;º;c              #  ó<  K  — dD ]u  }	 t        j                  |«      }|D ]W  }|j                  «       sŒt	        |«      }	 t        j
                  |«      }|j                  |j                  f}|| v sŒR||f–— ŒY  y y# t        $ r Y Œ„w xY w# t        $ r Y Œxw xY w­w)z©Yield ``(fd, identity)`` for every descriptor of this process on one of *identities*
    (SQLite's own connection descriptors; the cached header-probe fd too, harmless).)z/proc/self/fdz/dev/fdN)r+   Úlistdirr-   ÚisdigitÚintÚfstatr.   r/   )Ú
identitiesÚfd_dirÚnamesÚnamer(   r1   Úidents          r   Ú_own_fds_forr=   o   s²   è ø€ ð /ò ˆð	Ü—J‘J˜vÓ&ˆEð ò 
	 ˆDØ—<‘<”>ØÜ�T“ˆBðÜ—X‘X˜b“\�ð —Y‘Y §	¡	Ð*ˆEØ˜
Ò"Ø˜%�i“ð
	 ñ 	ñ!øô ò 	Ùð	ûô ò ÙðüsQ   ‚B‰A>ž!BÁ BÁBÁ2BÁ>	B
ÂBÂ	B
Â
BÂ	BÂBÂBÂBc                ó    — t        j                  | «      }i }|t        t        ff|dz   t        dfffD ]  \  }}t        |«      }|€Œ|||<   Œ |S )Nz-shmé   )r+   ÚfspathÚ_SHARED_FIRSTÚ_SHARED_SIZEÚ_SHM_DMS_BYTEr2   )Údb_pathÚbaseÚrangesr0   Úrngr<   s         r   Ú_guard_rangesrH   …   se   € Ü�9‰9�WÓ€DØ€FØœm¬\Ð:Ð;¸dÀV¹mÌmÐ]^ÐM_Ð=`Ðaò  ‰	ˆˆcÜ˜$“ˆØÑØˆF�5ŠMð ð €Mr   c                óº  — |€i n|}t        «       s|S t        | «      }	 t        5  t        t	        |«      «      D ]M  \  }}||   \  }}t        |t        ||«      sŒ!||vsŒ&||   ||<   t        j                  |d«      dz   t        |<   ŒO 	 ddd«       |S # 1 sw Y   |S xY w# t        $ r/ t        j                  dt        j                  | «      d¬«       Y |S w xY w)at  Lock the guard ranges on every descriptor this process has open on ``state.db`` and its
    ``-shm``. Returns the record :func:`release` needs; pass it back to extend an existing one
    (a ``-shm`` minted after open, a reopened connection). Idempotent per handle: an inode already
    in *held* is re-locked (cheap, covers a new descriptor) without a second handle count.Nr   r?   z!WAL lock guard unavailable for %sT)Úexc_info)r   rH   Ú_LOCKr=   Úsetr)   Ú_F_RDLCKr   Úgetr-   ÚloggerÚdebugr+   r@   )rD   ÚheldrF   r(   r<   r#   r$   s          r   ÚholdrR   �   sç   € ð
 �‰2 4€DÜŒ;ØˆÜ˜7Ó#€Fð]Üñ 	AÜ)¬#¨f«+Ó6ò A‘	��EØ & u¡‘��vÜ˜R¤¨5°&Õ9¸eÈ4Ò>OØ"(¨¡-�D˜‘KÜ&.§l¡l°5¸!Ó&<¸qÑ&@”H˜U’Oñ	A÷	Að €K÷	Að €Kûô ò ]Ü�‰Ð8¼"¿)¹)ÀGÓ:LÐW[ˆÕ\Ø€Kð]ús:   ŸB" ¥5BÁBÁ *BÂB" ÂBÂB" ÂB" Â"4CÃCc                óÔ  — t        «       r| syt        5  i }| j                  «       D ]H  \  }}t        j	                  |d«      dz
  }|dkD  r
|t        |<   Œ.t        j                  |d«       |||<   ŒJ | j                  «        |s
	 ddd«       y	 t        t        |«      «      D ]  \  }}||   \  }}t        |t        ||«       Œ! 	 ddd«       y# t        $ r Y Œw xY w# 1 sw Y   yxY w)ax  Drop this handle's claim. The last handle on an inode unlocks the range on every descriptor
    still referencing it. Call BEFORE the handle's own close so SQLite's close-time reset sees only
    real holders: a sibling process's intact locks still refuse the unlink, and a true last close
    ends the generation, so a later ``state.db`` replace never pairs with a stale WAL.Nr?   r   )r   rK   Úitemsr   rN   ÚpopÚclearr=   rL   r)   Ú_F_UNLCKr-   )rQ   Ú	to_unlockr<   rG   Ú	remainingr(   r#   r$   s           r   ÚreleaserZ   ¤   sò   € ô
 Œ;™dØÜ	ñ Øˆ	ØŸ*™*›,ò 	'‰JˆE�3Ü Ÿ™ U¨AÓ.°Ñ2ˆIØ˜1Š}Ø"+”˜’ä—‘˜U DÔ)Ø#&�	˜%Ò ð	'ð 	�
‰
ŒÙØ÷ð ð	Ü)¬#¨i«.Ó9ò 7‘	��EØ )¨%Ñ 0‘��vÜ˜"œh¨¨vÕ6ñ7÷ð øô  ò 	Ùð	ú÷!ð ús0   ”A1CÂ6CÃCÃ	CÃCÃCÃCÃC')ÚreturnÚbool)r"   r6   r#   r6   r$   r6   r[   Úbytes)
r(   r6   r"   r6   r#   r6   r$   r6   r[   r\   )r0   Ústrr[   zOptional[Identity])r8   zSet[Identity])r[   ÚHeldr   )rQ   zOptional[Held]r[   r_   )rQ   r_   r[   ÚNone)3Ú__doc__Ú
__future__r   Úloggingr+   r   ÚsysÚ	threadingÚtypingr   r   r   r   Ú	getLoggerrO   Ú_PENDING_BYTErA   rB   rC   r;   r   r   Ú__annotations__rM   rW   r!   ÚgetattrrN   ÚplatformÚrstripÚF_RDLCKÚF_UNLCKÚSEEK_SETÚcallableÚImportErrorÚAttributeErrorr   r6   ÚIdentityr_   ÚLockrK   r   r   r%   r)   r2   r=   rH   rR   rZ   r   r   r   ú<module>ru      s°  ðòõ0 #ã Û 	Û Û 
Û ß -Ó -à	ˆ×	Ñ	˜>Ó	*€ð €Ø Ñ!€Ø€Ø€ð ‡7�7ˆd‚?Ø€EØ"&€L�-Ó&Ø&'Ð'€HÐ'ˆx™)ð,Ûñ Ø�=¨B¸"Ñ"=×"AÑ"AÀ#Ç,Á,×BUÑBUÐVbÓBcÓ"dófˆà(-¯©°u·}±}ÀbÇkÁkÐ%ˆ�(˜Iñ ™  w°Ó5Ô6ÙÐDÓEÐEð 7ð  ŸL™L¨HÒ4¸ÀÇÁÑ8M‘ÐS[€à��c�‰?€ØˆH�e˜C ˜H‘oÐ%Ñ&€ð 	ˆ	�‰Ó€Ø "€Ð
Ó "ó$óNóó"óó,ôô*øðE ˜Ð(ò ,ØˆØˆØ*+Ð+ˆÐ+�8šið,ús   Á'A6E ÅE ÅE 