Skip to content

Commit 5fce62c

Browse files
committed
2. add force kill methods to morebuiltins.shared_memory.PLock:
1 parent 8bc3ea8 commit 5fce62c

File tree

2 files changed

+53
-12
lines changed

2 files changed

+53
-12
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
### 1.3.2 (2025-08-02)
22
1. fix typing-hint for `morebuiltins.funcs.threads` decorator, now it returns `Callable[..., Future]`.
3+
2. add force kill methods to `morebuiltins.shared_memory.PLock`:
4+
1. add `force_signum` to `morebuiltins.shared_memory.PLock`, to specify the signal number to force kill the process if it exists.
5+
2. add `is_free` to `morebuiltins.shared_memory.PLock`, to check if the lock is free.
6+
3. add `kill_with_name` to `morebuiltins.shared_memory.PLock`, to kill the process with the given name and signal number.
37

48
### 1.3.1 (2025-06-19)
59
1. add `check_recursion` to `morebuiltins.funcs`, to check if a function is recursive.

morebuiltins/shared_memory.py

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import time
44
import typing
55
from contextlib import closing
6-
76
from multiprocessing.shared_memory import SharedMemory
87

98
__all__ = ["PLock", "SharedBytes"]
@@ -44,12 +43,20 @@ class PLock:
4443
"""
4544

4645
DEFAULT_SIZE = 4 # 4 bytes, means 2^32 = 4GB
47-
DEFAULT_BYTEORDER = "little"
48-
49-
def __init__(self, name: str, force=False, close_atexit=False, pid=None):
46+
DEFAULT_BYTEORDER: typing.Literal["little", "big"] = "little"
47+
48+
def __init__(
49+
self,
50+
name: str,
51+
force=False,
52+
close_atexit=False,
53+
pid=None,
54+
force_signum: typing.Optional[int] = None,
55+
):
5056
self.name = name
5157
self.pid = pid or os.getpid()
5258
self.force = force
59+
self.force_signum = force_signum
5360
self.shm = None
5461
# whether the shared memory is closed
5562
self._closed = False
@@ -60,16 +67,43 @@ def __init__(self, name: str, force=False, close_atexit=False, pid=None):
6067
raise RuntimeError(f"Failed to create shared memory {name}")
6168

6269
@staticmethod
63-
def wait_for_free(name: str, timeout=3, interval=0.1):
70+
def is_free(name: str) -> bool:
71+
"""Check if the shared memory is free."""
72+
try:
73+
with closing(SharedMemory(name=name)):
74+
return False
75+
except FileNotFoundError:
76+
return True
77+
78+
@classmethod
79+
def kill_with_name(cls, name: str, sig_num=15):
80+
"""Kill the process that holds the shared memory."""
81+
if cls.is_free(name):
82+
return False
83+
else:
84+
with closing(SharedMemory(name=name)) as shm:
85+
mem_pid = int.from_bytes(
86+
shm.buf[: cls.DEFAULT_SIZE], byteorder=cls.DEFAULT_BYTEORDER
87+
)
88+
try:
89+
os.kill(mem_pid, sig_num)
90+
return True
91+
except ProcessLookupError:
92+
return False
93+
94+
@classmethod
95+
def wait_for_free(cls, name: str, timeout=3, interval=0.1):
6496
"""Wait for the shared memory to be free."""
6597
start = time.time()
66-
while time.time() - start < timeout:
67-
try:
68-
SharedMemory(name=name).close()
69-
time.sleep(interval)
70-
except FileNotFoundError:
98+
while True:
99+
free = cls.is_free(name)
100+
if free:
71101
return True
72-
return False
102+
elif time.time() - start > timeout:
103+
return False
104+
else:
105+
time.sleep(interval)
106+
continue
73107

74108
def init(self):
75109
if self._closed:
@@ -79,7 +113,10 @@ def init(self):
79113
self.shm = SharedMemory(name=self.name, create=True, size=self.DEFAULT_SIZE)
80114
except FileExistsError:
81115
self.shm = SharedMemory(name=self.name)
82-
if not self.force:
116+
if self.force:
117+
if self.force_signum is not None:
118+
ok = self.kill_with_name(self.name, sig_num=self.force_signum)
119+
else:
83120
ok = False
84121
if not ok:
85122
mem_pid = self.mem_pid

0 commit comments

Comments
 (0)