|
3 | 3 | import os
|
4 | 4 | import signal
|
5 | 5 | import sys
|
| 6 | +import threading |
6 | 7 | import time
|
7 | 8 | from hashlib import md5
|
8 | 9 |
|
9 | 10 | import mantid # for clearer error message
|
| 11 | +import psutil |
10 | 12 | import pyinotify
|
11 |
| -from mantid.simpleapi import StartLiveData |
| 13 | +from mantid.simpleapi import StartLiveData, mtd |
12 | 14 | from mantid.utils.logging import log_to_python as mtd_log_to_python
|
13 | 15 | from packaging.version import parse as parse_version
|
14 | 16 |
|
| 17 | +CONVERSION_FACTOR_BYTES_TO_MB = 1.0 / (1024 * 1024) |
| 18 | + |
15 | 19 | # ##################
|
16 | 20 | # configure logging
|
17 | 21 | # ##################
|
@@ -79,6 +83,13 @@ def stop(cls):
|
79 | 83 | else:
|
80 | 84 | cls.logger.info("mantid not initialized - nothing to cleanup")
|
81 | 85 |
|
| 86 | + def restart_and_clear(self): |
| 87 | + self.logger.info("Restarting Live Data and clearing workspaces") |
| 88 | + self.stop() |
| 89 | + time.sleep(1.0) |
| 90 | + mtd.clear() |
| 91 | + self.start() |
| 92 | + |
82 | 93 |
|
83 | 94 | # ##################
|
84 | 95 | # register a signal handler so we can exit gracefully if someone kills us
|
@@ -157,6 +168,10 @@ def __init__(self, filename):
|
157 | 168 | self.accumMethod = str(json_doc.get("accum_method", "Add"))
|
158 | 169 | self.periods = json_doc.get("periods", None)
|
159 | 170 | self.spectra = json_doc.get("spectra", None)
|
| 171 | + self.system_mem_limit_perc = json_doc.get("system_mem_limit_perc", 25) # set to 0 to disable |
| 172 | + self.mem_check_interval_sec = json_doc.get("mem_check_interval_sec", 1) |
| 173 | + self.mem_limit = psutil.virtual_memory().total * self.system_mem_limit_perc / 100 |
| 174 | + self.proc_pid = psutil.Process(os.getpid()) |
160 | 175 |
|
161 | 176 | # location of the scripts
|
162 | 177 | self.script_dir = json_doc.get("script_dir")
|
@@ -321,9 +336,16 @@ def process_default(self, event):
|
321 | 336 | self.scriptfiles[event.pathname] = newmd5
|
322 | 337 | # restart the service
|
323 | 338 | self.logger.info(f'Processing script "{event.pathname}" changed - restarting ' '"StartLiveData"')
|
324 |
| - self.livemanager.stop() |
325 |
| - time.sleep(1.0) # seconds |
326 |
| - self.livemanager.start() |
| 339 | + self.livemanager.restart_and_clear() |
| 340 | + |
| 341 | + |
| 342 | +def memory_checker(config, livemanager): |
| 343 | + while True: |
| 344 | + mem_used = config.proc_pid.memory_info().rss |
| 345 | + if mem_used > config.mem_limit: |
| 346 | + logger.error(f"Memory usage {mem_used * CONVERSION_FACTOR_BYTES_TO_MB:.2f} MB exceeds limit") |
| 347 | + livemanager.restart_and_clear() |
| 348 | + time.sleep(config.mem_check_interval_sec) |
327 | 349 |
|
328 | 350 |
|
329 | 351 | # determine the configuration file
|
@@ -355,6 +377,11 @@ def process_default(self, event):
|
355 | 377 | # start up the live data
|
356 | 378 | liveDataMgr.start()
|
357 | 379 |
|
| 380 | +# start the memory checker |
| 381 | +if config.system_mem_limit_perc > 0: |
| 382 | + memory_thread = threading.Thread(target=memory_checker, args=(config, liveDataMgr), daemon=True) |
| 383 | + memory_thread.start() |
| 384 | + |
358 | 385 | # inotify will keep the program running
|
359 | 386 | notifier.loop()
|
360 | 387 |
|
|
0 commit comments