xref: /aosp_15_r20/external/angle/build/fuchsia/test/modification_waiter.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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