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
« 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
12from scheduler.background_scheduler import BackgroundScheduler
13from core.performance_profiler import PerformanceProfiler
16class ForegroundScheduler:
17 """UserAction과 백그라운드 태스크 간 우선순위 조율 스케줄러.
19 foreground action이 실행되면 BackgroundScheduler의 태스크를 일시 중지하고,
20 모든 foreground action이 완료되면 다시 재개한다.
21 """
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()
35 @asynccontextmanager
36 async def context(self):
37 """Foreground 우선순위 컨텍스트 매니저.
39 첫 진입 시 BackgroundScheduler를 suspend하고,
40 마지막 퇴장 시 resume한다. 미들웨어에서 사용.
42 Usage::
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()
62 async def execute(self, coro):
63 """포그라운드 태스크를 실행한다.
65 첫 foreground action 시 BackgroundScheduler를 suspend하고,
66 마지막 foreground action 완료 시 resume한다.
68 Args:
69 coro: 실행할 코루틴 (awaitable).
71 Returns:
72 코루틴의 반환값.
73 """
74 async with self.context():
75 return await coro
77 @property
78 def active_count(self) -> int:
79 """현재 실행 중인 foreground action 수."""
80 return self._active_count
82 @property
83 def is_active(self) -> bool:
84 """foreground action이 실행 중인지 여부."""
85 return self._active_count > 0