# Milestone 5: Timer & Callback System

**Status**: Complete
**Goal**: Safe timer APIs managed by kernel.

---

## Overview

This milestone implements JavaScript-style timer APIs (`setTimeout`, `setInterval`) that are:
- Managed by the kernel (not Lua coroutines)
- Subject to per-app limits
- Properly cleaned up when apps stop
- Integrated with sandbox instruction counting

### Key Deliverables

1. **TimerManager class** - Central timer management
2. **Lua timer APIs** - setTimeout, setInterval, clearTimeout, clearInterval
3. **Kernel integration** - Fire timers from main loop

---

## File Structure

```
src/main/cpp/sandbox/
├── lua_sandbox.h           # (existing)
├── lua_sandbox.cpp         # (existing)
├── timer_manager.h         # NEW - Timer management
└── timer_manager.cpp       # NEW - Implementation
```

---

## Implementation Details

### 1. TimerManager Class

```cpp
// timer_manager.h
#pragma once

#include <string>
#include <functional>
#include <queue>
#include <unordered_map>
#include <chrono>
#include <mutex>

struct lua_State;

namespace mosis {

using TimerId = uint64_t;
using TimePoint = std::chrono::steady_clock::time_point;
using Duration = std::chrono::milliseconds;

struct Timer {
    TimerId id;
    std::string app_id;
    TimePoint fire_time;
    Duration interval;      // 0 for setTimeout, >0 for setInterval
    int callback_ref;       // Lua registry reference
    lua_State* L;           // Lua state that owns the callback
    bool cancelled = false;
};

class TimerManager {
public:
    TimerManager();
    ~TimerManager();

    // Create timers (returns timer ID)
    TimerId SetTimeout(lua_State* L, const std::string& app_id,
                       int callback_ref, int delay_ms);
    TimerId SetInterval(lua_State* L, const std::string& app_id,
                        int callback_ref, int interval_ms);

    // Cancel timers
    bool ClearTimer(const std::string& app_id, TimerId id);

    // Cancel all timers for an app
    void ClearAppTimers(const std::string& app_id);

    // Process timers (call from main loop)
    // Returns number of timers fired
    int ProcessTimers();

    // Get timer count for an app
    size_t GetTimerCount(const std::string& app_id) const;

    // Configuration
    static constexpr size_t MAX_TIMERS_PER_APP = 100;
    static constexpr int MIN_INTERVAL_MS = 10;
    static constexpr int MIN_TIMEOUT_MS = 0;

private:
    struct TimerCompare {
        bool operator()(const Timer& a, const Timer& b) const {
            return a.fire_time > b.fire_time;  // Min-heap
        }
    };

    TimerId m_next_id = 1;
    std::priority_queue<Timer, std::vector<Timer>, TimerCompare> m_timers;
    std::unordered_map<std::string, size_t> m_app_timer_counts;
    mutable std::mutex m_mutex;

    void FireTimer(Timer& timer);
};

// Lua API registration
void RegisterTimerAPI(lua_State* L, TimerManager* manager, const std::string& app_id);

} // namespace mosis
```

### 2. Lua Timer APIs

```lua
-- setTimeout: fire callback once after delay
local id = setTimeout(function()
    print("Fired after 1000ms")
end, 1000)

-- clearTimeout: cancel a pending timeout
clearTimeout(id)

-- setInterval: fire callback repeatedly
local id = setInterval(function()
    print("Fires every 500ms")
end, 500)

-- clearInterval: stop an interval
clearInterval(id)
```

### 3. Timer Limits

| Limit | Value | Reason |
|-------|-------|--------|
| Max timers per app | 100 | Prevent resource exhaustion |
| Min interval | 10ms | Prevent CPU spinning |
| Min timeout | 0ms | Allow immediate callbacks |

---

## Test Cases

### Test 1: SetTimeout Fires

