class ExecutionGuard
A guard mechanism that prevents recursive or repeated function calls by tracking active executions and enforcing cooldown periods between calls.
/tf/active/vicechatdev/CDocs/__init__.py
57 - 97
simple
Purpose
ExecutionGuard provides a thread-safe mechanism to prevent functions from being called recursively or too frequently. It tracks which functions are currently executing and enforces a configurable cooldown period between successive calls. This is useful for preventing race conditions, debouncing rapid function calls, and avoiding stack overflow from recursive calls. Common use cases include UI event handlers, API rate limiting, and preventing duplicate background task execution.
Source Code
class ExecutionGuard:
"""Prevent recursive or repeated function calls."""
def __init__(self):
"""Initialize execution guard."""
self._active_calls = {}
self._last_calls = {}
def guard(self, func_name, cooldown_ms=500):
"""
Check if a function should execute.
Parameters:
- func_name: Unique name for the function
- cooldown_ms: Minimum time between executions in milliseconds
Returns:
- Boolean indicating if function should proceed
"""
import time
now = time.time() * 1000 # Convert to milliseconds
# Check if function is already running
if self._active_calls.get(func_name, False):
logger.debug(f"Skipping repeated call to {func_name} (already active)")
return False
# Check cooldown period
last_call = self._last_calls.get(func_name, 0)
if (now - last_call) < cooldown_ms:
logger.debug(f"Skipping repeated call to {func_name} (cooldown: {now - last_call}ms < {cooldown_ms}ms)")
return False
# Record function activity
self._active_calls[func_name] = True
self._last_calls[func_name] = now
return True
def release(self, func_name):
"""Mark a function as no longer active."""
self._active_calls[func_name] = False
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
bases |
- | - |
Parameter Details
__init__: No parameters required. Initializes internal state tracking dictionaries for active calls and last call timestamps.
guard.func_name: A unique string identifier for the function being guarded. This should be consistent across calls to the same logical function. Typically the function's name or a descriptive identifier.
guard.cooldown_ms: Minimum time in milliseconds that must elapse between successive executions of the function. Default is 500ms. Set to 0 to only prevent recursive calls without cooldown. Must be a non-negative number.
release.func_name: The unique string identifier of the function to mark as no longer active. Must match the func_name used in the corresponding guard() call.
Return Value
The guard() method returns a boolean: True if the function should proceed with execution (not currently active and cooldown period has elapsed), False if the function should be skipped (either already running or called too soon after last execution). The release() method returns None. The __init__ constructor returns an ExecutionGuard instance.
Class Interface
Methods
__init__(self) -> None
Purpose: Initialize the ExecutionGuard with empty tracking dictionaries for active calls and last call timestamps
Returns: None - initializes the instance
guard(self, func_name: str, cooldown_ms: int = 500) -> bool
Purpose: Check if a function should be allowed to execute based on whether it's currently active and whether the cooldown period has elapsed since the last call
Parameters:
func_name: Unique string identifier for the function being guardedcooldown_ms: Minimum time in milliseconds between executions (default: 500)
Returns: Boolean - True if function should proceed, False if it should be skipped
release(self, func_name: str) -> None
Purpose: Mark a function as no longer active, allowing it to be called again (subject to cooldown period)
Parameters:
func_name: Unique string identifier for the function to release
Returns: None
Attributes
| Name | Type | Description | Scope |
|---|---|---|---|
_active_calls |
dict[str, bool] | Dictionary tracking which functions are currently executing. Keys are func_name strings, values are boolean indicating active status | instance |
_last_calls |
dict[str, float] | Dictionary storing the timestamp (in milliseconds) of the last call to each function. Keys are func_name strings, values are timestamps | instance |
Dependencies
timelogging
Required Imports
import time
import logging
Usage Example
# Basic usage pattern
import time
import logging
logger = logging.getLogger(__name__)
# Create an execution guard instance
guard = ExecutionGuard()
def my_function():
func_name = 'my_function'
# Check if function should execute
if not guard.guard(func_name, cooldown_ms=1000):
return # Skip execution
try:
# Perform function logic
print('Executing function')
time.sleep(0.5)
finally:
# Always release the guard
guard.release(func_name)
# First call executes
my_function() # Prints: Executing function
# Immediate second call is blocked (cooldown)
my_function() # Skipped
# After cooldown period, executes again
time.sleep(1.1)
my_function() # Prints: Executing function
# Decorator pattern example
def guarded_function(guard_instance, cooldown_ms=500):
def decorator(func):
def wrapper(*args, **kwargs):
func_name = func.__name__
if not guard_instance.guard(func_name, cooldown_ms):
return None
try:
return func(*args, **kwargs)
finally:
guard_instance.release(func_name)
return wrapper
return decorator
guard = ExecutionGuard()
@guarded_function(guard, cooldown_ms=1000)
def protected_function():
print('This runs with protection')
protected_function()
Best Practices
- Always call release() in a finally block to ensure the guard is released even if the function raises an exception
- Use consistent func_name strings across guard() and release() calls for the same function
- Consider using a decorator pattern to automatically handle guard() and release() calls
- Set appropriate cooldown_ms values based on your use case: 0 for recursion prevention only, higher values for rate limiting
- Create one ExecutionGuard instance per logical group of functions that need coordinated protection
- The guard is not thread-safe by default; for multi-threaded applications, consider adding locks or using thread-local storage
- func_name should be unique enough to avoid collisions between different functions
- Monitor debug logs to understand when and why function calls are being blocked
- For long-running functions, the cooldown timer starts when guard() is called, not when release() is called
- If a function crashes without calling release(), it will remain marked as active indefinitely; implement timeout mechanisms for critical applications
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function guard_execution 76.7% similar
-
class ScriptExecutor 44.3% similar
-
class periodic 43.0% similar
-
class ScriptExecutor_v1 41.8% similar
-
function cleanup_old_tasks 41.7% similar