ryry-cli 6.16__tar.gz → 6.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ryry_cli-6.16/ryry_cli.egg-info → ryry_cli-6.18}/PKG-INFO +1 -3
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/constant.py +2 -2
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/main.py +8 -20
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/ryry_server_socket.py +91 -15
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/ryry_webapi.py +0 -10
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/shared_memory.py +125 -31
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/store.py +1 -1
- ryry_cli-6.18/ryry/taskUtils.py +496 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/upload.py +0 -28
- {ryry_cli-6.16 → ryry_cli-6.18/ryry_cli.egg-info}/PKG-INFO +1 -3
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry_cli.egg-info/requires.txt +0 -3
- {ryry_cli-6.16 → ryry_cli-6.18}/setup.py +1 -2
- ryry_cli-6.16/ryry/taskUtils.py +0 -304
- {ryry_cli-6.16 → ryry_cli-6.18}/LICENSE +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/README.md +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/__init__.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/daemon_base.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/daemon_manager.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/proxy_manager.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/ryry_service.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/ryry_widget.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/script_template/__init__.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/script_template/daemon.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/script_template/main.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/script_template/run.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/server_func.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/task.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry/utils.py +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry_cli.egg-info/SOURCES.txt +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry_cli.egg-info/dependency_links.txt +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry_cli.egg-info/entry_points.txt +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/ryry_cli.egg-info/top_level.txt +0 -0
- {ryry_cli-6.16 → ryry_cli-6.18}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ryry-cli
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.18
|
|
4
4
|
Summary: ryry tools
|
|
5
5
|
Home-page: https://github.com/dalipenMedia
|
|
6
6
|
Author: dalipen
|
|
@@ -24,8 +24,6 @@ Requires-Dist: urlparser
|
|
|
24
24
|
Requires-Dist: urllib3
|
|
25
25
|
Requires-Dist: portalocker
|
|
26
26
|
Requires-Dist: PyYAML
|
|
27
|
-
Provides-Extra: with-mecord
|
|
28
|
-
Requires-Dist: mecord-cli>=0.7.407; extra == "with-mecord"
|
|
29
27
|
|
|
30
28
|
ryry Python Tool
|
|
31
29
|
===============================================
|
|
@@ -173,26 +173,14 @@ def status():
|
|
|
173
173
|
deviceid = utils.generate_unique_id()
|
|
174
174
|
import socket
|
|
175
175
|
machine_name = socket.gethostname()
|
|
176
|
-
service = ryry_service.ryryService()
|
|
177
|
-
thread_num = store.get_multithread()
|
|
178
|
-
def get_shared_memory_max_counter():
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
elif system == 'Darwin':
|
|
185
|
-
cache_dir = os.path.expanduser('~/Library/Caches')
|
|
186
|
-
data_dir = os.path.join(cache_dir, 'widget_shared_memory')
|
|
187
|
-
else:
|
|
188
|
-
data_dir = '/tmp/widget_shared_memory'
|
|
189
|
-
data_file = os.path.join(data_dir, 'widget_power.json')
|
|
190
|
-
try:
|
|
191
|
-
with open(data_file, 'r', encoding='utf-8') as f:
|
|
192
|
-
data = json.load(f)
|
|
193
|
-
return data.get('max_counter', None)
|
|
194
|
-
except Exception:
|
|
195
|
-
return None
|
|
176
|
+
service = ryry_service.ryryService()
|
|
177
|
+
thread_num = store.get_multithread()
|
|
178
|
+
def get_shared_memory_max_counter():
|
|
179
|
+
try:
|
|
180
|
+
from ryry.shared_memory import shared_memory_service
|
|
181
|
+
return shared_memory_service.get_max_counter()
|
|
182
|
+
except Exception:
|
|
183
|
+
return None
|
|
196
184
|
shared_max_counter = get_shared_memory_max_counter()
|
|
197
185
|
shared_max_counter_str = ""
|
|
198
186
|
if shared_max_counter and shared_max_counter != thread_num:
|
|
@@ -17,8 +17,20 @@ class RyryTaskExecutor(Thread):
|
|
|
17
17
|
ttt = 0
|
|
18
18
|
|
|
19
19
|
def appendTask(self, data, callback):
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if not self.workers_started:
|
|
21
|
+
raise Exception("task workers are not ready")
|
|
22
|
+
counter_incremented = False
|
|
23
|
+
try:
|
|
24
|
+
shared_memory_service.increment_cur_counter()
|
|
25
|
+
counter_incremented = True
|
|
26
|
+
self.task_queue.put([data, callback])
|
|
27
|
+
except Exception:
|
|
28
|
+
if counter_incremented:
|
|
29
|
+
try:
|
|
30
|
+
shared_memory_service.decrement_cur_counter()
|
|
31
|
+
except:
|
|
32
|
+
pass
|
|
33
|
+
raise
|
|
22
34
|
try:
|
|
23
35
|
taskUUID = data["taskUUID"]
|
|
24
36
|
taskUtils.taskPrint(taskUUID, f"{current_thread().name} === addQueue task : {taskUUID}")
|
|
@@ -27,6 +39,15 @@ class RyryTaskExecutor(Thread):
|
|
|
27
39
|
|
|
28
40
|
def idlePower(self):
|
|
29
41
|
return shared_memory_service.get_max_counter() - shared_memory_service.get_cur_counter()
|
|
42
|
+
|
|
43
|
+
def canAcceptTask(self):
|
|
44
|
+
if not self.workers_started:
|
|
45
|
+
return False
|
|
46
|
+
try:
|
|
47
|
+
return shared_memory_service.ensure_available()
|
|
48
|
+
except Exception as e:
|
|
49
|
+
taskUtils.taskPrint(None, f"{current_thread().name} === shared memory unavailable : {e}")
|
|
50
|
+
return False
|
|
30
51
|
|
|
31
52
|
def widgetHasPower(self, cur_widget_id):
|
|
32
53
|
widget_power = shared_memory_service.get_widget_power(cur_widget_id)
|
|
@@ -58,6 +79,7 @@ class RyryTaskExecutor(Thread):
|
|
|
58
79
|
self.max_counter = store.get_multithread()
|
|
59
80
|
self.is_running = True
|
|
60
81
|
self.THEADING_LIST = []
|
|
82
|
+
self.workers_started = False
|
|
61
83
|
self.start()
|
|
62
84
|
|
|
63
85
|
def taskRunning(self):
|
|
@@ -67,6 +89,14 @@ class RyryTaskExecutor(Thread):
|
|
|
67
89
|
data, callback = self.task_queue.get()
|
|
68
90
|
if data is None:
|
|
69
91
|
break
|
|
92
|
+
taskUUID = data.get("taskUUID", "")
|
|
93
|
+
widget_id = None
|
|
94
|
+
task_record_added = False
|
|
95
|
+
widget_power_added = False
|
|
96
|
+
callback_needed = False
|
|
97
|
+
is_ok = False
|
|
98
|
+
msg = ""
|
|
99
|
+
result = "{}"
|
|
70
100
|
try:
|
|
71
101
|
data_config = json.loads(data["config"])
|
|
72
102
|
widget_id = data_config["widget_id"]
|
|
@@ -76,34 +106,55 @@ class RyryTaskExecutor(Thread):
|
|
|
76
106
|
timeout = data_config.get("timeout", 600)
|
|
77
107
|
if "timeout" in data:
|
|
78
108
|
timeout = data["timeout"]
|
|
79
|
-
|
|
80
|
-
taskUUID = data["taskUUID"]
|
|
81
109
|
domain = ""
|
|
82
110
|
params_tmp = json.loads(data["data"])
|
|
83
111
|
if "domain" in params_tmp:
|
|
84
112
|
domain = params_tmp["domain"]
|
|
85
113
|
taskUtils.taskPrint(taskUUID, f"{current_thread().name} === receive task : {taskUUID}")
|
|
86
114
|
_appendTask(taskUUID, domain)
|
|
115
|
+
task_record_added = True
|
|
87
116
|
self.addWidgetPower(widget_id, max_task_number)
|
|
117
|
+
widget_power_added = True
|
|
88
118
|
is_ok, msg, result = task.runTask(data, timeout)
|
|
119
|
+
callback_needed = True
|
|
89
120
|
if is_ok == False:
|
|
90
121
|
taskUtils.notifyTaskFail(taskUUID, msg)
|
|
91
|
-
shared_memory_service.decrement_cur_counter()
|
|
92
|
-
callback(taskUUID, is_ok, msg, result)
|
|
93
|
-
_removeTask(taskUUID)
|
|
94
|
-
self.delWidgetPower(widget_id)
|
|
95
122
|
except Exception as e:
|
|
96
|
-
|
|
97
|
-
|
|
123
|
+
msg = f"{current_thread().name} === task exception : {e}"
|
|
124
|
+
result = "{}"
|
|
125
|
+
callback_needed = bool(taskUUID)
|
|
126
|
+
taskUtils.taskPrint(taskUUID, msg)
|
|
127
|
+
if taskUUID:
|
|
128
|
+
taskUtils.notifyScriptError(taskUUID)
|
|
98
129
|
finally:
|
|
99
|
-
|
|
130
|
+
try:
|
|
131
|
+
shared_memory_service.decrement_cur_counter()
|
|
132
|
+
except Exception as e:
|
|
133
|
+
taskUtils.taskPrint(taskUUID, f"{current_thread().name} === decrement counter exception : {e}")
|
|
134
|
+
if callback_needed and taskUUID:
|
|
135
|
+
try:
|
|
136
|
+
callback(taskUUID, is_ok, msg, result)
|
|
137
|
+
except Exception as e:
|
|
138
|
+
taskUtils.taskPrint(taskUUID, f"{current_thread().name} === task callback exception : {e}")
|
|
139
|
+
if task_record_added:
|
|
140
|
+
try:
|
|
141
|
+
_removeTask(taskUUID)
|
|
142
|
+
except Exception as e:
|
|
143
|
+
taskUtils.taskPrint(taskUUID, f"{current_thread().name} === remove task exception : {e}")
|
|
144
|
+
if widget_power_added:
|
|
145
|
+
try:
|
|
146
|
+
self.delWidgetPower(widget_id)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
taskUtils.taskPrint(taskUUID, f"{current_thread().name} === widget power cleanup exception : {e}")
|
|
149
|
+
if taskUUID:
|
|
150
|
+
taskUtils.taskPrint(taskUUID, None)
|
|
100
151
|
self.task_queue.task_done()
|
|
101
152
|
except Exception as ex:
|
|
102
153
|
taskUtils.taskPrint(None, f"{current_thread().name} === exception : {ex}")
|
|
103
154
|
pass
|
|
104
155
|
print(f" {current_thread().name} taskRunning stop")
|
|
105
156
|
|
|
106
|
-
def
|
|
157
|
+
def startTaskWorkers(self):
|
|
107
158
|
shared_memory_service.start_service()
|
|
108
159
|
shared_memory_service.set_max_counter(self.max_counter)
|
|
109
160
|
shared_memory_service.set_cur_counter(0)
|
|
@@ -111,7 +162,18 @@ class RyryTaskExecutor(Thread):
|
|
|
111
162
|
self.THEADING_LIST.append(Thread(name=f"exector-{idx}",target=self.taskRunning))
|
|
112
163
|
for t in self.THEADING_LIST:
|
|
113
164
|
t.start()
|
|
165
|
+
self.workers_started = True
|
|
166
|
+
|
|
167
|
+
def run(self):
|
|
114
168
|
while self.is_running:
|
|
169
|
+
if not self.workers_started:
|
|
170
|
+
try:
|
|
171
|
+
self.startTaskWorkers()
|
|
172
|
+
print(f" {self.name} workers started")
|
|
173
|
+
except Exception as e:
|
|
174
|
+
taskUtils.taskPrint(None, f"{self.name} === start workers exception : {e}")
|
|
175
|
+
time.sleep(5)
|
|
176
|
+
continue
|
|
115
177
|
time.sleep(5)
|
|
116
178
|
for t in self.THEADING_LIST:
|
|
117
179
|
t.join()
|
|
@@ -123,7 +185,10 @@ class RyryTaskExecutor(Thread):
|
|
|
123
185
|
for _ in range(self.max_counter*2):
|
|
124
186
|
self.task_queue.put([None, None])
|
|
125
187
|
print(f" {self.name} stop")
|
|
126
|
-
|
|
188
|
+
try:
|
|
189
|
+
shared_memory_service.stop_service()
|
|
190
|
+
except Exception as e:
|
|
191
|
+
taskUtils.taskPrint(None, f"{self.name} === stop shared memory exception : {e}")
|
|
127
192
|
|
|
128
193
|
lock = Lock()
|
|
129
194
|
task_config_file = os.path.join(constant.base_path, f"task_config.txt")
|
|
@@ -238,6 +303,9 @@ class RyryShortConnectThread(Thread):
|
|
|
238
303
|
if self.executor.idlePower() <= 0:
|
|
239
304
|
time.sleep(1)
|
|
240
305
|
continue
|
|
306
|
+
if not self.executor.canAcceptTask():
|
|
307
|
+
time.sleep(wait_time)
|
|
308
|
+
continue
|
|
241
309
|
|
|
242
310
|
widget_list = {}
|
|
243
311
|
map = store.widgetMap()
|
|
@@ -250,7 +318,15 @@ class RyryShortConnectThread(Thread):
|
|
|
250
318
|
widget_list[it] = map[it].get("version", "0.0")
|
|
251
319
|
datas = ryry_webapi.GetTask(widget_list)
|
|
252
320
|
for it in datas:
|
|
253
|
-
|
|
321
|
+
try:
|
|
322
|
+
self.executor.appendTask(it, self.taskCallback)
|
|
323
|
+
except Exception as e:
|
|
324
|
+
taskUUID = it.get("taskUUID", "")
|
|
325
|
+
msg = f"{self.name} === append task exception : {e}"
|
|
326
|
+
taskUtils.taskPrint(taskUUID, msg)
|
|
327
|
+
if taskUUID:
|
|
328
|
+
taskUtils.notifyScriptError(taskUUID)
|
|
329
|
+
self.taskCallback(taskUUID, False, msg, "{}")
|
|
254
330
|
|
|
255
331
|
# if task empty -> full, set wait_time = max -> min
|
|
256
332
|
if len(datas) > 0:
|
|
@@ -281,4 +357,4 @@ class RyryShortConnectThread(Thread):
|
|
|
281
357
|
|
|
282
358
|
def markStop(self):
|
|
283
359
|
print(f" {self.name} waiting stop")
|
|
284
|
-
self.is_running = False
|
|
360
|
+
self.is_running = False
|
|
@@ -311,16 +311,6 @@ def upload(localFilePath, ext, keepItAlways=False, needTranscode=False, addition
|
|
|
311
311
|
return _upload(localFilePath, ext, keepItAlways=keepItAlways)
|
|
312
312
|
except:
|
|
313
313
|
pass
|
|
314
|
-
from mecord import upload as mecord_upload
|
|
315
|
-
try:
|
|
316
|
-
url = mecord_upload.upload(localFilePath,
|
|
317
|
-
None,
|
|
318
|
-
"sg",
|
|
319
|
-
needTranscode=needTranscode,
|
|
320
|
-
needAddtionUrl=True) #mecord会使用签名信息,强制needAddtionUrl
|
|
321
|
-
return _changeDomain(url, "m.mecordai.com")
|
|
322
|
-
except:
|
|
323
|
-
pass
|
|
324
314
|
def upload_oss(localFilePath, ext, bucket, endpoint):
|
|
325
315
|
import hashlib
|
|
326
316
|
import hmac
|
|
@@ -2,7 +2,6 @@ import os
|
|
|
2
2
|
import json
|
|
3
3
|
import time
|
|
4
4
|
import threading
|
|
5
|
-
import tempfile
|
|
6
5
|
import platform
|
|
7
6
|
import subprocess
|
|
8
7
|
from threading import Lock
|
|
@@ -28,17 +27,20 @@ class SharedMemoryService:
|
|
|
28
27
|
def __init__(self):
|
|
29
28
|
self.service_name = "widget_power"
|
|
30
29
|
# 使用系统推荐目录,确保多进程共享
|
|
31
|
-
self.
|
|
32
|
-
self.data_file = os.path.join(self.data_dir, f"{self.service_name}.json")
|
|
33
|
-
self.lock_file = os.path.join(self.data_dir, f"{self.service_name}.lock")
|
|
34
|
-
self.process_count_file = os.path.join(self.data_dir, f"{self.service_name}_process_count.json")
|
|
35
|
-
self.process_count_lock_file = self.process_count_file + '.lock'
|
|
30
|
+
self._set_data_dir(self._get_system_shared_dir())
|
|
36
31
|
self.current_service_name = os.path.basename(sys.argv[0])
|
|
37
|
-
#
|
|
32
|
+
# 确保共享目录存在;锁文件保持固定路径,供其他程序共同读写
|
|
38
33
|
os.makedirs(self.data_dir, exist_ok=True)
|
|
39
34
|
|
|
40
35
|
# 增加启动标志位,避免重复初始化
|
|
41
36
|
self._started = False
|
|
37
|
+
|
|
38
|
+
def _set_data_dir(self, data_dir: str):
|
|
39
|
+
self.data_dir = data_dir
|
|
40
|
+
self.data_file = os.path.join(self.data_dir, f"{self.service_name}.json")
|
|
41
|
+
self.lock_file = os.path.join(self.data_dir, f"{self.service_name}.lock")
|
|
42
|
+
self.process_count_file = os.path.join(self.data_dir, f"{self.service_name}_process_count.json")
|
|
43
|
+
self.process_count_lock_file = self.process_count_file + '.lock'
|
|
42
44
|
|
|
43
45
|
def _get_system_shared_dir(self) -> str:
|
|
44
46
|
"""获取系统推荐的共享目录"""
|
|
@@ -57,6 +59,86 @@ class SharedMemoryService:
|
|
|
57
59
|
else: # Linux 和其他 Unix-like 系统
|
|
58
60
|
# Linux: 使用 /tmp 或 /var/tmp
|
|
59
61
|
return '/tmp/widget_shared_memory'
|
|
62
|
+
|
|
63
|
+
def _chmod_if_possible(self, path: str, mode: int):
|
|
64
|
+
if platform.system() == 'Windows':
|
|
65
|
+
return
|
|
66
|
+
try:
|
|
67
|
+
os.chmod(path, mode)
|
|
68
|
+
except (PermissionError, FileNotFoundError):
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
def _shared_dir_mode(self) -> int:
|
|
72
|
+
if platform.system() == 'Linux':
|
|
73
|
+
return 0o1777
|
|
74
|
+
return 0o700
|
|
75
|
+
|
|
76
|
+
def _shared_file_mode(self) -> int:
|
|
77
|
+
if platform.system() == 'Linux':
|
|
78
|
+
return 0o666
|
|
79
|
+
return 0o600
|
|
80
|
+
|
|
81
|
+
def _path_permission_info(self, path: str) -> str:
|
|
82
|
+
try:
|
|
83
|
+
st = os.stat(path)
|
|
84
|
+
return f"mode={oct(st.st_mode & 0o7777)} uid={st.st_uid} gid={st.st_gid}"
|
|
85
|
+
except OSError as e:
|
|
86
|
+
return f"stat failed: {e}"
|
|
87
|
+
|
|
88
|
+
def _assert_shared_dir_writable(self):
|
|
89
|
+
if os.access(self.data_dir, os.W_OK | os.X_OK):
|
|
90
|
+
return
|
|
91
|
+
raise PermissionError(
|
|
92
|
+
f"shared memory dir is not writable: {self.data_dir} "
|
|
93
|
+
f"({self._path_permission_info(self.data_dir)}). "
|
|
94
|
+
f"fix: sudo chown -R $(id -u):$(id -g) {self.data_dir} "
|
|
95
|
+
f"or sudo chmod 1777 {self.data_dir}"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def _prepare_existing_shared_file(self, path: str):
|
|
99
|
+
if os.path.isdir(path):
|
|
100
|
+
raise PermissionError(f"shared memory file path is a directory: {path}")
|
|
101
|
+
if os.path.exists(path):
|
|
102
|
+
self._chmod_if_possible(path, self._shared_file_mode())
|
|
103
|
+
|
|
104
|
+
def _touch_shared_file(self, path: str):
|
|
105
|
+
self._prepare_existing_shared_file(path)
|
|
106
|
+
with open(path, 'a+', encoding='utf-8'):
|
|
107
|
+
pass
|
|
108
|
+
self._chmod_if_possible(path, self._shared_file_mode())
|
|
109
|
+
|
|
110
|
+
def _open_lock_file(self, path: str):
|
|
111
|
+
self._ensure_shared_storage()
|
|
112
|
+
return open(path, 'a+', encoding='utf-8')
|
|
113
|
+
|
|
114
|
+
def _ensure_shared_storage(self):
|
|
115
|
+
"""确保固定共享目录和锁文件可写"""
|
|
116
|
+
os.makedirs(self.data_dir, mode=self._shared_dir_mode(), exist_ok=True)
|
|
117
|
+
self._chmod_if_possible(self.data_dir, self._shared_dir_mode())
|
|
118
|
+
self._assert_shared_dir_writable()
|
|
119
|
+
self._touch_shared_file(self.lock_file)
|
|
120
|
+
self._touch_shared_file(self.process_count_lock_file)
|
|
121
|
+
self._prepare_existing_shared_file(self.data_file)
|
|
122
|
+
self._prepare_existing_shared_file(self.process_count_file)
|
|
123
|
+
|
|
124
|
+
def _default_data(self) -> Dict[str, Any]:
|
|
125
|
+
return {
|
|
126
|
+
"max_counter": 999,
|
|
127
|
+
"cur_counter": 0,
|
|
128
|
+
"widget_power": {},
|
|
129
|
+
"last_update": time.time()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
def _validate_counter_data(self, data: Any) -> bool:
|
|
133
|
+
if not isinstance(data, dict):
|
|
134
|
+
return False
|
|
135
|
+
if not isinstance(data.get("max_counter", 999), int):
|
|
136
|
+
return False
|
|
137
|
+
if not isinstance(data.get("cur_counter", 0), int):
|
|
138
|
+
return False
|
|
139
|
+
if not isinstance(data.get("widget_power", {}), dict):
|
|
140
|
+
return False
|
|
141
|
+
return True
|
|
60
142
|
|
|
61
143
|
def _acquire_shared_lock(self, file_obj):
|
|
62
144
|
"""获取共享锁(跨平台)"""
|
|
@@ -102,9 +184,10 @@ class SharedMemoryService:
|
|
|
102
184
|
def operation(data):
|
|
103
185
|
# 验证数据完整性
|
|
104
186
|
if not self._validate_process_data(data):
|
|
105
|
-
data
|
|
187
|
+
data.clear()
|
|
188
|
+
data["processes"] = {}
|
|
106
189
|
|
|
107
|
-
#
|
|
190
|
+
# 清理共享文件中的死亡进程,避免旧状态长期占用任务槽
|
|
108
191
|
self._cleanup_dead_processes_internal(data, current_time, skip_pid=current_pid)
|
|
109
192
|
|
|
110
193
|
# 检查当前进程是否已经注册
|
|
@@ -121,7 +204,7 @@ class SharedMemoryService:
|
|
|
121
204
|
return len(data["processes"])
|
|
122
205
|
|
|
123
206
|
# 原子性地注册进程
|
|
124
|
-
with
|
|
207
|
+
with self._open_lock_file(self.process_count_lock_file) as lock_f:
|
|
125
208
|
self._acquire_exclusive_lock(lock_f)
|
|
126
209
|
try:
|
|
127
210
|
if os.path.exists(self.process_count_file):
|
|
@@ -132,6 +215,7 @@ class SharedMemoryService:
|
|
|
132
215
|
data = {"processes": {}}
|
|
133
216
|
else:
|
|
134
217
|
data = {"processes": {}}
|
|
218
|
+
data = self._normalize_process_data(data)
|
|
135
219
|
|
|
136
220
|
process_count = operation(data)
|
|
137
221
|
|
|
@@ -170,11 +254,7 @@ class SharedMemoryService:
|
|
|
170
254
|
if skip_pid is not None and pid == skip_pid:
|
|
171
255
|
continue # 跳过当前进程
|
|
172
256
|
|
|
173
|
-
#
|
|
174
|
-
if info.get("service_name") != self.current_service_name:
|
|
175
|
-
continue
|
|
176
|
-
|
|
177
|
-
# 检测1: 检查进程是否真的存在
|
|
257
|
+
# 检查进程是否真的存在;其他服务的死进程也要清理,避免共享计数长期卡住
|
|
178
258
|
if not self._is_process_alive(pid):
|
|
179
259
|
dead_processes.append(pid_str)
|
|
180
260
|
continue
|
|
@@ -197,9 +277,11 @@ class SharedMemoryService:
|
|
|
197
277
|
def operation(data):
|
|
198
278
|
# 验证数据完整性
|
|
199
279
|
if not self._validate_process_data(data):
|
|
280
|
+
data.clear()
|
|
281
|
+
data["processes"] = {}
|
|
200
282
|
return 0
|
|
201
283
|
|
|
202
|
-
#
|
|
284
|
+
# 清理共享文件中的死亡进程,避免旧状态长期占用任务槽
|
|
203
285
|
self._cleanup_dead_processes_internal(data, current_time, skip_pid=current_pid)
|
|
204
286
|
|
|
205
287
|
# 注销当前进程(只注销本服务的)
|
|
@@ -209,7 +291,7 @@ class SharedMemoryService:
|
|
|
209
291
|
return len(data["processes"])
|
|
210
292
|
|
|
211
293
|
# 原子性地注销进程
|
|
212
|
-
with
|
|
294
|
+
with self._open_lock_file(self.process_count_lock_file) as lock_f:
|
|
213
295
|
self._acquire_exclusive_lock(lock_f)
|
|
214
296
|
try:
|
|
215
297
|
if os.path.exists(self.process_count_file):
|
|
@@ -217,7 +299,8 @@ class SharedMemoryService:
|
|
|
217
299
|
with open(self.process_count_file, 'r', encoding='utf-8') as f:
|
|
218
300
|
data = json.load(f)
|
|
219
301
|
except (json.JSONDecodeError, UnicodeDecodeError, FileNotFoundError):
|
|
220
|
-
|
|
302
|
+
data = {"processes": {}}
|
|
303
|
+
data = self._normalize_process_data(data)
|
|
221
304
|
|
|
222
305
|
remaining_processes = operation(data)
|
|
223
306
|
|
|
@@ -241,11 +324,10 @@ class SharedMemoryService:
|
|
|
241
324
|
return 0
|
|
242
325
|
|
|
243
326
|
# 验证数据完整性
|
|
244
|
-
|
|
245
|
-
return 0
|
|
327
|
+
data = self._normalize_process_data(data)
|
|
246
328
|
|
|
247
329
|
# 清理死亡进程
|
|
248
|
-
with
|
|
330
|
+
with self._open_lock_file(self.process_count_lock_file) as lock_f:
|
|
249
331
|
self._acquire_exclusive_lock(lock_f)
|
|
250
332
|
try:
|
|
251
333
|
self._cleanup_dead_processes_internal(data, time.time())
|
|
@@ -264,6 +346,8 @@ class SharedMemoryService:
|
|
|
264
346
|
|
|
265
347
|
def operation(data):
|
|
266
348
|
if "processes" not in data:
|
|
349
|
+
data.clear()
|
|
350
|
+
data["processes"] = {}
|
|
267
351
|
return 0
|
|
268
352
|
|
|
269
353
|
dead_processes = []
|
|
@@ -290,7 +374,7 @@ class SharedMemoryService:
|
|
|
290
374
|
return len(data["processes"])
|
|
291
375
|
|
|
292
376
|
# 原子性地清理死亡进程
|
|
293
|
-
with
|
|
377
|
+
with self._open_lock_file(self.process_count_lock_file) as lock_f:
|
|
294
378
|
self._acquire_exclusive_lock(lock_f)
|
|
295
379
|
try:
|
|
296
380
|
if os.path.exists(self.process_count_file):
|
|
@@ -299,6 +383,7 @@ class SharedMemoryService:
|
|
|
299
383
|
data = json.load(f)
|
|
300
384
|
except (json.JSONDecodeError, UnicodeDecodeError, FileNotFoundError):
|
|
301
385
|
data = {"processes": {}}
|
|
386
|
+
data = self._normalize_process_data(data)
|
|
302
387
|
|
|
303
388
|
remaining_processes = operation(data)
|
|
304
389
|
|
|
@@ -358,6 +443,11 @@ class SharedMemoryService:
|
|
|
358
443
|
|
|
359
444
|
except Exception:
|
|
360
445
|
return False
|
|
446
|
+
|
|
447
|
+
def _normalize_process_data(self, data: Any) -> Dict[str, Any]:
|
|
448
|
+
if self._validate_process_data(data):
|
|
449
|
+
return data
|
|
450
|
+
return {"processes": {}}
|
|
361
451
|
|
|
362
452
|
def _update_heartbeat(self):
|
|
363
453
|
"""更新当前进程的心跳(简化版,仅保持数据结构一致性)"""
|
|
@@ -370,7 +460,7 @@ class SharedMemoryService:
|
|
|
370
460
|
return len(data.get("processes", {}))
|
|
371
461
|
|
|
372
462
|
# 原子性地更新心跳
|
|
373
|
-
with
|
|
463
|
+
with self._open_lock_file(self.process_count_lock_file) as lock_f:
|
|
374
464
|
self._acquire_exclusive_lock(lock_f)
|
|
375
465
|
try:
|
|
376
466
|
if os.path.exists(self.process_count_file):
|
|
@@ -379,6 +469,7 @@ class SharedMemoryService:
|
|
|
379
469
|
data = json.load(f)
|
|
380
470
|
except (json.JSONDecodeError, UnicodeDecodeError, FileNotFoundError):
|
|
381
471
|
data = {"processes": {}}
|
|
472
|
+
data = self._normalize_process_data(data)
|
|
382
473
|
|
|
383
474
|
operation(data)
|
|
384
475
|
|
|
@@ -424,15 +515,12 @@ class SharedMemoryService:
|
|
|
424
515
|
data = json.load(f)
|
|
425
516
|
finally:
|
|
426
517
|
self._release_lock(f)
|
|
518
|
+
if not self._validate_counter_data(data):
|
|
519
|
+
return self._default_data()
|
|
427
520
|
return data
|
|
428
|
-
except (FileNotFoundError, json.JSONDecodeError):
|
|
521
|
+
except (FileNotFoundError, json.JSONDecodeError, UnicodeDecodeError):
|
|
429
522
|
# 如果文件不存在或损坏,返回默认数据
|
|
430
|
-
return
|
|
431
|
-
"max_counter": 999,
|
|
432
|
-
"cur_counter": 0,
|
|
433
|
-
"widget_power": {},
|
|
434
|
-
"last_update": time.time()
|
|
435
|
-
}
|
|
523
|
+
return self._default_data()
|
|
436
524
|
|
|
437
525
|
def _write_data(self, data: Dict[str, Any]):
|
|
438
526
|
"""写入数据文件"""
|
|
@@ -459,7 +547,7 @@ class SharedMemoryService:
|
|
|
459
547
|
def _atomic_operation(self, operation):
|
|
460
548
|
"""执行原子操作(多进程安全)"""
|
|
461
549
|
# 使用文件锁确保多进程间的原子性
|
|
462
|
-
with
|
|
550
|
+
with self._open_lock_file(self.lock_file) as lock_f:
|
|
463
551
|
# 获取独占锁,阻塞直到获得锁
|
|
464
552
|
self._acquire_exclusive_lock(lock_f)
|
|
465
553
|
try:
|
|
@@ -469,6 +557,12 @@ class SharedMemoryService:
|
|
|
469
557
|
return result
|
|
470
558
|
finally:
|
|
471
559
|
self._release_lock(lock_f)
|
|
560
|
+
|
|
561
|
+
def ensure_available(self) -> bool:
|
|
562
|
+
"""确认共享计数文件可读写,避免拉到任务后本地无法入队"""
|
|
563
|
+
def operation(data):
|
|
564
|
+
return True
|
|
565
|
+
return self._atomic_operation(operation)
|
|
472
566
|
|
|
473
567
|
def start_service(self):
|
|
474
568
|
"""启动共享内存服务(轻量级,无需额外进程)"""
|
|
@@ -177,7 +177,7 @@ def extend():
|
|
|
177
177
|
_genExtend()
|
|
178
178
|
elif read_data["extend"].get("device_id", "") != read_data.get("deviceInfo", {}).get("device_id", ""):
|
|
179
179
|
_genExtend()
|
|
180
|
-
elif read_data["extend"].get("app", "") == "ryry-cli 6.
|
|
180
|
+
elif read_data["extend"].get("app", "") == "ryry-cli 6.18":
|
|
181
181
|
_genExtend()
|
|
182
182
|
elif any(k not in read_data["extend"] for k in ["device_id", "host_name", "cpu", "memory", "disk", "gpu"]):
|
|
183
183
|
_genExtend()
|