feat(client): add durable HTTP task state
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"""同一本地数据库的 Windows named mutex。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
import hashlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from cmbuyer_client.core.errors import SingleInstanceError
|
||||
|
||||
|
||||
class NamedMutex:
|
||||
_ALREADY_EXISTS = 183
|
||||
|
||||
def __init__(self, database_path: Path) -> None:
|
||||
if os.name != "nt":
|
||||
raise SingleInstanceError("named_mutex_requires_windows")
|
||||
canonical = str(database_path.expanduser().resolve()).casefold().encode("utf-8")
|
||||
# Global namespace 覆盖同一 Windows 用户的多个交互 session;默认 DACL 不向其他用户泄露句柄。
|
||||
name = "Global\\cmbuyer-" + hashlib.sha256(canonical).hexdigest()
|
||||
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
|
||||
kernel32.CreateMutexW.argtypes = (wintypes.LPVOID, wintypes.BOOL, wintypes.LPCWSTR)
|
||||
kernel32.CreateMutexW.restype = wintypes.HANDLE
|
||||
kernel32.ReleaseMutex.argtypes = (wintypes.HANDLE,)
|
||||
kernel32.ReleaseMutex.restype = wintypes.BOOL
|
||||
kernel32.CloseHandle.argtypes = (wintypes.HANDLE,)
|
||||
kernel32.CloseHandle.restype = wintypes.BOOL
|
||||
ctypes.set_last_error(0)
|
||||
handle = kernel32.CreateMutexW(None, True, name)
|
||||
if not handle:
|
||||
raise SingleInstanceError("named_mutex_failed")
|
||||
if ctypes.get_last_error() == self._ALREADY_EXISTS:
|
||||
kernel32.CloseHandle(handle)
|
||||
raise SingleInstanceError("instance_already_running")
|
||||
self._kernel32 = kernel32
|
||||
self._handle = handle
|
||||
|
||||
def close(self) -> None:
|
||||
handle = getattr(self, "_handle", None)
|
||||
if handle:
|
||||
self._kernel32.ReleaseMutex(handle)
|
||||
self._kernel32.CloseHandle(handle)
|
||||
self._handle = None
|
||||
|
||||
def __enter__(self) -> "NamedMutex":
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type: object, exc: object, traceback: object) -> None:
|
||||
self.close()
|
||||
Reference in New Issue
Block a user