mirror of
https://github.com/saveweb/biliarchiver.git
synced 2024-09-19 19:15:27 -07:00
92 lines
3.0 KiB
Python
92 lines
3.0 KiB
Python
import os
|
||
import sys
|
||
import importlib.util
|
||
|
||
class AlreadyRunningError(Exception):
|
||
def __init__(self, message: str=""):
|
||
self.message = message
|
||
super().__init__(self.message)
|
||
def __str__(self):
|
||
return self.message
|
||
|
||
|
||
LOCK_FILENAME = '_uploading.lock'
|
||
|
||
class UploadLock_Basic:
|
||
def __init__(self, lock_dir):
|
||
self.lock_file = os.path.join(lock_dir, LOCK_FILENAME)
|
||
|
||
def __enter__(self):
|
||
if os.path.exists(self.lock_file):
|
||
with open(self.lock_file, 'r', encoding='utf-8') as f:
|
||
print(f.read())
|
||
print("Another instance is already running.")
|
||
raise AlreadyRunningError('Another instance is already running.')
|
||
else:
|
||
with open(self.lock_file, 'w', encoding='utf-8') as f:
|
||
f.write(f'PID: {os.getpid()}: Running')
|
||
# print("Acquired lock, continuing.")
|
||
|
||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||
os.remove(self.lock_file)
|
||
# print("Released lock.")
|
||
|
||
# decorator
|
||
def __call__(self, func):
|
||
def wrapper(*args, **kwargs):
|
||
with self:
|
||
return func(*args, **kwargs)
|
||
return wrapper
|
||
|
||
|
||
class UploadLock_Fcntl():
|
||
fcntl = None
|
||
try:
|
||
import fcntl
|
||
except ModuleNotFoundError:
|
||
pass
|
||
|
||
def __init__(self, lock_dir):
|
||
if self.fcntl is None:
|
||
raise(ModuleNotFoundError("No module named 'fcntl'", name='fcntl'))
|
||
|
||
self.lock_file = os.path.join(lock_dir, LOCK_FILENAME)
|
||
self.lock_file_fd = None
|
||
|
||
def __enter__(self):
|
||
assert self.fcntl is not None
|
||
self.lock_file_fd = open(self.lock_file, 'w')
|
||
try:
|
||
self.fcntl.lockf(self.lock_file_fd, self.fcntl.LOCK_EX | self.fcntl.LOCK_NB)
|
||
# print("Acquired lock, continuing.")
|
||
except IOError:
|
||
raise AlreadyRunningError("Another instance is already running.")
|
||
|
||
|
||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||
assert self.fcntl is not None
|
||
if self.lock_file_fd is None:
|
||
raise IOError("Lock file not opened.")
|
||
self.fcntl.lockf(self.lock_file_fd, self.fcntl.LOCK_UN)
|
||
self.lock_file_fd.close() # lock_file_fd.close() 之后,其他进程有机会在本进程删掉锁文件之前拿到新锁
|
||
try:
|
||
os.remove(self.lock_file) # 删除文件不影响其他进程已持有的 inode 新锁
|
||
except FileNotFoundError:
|
||
# 如果抢到新锁的是本进程,删除文件的是其他进程,那么本进程再删除时自然会 FileNotFoundError,忽略就好
|
||
pass
|
||
# print("Released lock.")
|
||
|
||
# decorator
|
||
def __call__(self, func):
|
||
def wrapper(*args, **kwargs):
|
||
with self:
|
||
return func(*args, **kwargs)
|
||
return wrapper
|
||
|
||
class UploadLock():
|
||
def __new__(cls, lock_dir):
|
||
fcntl_avaivable = importlib.util.find_spec('fcntl')
|
||
if fcntl_avaivable is not None:
|
||
return UploadLock_Fcntl(lock_dir)
|
||
else:
|
||
return UploadLock_Basic(lock_dir) |