Timer Operations
Purpose
The Timer Operations subtopic focuses on the fundamental mechanics behind the TaskTimer class responsible for tracking elapsed time. It addresses key challenges in timing tasks accurately and robustly, such as:
Ensuring idempotent start and stop behavior, so multiple calls to
start()orstop()do not corrupt state or measurements.Handling edge cases like stopping a timer before it has started without errors.
Providing consistent elapsed time retrieval whether the timer is running or stopped.
Allowing a clean reset of all timing state for reuse.
This subtopic is essential to the parent topic of Precise Task Timing because it implements the core logic that enables reliable, millisecond-precision timing for arbitrary code blocks or tasks. It complements other subtopics such as Current Time Utility, which supplies the timing source, and Timer Usage Demo, which illustrates practical application scenarios.
Functionality
The core operational workflow revolves around managing internal timestamps and a running state to measure elapsed time accurately:
Starting the Timer
The.start()method sets the start timestamp if the timer is not already running. It uses thenow_ts()helper (defined in this module) to retrieve the current epoch time. If the timer is already running, the method does nothing, ensuring idempotency.Stopping the Timer
The.stop()method records the stop timestamp only if the timer is currently running. Callingstop()multiple times or before starting the timer has no effect, which prevents invalid states or negative timings.Calculating Elapsed Time
Theelapsed_msproperty computes the elapsed time in milliseconds. If the timer is running, it calculates the difference between the current time (now_ts()) and the start time. If stopped, it uses the recorded stop time. If never started, it returns zero. This logic ensures consistent and meaningful elapsed time readings in all states.Resetting the Timer
The.reset()method clears all stored timestamps and running flags, returning the timer to its initial state ready for reuse.
Key Code Snippets
The following excerpts illustrate critical aspects of this subtopic's behavior:
Starting the timer only if not already running:
def start(self) -> "TaskTimer":
if not self._running:
self.started_at = now_ts()
self._running = True
return self
Stopping the timer only if running:
def stop(self) -> "TaskTimer":
if self._running:
self.stopped_at = now_ts()
self._running = False
return self
Calculating elapsed milliseconds with running and stopped states handled:
@property
def elapsed_ms(self) -> int:
if self.started_at is None:
return 0
end = now_ts() if self._running and self.stopped_at is None else self.stopped_at or self.started_at
return int((end - self.started_at) * 1000)
The design ensures that the timer behaves predictably even when methods are called in an unexpected order, such as stopping before starting or starting multiple times.
Integration
This subtopic integrates tightly with the broader timing utility by providing the reliable internal state management that the TaskTimer API exposes. While the parent topic introduces the timer concept and usage, Timer Operations dives into the implementation details that guarantee correct behavior and robustness.
Additionally, it leverages the Current Time Utility subtopic via the now_ts() function to abstract the current time retrieval. This separation allows for easier testing and potential future extension or mocking of time sources.
The timer state and elapsed time logic here form the foundation for the Timer Usage Demo, which shows how these operations translate into practical timing of code blocks.
Diagram
flowchart TD
StartCall["start() called"]
CheckRunning{"_running?"}
NotRunning["Set started_at = now_ts()"]
SetRunning["Set _running = True"]
ReturnStart["Return self"]
StopCall["stop() called"]
CheckRunningStop{"_running?"}
SetStoppedAt["Set stopped_at = now_ts()"]
SetNotRunning["Set _running = False"]
ReturnStop["Return self"]
GetElapsed["elapsed_ms requested"]
StartedNone{"started_at is None?"}
ReturnZero["Return 0"]
RunningCheck{"_running and stopped_at is None?"}
GetNow["end = now_ts()"]
UseStoppedAt["end = stopped_at or started_at"]
CalcElapsed["Return int((end - started_at)*1000)"]
ResetCall["reset() called"]
ClearStarted["started_at = None"]
ClearStopped["stopped_at = None"]
ClearRunning["_running = False"]
%% start flow
StartCall --> CheckRunning
CheckRunning -- No --> NotRunning --> SetRunning --> ReturnStart
CheckRunning -- Yes --> ReturnStart
%% stop flow
StopCall --> CheckRunningStop
CheckRunningStop -- Yes --> SetStoppedAt --> SetNotRunning --> ReturnStop
CheckRunningStop -- No --> ReturnStop
%% elapsed_ms flow
GetElapsed --> StartedNone
StartedNone -- Yes --> ReturnZero
StartedNone -- No --> RunningCheck
RunningCheck -- Yes --> GetNow --> CalcElapsed
RunningCheck -- No --> UseStoppedAt --> CalcElapsed
%% reset flow
ResetCall --> ClearStarted --> ClearStopped --> ClearRunning