Coverage for scheduler / foreground_scheduler.py: 100%

36 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-04 15:08 +0000

1# scheduler/foreground_scheduler.py 

2""" 

3포그라운드 태스크 스케줄러. 

4UserAction 실행 시 BackgroundScheduler의 태스크를 suspend/resume 조율한다. 

5Reference counting 방식: 첫 foreground action → suspend, 마지막 완료 → resume. 

6""" 

7import asyncio 

8import logging 

9from contextlib import asynccontextmanager 

10from typing import Optional 

11 

12from scheduler.background_scheduler import BackgroundScheduler 

13from core.performance_profiler import PerformanceProfiler 

14 

15 

16class ForegroundScheduler: 

17 """UserAction과 백그라운드 태스크 간 우선순위 조율 스케줄러. 

18 

19 foreground action이 실행되면 BackgroundScheduler의 태스크를 일시 중지하고, 

20 모든 foreground action이 완료되면 다시 재개한다. 

21 """ 

22 

23 def __init__( 

24 self, 

25 background_scheduler: BackgroundScheduler, 

26 logger=None, 

27 performance_profiler: Optional[PerformanceProfiler] = None, 

28 ): 

29 self._bg = background_scheduler 

30 self._logger = logger or logging.getLogger(__name__) 

31 self._pm = performance_profiler if performance_profiler else PerformanceProfiler(enabled=False) 

32 self._active_count = 0 

33 self._lock = asyncio.Lock() 

34 

35 @asynccontextmanager 

36 async def context(self): 

37 """Foreground 우선순위 컨텍스트 매니저. 

38 

39 첫 진입 시 BackgroundScheduler를 suspend하고, 

40 마지막 퇴장 시 resume한다. 미들웨어에서 사용. 

41 

42 Usage:: 

43 

44 async with fg.context(): 

45 # broker API 호출 등 foreground 작업 

46 ... 

47 """ 

48 async with self._lock: 

49 self._active_count += 1 

50 if self._active_count == 1: 

51 self._logger.debug("[ForegroundScheduler] 백그라운드 태스크 일시 중지") 

52 await self._bg.suspend_all() 

53 try: 

54 yield 

55 finally: 

56 async with self._lock: 

57 self._active_count -= 1 

58 if self._active_count == 0: 

59 self._logger.debug("[ForegroundScheduler] 백그라운드 태스크 재개") 

60 await self._bg.resume_all() 

61 

62 async def execute(self, coro): 

63 """포그라운드 태스크를 실행한다. 

64 

65 첫 foreground action 시 BackgroundScheduler를 suspend하고, 

66 마지막 foreground action 완료 시 resume한다. 

67 

68 Args: 

69 coro: 실행할 코루틴 (awaitable). 

70 

71 Returns: 

72 코루틴의 반환값. 

73 """ 

74 async with self.context(): 

75 return await coro 

76 

77 @property 

78 def active_count(self) -> int: 

79 """현재 실행 중인 foreground action 수.""" 

80 return self._active_count 

81 

82 @property 

83 def is_active(self) -> bool: 

84 """foreground action이 실행 중인지 여부.""" 

85 return self._active_count > 0