1*8975f5c5SAndroid Build Coastguard Worker# Copyright 2024 The Chromium Authors 2*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 3*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file. 4*8975f5c5SAndroid Build Coastguard Worker""" An AbstractContextManager to wait the modifications to finish during exit. 5*8975f5c5SAndroid Build Coastguard Worker""" 6*8975f5c5SAndroid Build Coastguard Worker 7*8975f5c5SAndroid Build Coastguard Workerimport os 8*8975f5c5SAndroid Build Coastguard Workerimport time 9*8975f5c5SAndroid Build Coastguard Workerfrom contextlib import AbstractContextManager 10*8975f5c5SAndroid Build Coastguard Worker 11*8975f5c5SAndroid Build Coastguard Worker 12*8975f5c5SAndroid Build Coastguard Workerclass ModificationWaiter(AbstractContextManager): 13*8975f5c5SAndroid Build Coastguard Worker """ Exits if there is no modifications for a certain time period, or the 14*8975f5c5SAndroid Build Coastguard Worker timeout has been reached. """ 15*8975f5c5SAndroid Build Coastguard Worker 16*8975f5c5SAndroid Build Coastguard Worker def __init__(self, path: str) -> None: 17*8975f5c5SAndroid Build Coastguard Worker self._path = path 18*8975f5c5SAndroid Build Coastguard Worker # Waits at most 60 seconds. 19*8975f5c5SAndroid Build Coastguard Worker self._timeout = 60 20*8975f5c5SAndroid Build Coastguard Worker # Exits early if no modification happened during last 5 seconds. 21*8975f5c5SAndroid Build Coastguard Worker self._quiet_time = 5 22*8975f5c5SAndroid Build Coastguard Worker 23*8975f5c5SAndroid Build Coastguard Worker def __enter__(self) -> None: 24*8975f5c5SAndroid Build Coastguard Worker # Do nothing, the logic happens in __exit__ 25*8975f5c5SAndroid Build Coastguard Worker return 26*8975f5c5SAndroid Build Coastguard Worker 27*8975f5c5SAndroid Build Coastguard Worker def __exit__(self, exc_type, exc_value, traceback) -> bool: 28*8975f5c5SAndroid Build Coastguard Worker # The default log.dir is /tmp and it's not a good idea to monitor it. 29*8975f5c5SAndroid Build Coastguard Worker if not self._path: 30*8975f5c5SAndroid Build Coastguard Worker return False 31*8975f5c5SAndroid Build Coastguard Worker # Always consider the last modification happening now to avoid an 32*8975f5c5SAndroid Build Coastguard Worker # unexpected early return. 33*8975f5c5SAndroid Build Coastguard Worker last_mod_time = time.time() 34*8975f5c5SAndroid Build Coastguard Worker start_time = last_mod_time 35*8975f5c5SAndroid Build Coastguard Worker while True: 36*8975f5c5SAndroid Build Coastguard Worker cur_time = time.time() 37*8975f5c5SAndroid Build Coastguard Worker if cur_time - start_time >= self._timeout: 38*8975f5c5SAndroid Build Coastguard Worker break 39*8975f5c5SAndroid Build Coastguard Worker cur_mod_time = os.path.getmtime(self._path) 40*8975f5c5SAndroid Build Coastguard Worker if cur_mod_time > last_mod_time: 41*8975f5c5SAndroid Build Coastguard Worker last_mod_time = cur_mod_time 42*8975f5c5SAndroid Build Coastguard Worker elif cur_time - last_mod_time >= self._quiet_time: 43*8975f5c5SAndroid Build Coastguard Worker break 44*8975f5c5SAndroid Build Coastguard Worker time.sleep(1) 45*8975f5c5SAndroid Build Coastguard Worker 46*8975f5c5SAndroid Build Coastguard Worker # Do not suppress exceptions. 47*8975f5c5SAndroid Build Coastguard Worker return False 48