```cpp
bool Test_SetTimeoutFires(std::string& error_msg) {
    mosis::TimerManager manager;
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    // Register timer API
    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    // Set a timeout
    sandbox.LoadString(R"(
        fired = false
        setTimeout(function() fired = true end, 50)
    )", "timeout_test");

    // Process timers after delay
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    manager.ProcessTimers();

    // Check if callback fired
    sandbox.LoadString("assert(fired == true)", "check");

    return true;
}
```

### Test 2: SetInterval Fires Multiple Times

```cpp
bool Test_SetIntervalFires(std::string& error_msg) {
    mosis::TimerManager manager;
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    sandbox.LoadString(R"(
        count = 0
        setInterval(function() count = count + 1 end, 30)
    )", "interval_test");

    // Process multiple times
    for (int i = 0; i < 5; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(40));
        manager.ProcessTimers();
    }

    // Should have fired multiple times
    sandbox.LoadString("assert(count >= 3)", "check");

    return true;
}
```

### Test 3: ClearTimeout Cancels

```cpp
bool Test_ClearTimeoutCancels(std::string& error_msg) {
    mosis::TimerManager manager;
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    sandbox.LoadString(R"(
        fired = false
        local id = setTimeout(function() fired = true end, 100)
        clearTimeout(id)
    )", "clear_test");

    std::this_thread::sleep_for(std::chrono::milliseconds(150));
    manager.ProcessTimers();

    // Should NOT have fired
    sandbox.LoadString("assert(fired == false)", "check");

    return true;
}
```

### Test 4: Timer Limit Enforced

```cpp
bool Test_TimerLimitEnforced(std::string& error_msg) {
    mosis::TimerManager manager;
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    // Try to create too many timers
    sandbox.LoadString(R"(
        for i = 1, 150 do
            setTimeout(function() end, 1000000)
        end
    )", "limit_test");

    // Should be capped at MAX_TIMERS_PER_APP
    EXPECT_TRUE(manager.GetTimerCount(ctx.app_id) <= 100);

    return true;
}
```

### Test 5: ClearAppTimers Cleanup

```cpp
bool Test_ClearAppTimersCleanup(std::string& error_msg) {
    mosis::TimerManager manager;
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    sandbox.LoadString(R"(
        for i = 1, 10 do
            setTimeout(function() end, 1000000)
        end
    )", "cleanup_test");

    EXPECT_TRUE(manager.GetTimerCount(ctx.app_id) == 10);

    // Clear all timers for app
    manager.ClearAppTimers(ctx.app_id);

    EXPECT_TRUE(manager.GetTimerCount(ctx.app_id) == 0);

    return true;
}
```

### Test 6: Minimum Interval Enforced

```cpp
bool Test_MinIntervalEnforced(std::string& error_msg) {
    mosis::TimerManager manager;
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    // Try to set interval less than minimum
    sandbox.LoadString(R"(
        count = 0
        setInterval(function() count = count + 1 end, 1)  -- 1ms, should be clamped to 10ms
    )", "min_interval_test");

    // With 1ms interval, in 50ms we'd get 50 callbacks
    // With 10ms minimum, we should get ~5
    std::this_thread::sleep_for(std::chrono::milliseconds(55));
    for (int i = 0; i < 10; i++) {
        manager.ProcessTimers();
    }

    sandbox.LoadString("assert(count <= 10)", "check");

    return true;
}
```

---

## Acceptance Criteria

All tests must pass:

- [x] `Test_SetTimeoutFires` - Timeout fires after delay
- [x] `Test_SetIntervalFires` - Interval fires repeatedly
- [x] `Test_ClearTimeoutCancels` - Cancelled timeout doesn't fire
- [x] `Test_ClearIntervalCancels` - Cancelled interval stops
- [x] `Test_TimerLimitEnforced` - Max 100 timers per app
- [x] `Test_ClearAppTimersCleanup` - All app timers cleared on stop
- [x] `Test_MinIntervalEnforced` - Interval clamped to 10ms minimum

---

## Next Steps

After Milestone 5 passes:
1. Milestone 6: JSON & Crypto APIs
2. Milestone 7: Virtual Filesystem
