20
20
import re
21
21
from copy import deepcopy
22
22
from time import time
23
- from typing import Any , Dict , List , Optional , Tuple , Callable
23
+ from typing import Any , Dict , Optional , Tuple , TYPE_CHECKING
24
24
25
25
from cylc .flow import LOG
26
26
from cylc .flow .exceptions import XtriggerConfigError
27
27
import cylc .flow .flags
28
28
from cylc .flow .hostuserutil import get_user
29
+ from cylc .flow .subprocpool import get_func
29
30
from cylc .flow .xtriggers .wall_clock import wall_clock
30
31
31
- from cylc .flow .subprocctx import SubFuncContext
32
- from cylc .flow .broadcast_mgr import BroadcastMgr
33
- from cylc .flow .data_store_mgr import DataStoreMgr
34
- from cylc .flow .subprocpool import SubProcPool
35
- from cylc .flow .task_proxy import TaskProxy
36
- from cylc .flow .subprocpool import get_func
32
+ if TYPE_CHECKING :
33
+ from cylc .flow .broadcast_mgr import BroadcastMgr
34
+ from cylc .flow .data_store_mgr import DataStoreMgr
35
+ from cylc .flow .subprocctx import SubFuncContext
36
+ from cylc .flow .subprocpool import SubProcPool
37
+ from cylc .flow .task_proxy import TaskProxy
38
+ from cylc .flow .workflow_db_mgr import WorkflowDatabaseManager
37
39
38
40
39
41
class TemplateVariables (Enum ):
@@ -185,6 +187,7 @@ class XtriggerManager:
185
187
Args:
186
188
workflow: workflow name
187
189
user: workflow owner
190
+ workflow_db_mgr: the DB Manager
188
191
broadcast_mgr: the Broadcast Manager
189
192
proc_pool: pool of Subprocesses
190
193
workflow_run_dir: workflow run directory
@@ -195,9 +198,10 @@ class XtriggerManager:
195
198
def __init__ (
196
199
self ,
197
200
workflow : str ,
198
- broadcast_mgr : BroadcastMgr ,
199
- data_store_mgr : DataStoreMgr ,
200
- proc_pool : SubProcPool ,
201
+ broadcast_mgr : 'BroadcastMgr' ,
202
+ workflow_db_mgr : 'WorkflowDatabaseManager' ,
203
+ data_store_mgr : 'DataStoreMgr' ,
204
+ proc_pool : 'SubProcPool' ,
201
205
user : Optional [str ] = None ,
202
206
workflow_run_dir : Optional [str ] = None ,
203
207
workflow_share_dir : Optional [str ] = None ,
@@ -230,11 +234,15 @@ def __init__(
230
234
}
231
235
232
236
self .proc_pool = proc_pool
237
+ self .workflow_db_mgr = workflow_db_mgr
233
238
self .broadcast_mgr = broadcast_mgr
234
239
self .data_store_mgr = data_store_mgr
240
+ self .do_housekeeping = False
235
241
236
242
@staticmethod
237
- def validate_xtrigger (label : str , fctx : SubFuncContext , fdir : str ) -> None :
243
+ def validate_xtrigger (
244
+ label : str , fctx : 'SubFuncContext' , fdir : str
245
+ ) -> None :
238
246
"""Validate an Xtrigger function.
239
247
240
248
Args:
@@ -305,7 +313,7 @@ def validate_xtrigger(label: str, fctx: SubFuncContext, fdir: str) -> None:
305
313
f' { ", " .join (t .value for t in deprecated_variables )} '
306
314
)
307
315
308
- def add_trig (self , label : str , fctx : SubFuncContext , fdir : str ) -> None :
316
+ def add_trig (self , label : str , fctx : ' SubFuncContext' , fdir : str ) -> None :
309
317
"""Add a new xtrigger function.
310
318
311
319
Check the xtrigger function exists here (e.g. during validation).
@@ -334,7 +342,7 @@ def load_xtrigger_for_restart(self, row_idx: int, row: Tuple[str, str]):
334
342
sig , results = row
335
343
self .sat_xtrig [sig ] = json .loads (results )
336
344
337
- def _get_xtrigs (self , itask : TaskProxy , unsat_only : bool = False ,
345
+ def _get_xtrigs (self , itask : ' TaskProxy' , unsat_only : bool = False ,
338
346
sigs_only : bool = False ):
339
347
"""(Internal helper method.)
340
348
@@ -361,7 +369,9 @@ def _get_xtrigs(self, itask: TaskProxy, unsat_only: bool = False,
361
369
res .append ((label , sig , ctx , satisfied ))
362
370
return res
363
371
364
- def get_xtrig_ctx (self , itask : TaskProxy , label : str ) -> SubFuncContext :
372
+ def get_xtrig_ctx (
373
+ self , itask : 'TaskProxy' , label : str
374
+ ) -> 'SubFuncContext' :
365
375
"""Get a real function context from the template.
366
376
367
377
Args:
@@ -412,7 +422,7 @@ def get_xtrig_ctx(self, itask: TaskProxy, label: str) -> SubFuncContext:
412
422
ctx .update_command (self .workflow_run_dir )
413
423
return ctx
414
424
415
- def call_xtriggers_async (self , itask : TaskProxy ):
425
+ def call_xtriggers_async (self , itask : ' TaskProxy' ):
416
426
"""Call itask's xtrigger functions via the process pool...
417
427
418
428
...if previous call not still in-process and retry period is up.
@@ -421,16 +431,23 @@ def call_xtriggers_async(self, itask: TaskProxy):
421
431
itask: task proxy to check.
422
432
"""
423
433
for label , sig , ctx , _ in self ._get_xtrigs (itask , unsat_only = True ):
434
+ # Special case: quick synchronous clock check:
424
435
if sig .startswith ("wall_clock" ):
425
- # Special case: quick synchronous clock check.
426
- if wall_clock (* ctx .func_args , ** ctx .func_kwargs ):
436
+ if sig in self .sat_xtrig :
437
+ # Already satisfied, just update the task
438
+ itask .state .xtriggers [label ] = True
439
+ elif wall_clock (* ctx .func_args , ** ctx .func_kwargs ):
440
+ # Newly satisfied
427
441
itask .state .xtriggers [label ] = True
428
442
self .sat_xtrig [sig ] = {}
429
443
self .data_store_mgr .delta_task_xtrigger (sig , True )
444
+ self .workflow_db_mgr .put_xtriggers ({sig : {}})
430
445
LOG .info ('xtrigger satisfied: %s = %s' , label , sig )
446
+ self .do_housekeeping = True
431
447
continue
432
448
# General case: potentially slow asynchronous function call.
433
449
if sig in self .sat_xtrig :
450
+ # Already satisfied, just update the task
434
451
if not itask .state .xtriggers [label ]:
435
452
itask .state .xtriggers [label ] = True
436
453
res = {}
@@ -445,6 +462,8 @@ def call_xtriggers_async(self, itask: TaskProxy):
445
462
xtrigger_env
446
463
)
447
464
continue
465
+
466
+ # Call the function to check the unsatisfied xtrigger.
448
467
if sig in self .active :
449
468
# Already waiting on this result.
450
469
continue
@@ -457,8 +476,10 @@ def call_xtriggers_async(self, itask: TaskProxy):
457
476
self .active .append (sig )
458
477
self .proc_pool .put_command (ctx , callback = self .callback )
459
478
460
- def housekeep (self , itasks : List [TaskProxy ]):
461
- """Delete satisfied xtriggers no longer needed by any task.
479
+ def housekeep (self , itasks ):
480
+ """Forget satisfied xtriggers no longer needed by any task.
481
+
482
+ Check self.do_housekeeping before calling this method.
462
483
463
484
Args:
464
485
itasks: list of all task proxies.
@@ -469,8 +490,9 @@ def housekeep(self, itasks: List[TaskProxy]):
469
490
for sig in list (self .sat_xtrig ):
470
491
if sig not in all_xtrig :
471
492
del self .sat_xtrig [sig ]
493
+ self .do_housekeeping = False
472
494
473
- def callback (self , ctx : SubFuncContext ):
495
+ def callback (self , ctx : ' SubFuncContext' ):
474
496
"""Callback for asynchronous xtrigger functions.
475
497
476
498
Record satisfaction status and function results dict.
@@ -489,23 +511,9 @@ def callback(self, ctx: SubFuncContext):
489
511
return
490
512
LOG .debug ('%s: returned %s' , sig , results )
491
513
if satisfied :
514
+ # Newly satisfied
492
515
self .data_store_mgr .delta_task_xtrigger (sig , True )
516
+ self .workflow_db_mgr .put_xtriggers ({sig : results })
493
517
LOG .info ('xtrigger satisfied: %s = %s' , ctx .label , sig )
494
518
self .sat_xtrig [sig ] = results
495
-
496
- def check_xtriggers (
497
- self ,
498
- itask : TaskProxy ,
499
- db_update_func : Callable [[dict ], None ]) -> bool :
500
- """Check if all of itasks' xtriggers have become satisfied.
501
-
502
- Return True if satisfied, else False
503
-
504
- Args:
505
- itasks: task proxies to check
506
- db_update_func: method to update xtriggers in the DB
507
- """
508
- if itask .state .xtriggers_all_satisfied ():
509
- db_update_func (self .sat_xtrig )
510
- return True
511
- return False
519
+ self .do_housekeeping = True
0 commit comments