5353except ImportError : # pragma: no cover - Windows fallback
5454 fcntl = None
5555
56+ from telegram_mcp .singleton import try_lock_exclusive
57+
5658from functools import wraps
5759import telethon .errors .rpcerrorlist
5860from sanitize import sanitize_user_content , sanitize_name , sanitize_dict , format_tool_result
@@ -366,11 +368,6 @@ def _parse_session_pool() -> List[str]:
366368
367369def _acquire_session (pool : List [str ]) -> str :
368370 """Claim the first free session in the pool via an advisory file lock."""
369- if fcntl is None :
370- # No advisory locks (e.g. Windows): can't coordinate slots, so use the
371- # first session. For concurrent clients there, prefer distinct
372- # TELEGRAM_SESSION_STRING_<LABEL> accounts instead.
373- return pool [0 ]
374371 lock_dir = os .path .join (tempfile .gettempdir (), "telegram-mcp-session-locks" )
375372 try :
376373 os .makedirs (lock_dir , exist_ok = True )
@@ -380,9 +377,12 @@ def _acquire_session(pool: List[str]) -> str:
380377 digest = hashlib .sha1 (session .encode ("utf-8" )).hexdigest ()[:16 ]
381378 lock_path = os .path .join (lock_dir , f"session-{ digest } .lock" )
382379 try :
383- fh = open (lock_path , "w" )
384- fcntl .flock (fh .fileno (), fcntl .LOCK_EX | fcntl .LOCK_NB )
380+ # "a+", not "w": on Windows the lock covers the first byte, and
381+ # truncating a file another live client holds is refused.
382+ fh = open (lock_path , "a+" )
385383 except OSError :
384+ continue
385+ if not try_lock_exclusive (fh ):
386386 # Locked by another live client — try the next session.
387387 try :
388388 fh .close ()
@@ -391,6 +391,8 @@ def _acquire_session(pool: List[str]) -> str:
391391 continue
392392 _SESSION_LOCKS .append (fh )
393393 try :
394+ fh .seek (0 )
395+ fh .truncate ()
394396 fh .write (f"pid={ os .getpid ()} \n " )
395397 fh .flush ()
396398 except OSError :
0 commit comments