Admission Control with Rate Limits, Min‑Gap, Cooldown and Maintenance
Level: Difficult
Concepts: if/else chains, priority ordering of guards, boundary handling, integer comparisons, state carried via pointers, deterministic side effects
Scenario
You are building a lightweight admission controller for a device interface that receives incoming requests (e.g., keypad or UART-triggered operations). The system must accept or reject each request based on:
- A fixed time window rate limit (max N accepts per window),
- A minimum gap between consecutive accepts,
- A cooldown period that activates after a rate-limit breach,
- A maintenance mode that blocks normal traffic unless an emergency override is set.
All logic must be expressed using if/else only (no switch), and state must be maintained across calls using pointer arguments.
Problem Statement
Implement a function that, given the current timestamp and controller state, decides whether to ACCEPT or REJECT the request. The function must:
- Enforce priority among checks (maintenance → cooldown → min-gap → window limit).
- Update state only when inputs are valid.
- Return a decision (
0=REJECT,1=ACCEPT) and a reason (enum) via output parameters.
Requirements
- Allowed C types only:
int,long,double,char,bool, andenum(including pointers/arrays of these). - Inputs:
now_ms— current time in milliseconds (monotonic,now_ms >= 0).*window_start_ms— start time of the current fixed window (ms, non-negative).window_ms— size of the window (ms, must be > 0).max_in_window— maximum accepts per window (must be ≥ 1).min_gap_ms— minimum time between accepts (ms, ≥ 0).cooldown_ms— duration of cooldown after a rate-limit breach (ms, ≥ 0).*last_accept_ms— timestamp of last accepted request, or-1if none.*cooldown_until_ms— ifnow_ms < *cooldown_until_ms, system is in cooldown; else not. Initialize to0for “no cooldown”.*count_in_window— number of accepted requests in the current window (≥ 0).maintenance_mode— iftrue, normal traffic must be blocked.emergency_override— iftrue, bypass maintenance block and proceed with other checks.*out_decision—0=REJECT,1=ACCEPT.*out_reason— reason enum (see below).
- Priority of checks (highest → lowest):
- Maintenance:
- If
maintenance_mode == trueandemergency_override == false→ REJECT (REASON_MAINTENANCE).
- If
- Cooldown:
- If
now_ms < *cooldown_until_ms→ REJECT (REASON_COOLDOWN).
- If
- Minimum gap from last ACCEPT:
- If
*last_accept_ms >= 0andnow_ms - *last_accept_ms < min_gap_ms→ REJECT (REASON_MIN_GAP).
- If
- Windowed rate limit:
- If
now_ms >= *window_start_ms + window_ms, roll to a new window:
*window_start_ms = now_ms; *count_in_window = 0; - If
*count_in_window >= max_in_window→ REJECT (REASON_WINDOW_LIMIT) and set*cooldown_until_ms = now_ms + cooldown_ms(one-shot cooldown).
- If
- Otherwise ACCEPT:
- Set
*last_accept_ms = now_ms; ++(*count_in_window); *out_decision = 1; *out_reason = REASON_ACCEPTED.
- Set
- Maintenance:
- Validation:
- All pointer arguments must be non-null.
window_ms > 0,max_in_window ≥ 1,min_gap_ms ≥ 0,cooldown_ms ≥ 0,now_ms ≥ 0.*window_start_ms ≥ 0,*count_in_window ≥ 0.now_msshould be monotonic (assume non-decreasing; if obviously inconsistent, return error).- On invalid input, return error and do not modify any outputs.
- Return codes:
0on success (decision provided).-1on invalid arguments (no outputs modified).
Function Details
- Name:
decide_admission - Arguments:
long now_mslong *window_start_msint window_msint max_in_windowlong min_gap_mslong cooldown_mslong *last_accept_mslong *cooldown_until_msint *count_in_windowbool maintenance_modebool emergency_overrideint *out_decision// 0=REJECT, 1=ACCEPTenum AdmissionReason *out_reason
- Return Value:
int—0on success;-1on invalid input.
- Description:
Implements a deterministicif/elsechain that:- Applies maintenance guard first (unless overridden).
- Enforces cooldown window.
- Respects minimum inter-accept gap.
- Maintains a fixed-size window for counting accepts; rolling the window when expired.
- Triggers cooldown on rate-limit breach.
- Updates state only after input validation; does not modify outputs on error.
The function never usesswitch; all branching is viaif/else.
Solution Approach
- Validate all pointers and numeric parameters first; check non-negative times and proper bounds.
- Normalize window: if the window has expired (
now_ms ≥ *window_start_ms + window_ms), start a new window and reset the counter. - Apply guards in the exact priority order specified using a single
if/else-if/...ladder so only one reason determines the outcome. - On REJECT for window limit, set
*cooldown_until_ms = now_ms + cooldown_ms(bounded by long range assumptions). - On ACCEPT, update
*last_accept_msand increment*count_in_window. - Always write both outputs (
*out_decision,*out_reason) on success; never write them on error.
Tasks to Perform
- Validate inputs:
- Check all pointers:
window_start_ms,last_accept_ms,cooldown_until_ms,count_in_window,out_decision,out_reasonare not NULL. - Check numeric constraints:
now_ms ≥ 0,*window_start_ms ≥ 0,window_ms > 0,max_in_window ≥ 1,min_gap_ms ≥ 0,cooldown_ms ≥ 0,*count_in_window ≥ 0.
If any invalid → return-1.
- Check all pointers:
- Maintenance guard:
- If
maintenance_mode && !emergency_override→ set decision to REJECT withREASON_MAINTENANCE.
- If
- Cooldown guard:
- Else if
now_ms < *cooldown_until_ms→ REJECT withREASON_COOLDOWN.
- Else if
- Minimum gap guard:
- Else if
*last_accept_ms >= 0and(now_ms - *last_accept_ms) < min_gap_ms→ REJECT withREASON_MIN_GAP.
- Else if
- Window handling:
- If
now_ms >= *window_start_ms + window_ms→ roll window:
*window_start_ms = now_ms; *count_in_window = 0; - If
*count_in_window >= max_in_window→ REJECT withREASON_WINDOW_LIMITand set*cooldown_until_ms = now_ms + cooldown_ms.
- If
- Accept path:
- Otherwise ACCEPT:
*last_accept_ms = now_ms; (*count_in_window)++;
decision =1, reason =REASON_ACCEPTED.
- Otherwise ACCEPT:
- Write outputs & return:
- On success, write
*out_decisionand*out_reason; return0. - On any invalid input (Step 1), do not modify outputs; return
-1.
- On success, write
Test Cases
| # | Inputs / Precondition | Expected Output | Notes |
|---|---|---|---|
| 1 | now=1000, window_start=1000, window_ms=10000, max=3, min_gap=500, cooldown=3000, last_accept=-1, cooldown_until=0, count=0, maint=false, override=false |
ret=0, decision=1, reason=REASON_ACCEPTED; updates: last_accept=1000, count=1 |
First request in new window |
| 2 | Same as #1 but now=1200 (200ms later) |
ret=0, decision=0, reason=REASON_MIN_GAP; no state increments |
Violates min_gap=500 |
| 3 | After #1, now=1700 (700ms later) |
ret=0, decision=1, reason=REASON_ACCEPTED; count=2, last_accept=1700 |
Satisfies min-gap |
| 4 | After #3, now=2300 (600ms later) |
ret=0, decision=1, reason=REASON_ACCEPTED; count=3, last_accept=2300 |
Hits window quota (=3) |
| 5 | After #4, now=2400 |
ret=0, decision=0, reason=REASON_WINDOW_LIMIT; cooldown_until=2400+3000=5400 |
Exceeds window quota; cooldown starts |
| 6 | During cooldown now=5300 (<5400) |
ret=0, decision=0, reason=REASON_COOLDOWN |
Cooldown active |
| 7 | Cooldown ends now=5400 (==5400) |
ret=0, decision=0 or 1 depending on min-gap/window → Here: window still 1000–10999 with count=3, so REASON_WINDOW_LIMIT |
At boundary: not in cooldown, but still at quota |
| 8 | New window rolls now=11000 (>=1000+10000) |
ret=0, decision=1, reason=REASON_ACCEPTED; window_start=11000, count=1, last_accept=11000 |
Window reset, accept allowed |
| 9 | Maintenance block maint=true, override=false, now=12000 |
ret=0, decision=0, reason=REASON_MAINTENANCE |
Maintenance has highest priority |
| 10 | Emergency override maint=true, override=true, now=12550 |
Proceed to next guards; if now - last_accept < min_gap then REASON_MIN_GAP, else accept |
Override bypasses maintenance but not other guards |
| 11 | Invalid args: window_ms=0 or any NULL pointer |
ret=-1 |
No outputs modified |
Enum suggestion for reasons (for your implementation):
enum AdmissionReason { REASON_ACCEPTED=0, REASON_MAINTENANCE=1, REASON_COOLDOWN=2, REASON_MIN_GAP=3, REASON_WINDOW_LIMIT=4 };