Skip to content

Add class to support reacting to power state changes - #809

Merged
mlw merged 3 commits into
mainfrom
mlw/power-monitor
Feb 23, 2026
Merged

Add class to support reacting to power state changes#809
mlw merged 3 commits into
mainfrom
mlw/power-monitor

Conversation

@mlw

@mlw mlw commented Feb 23, 2026

Copy link
Copy Markdown
Contributor

Allow callers to provide a block that gets called during power state changes in order to perform some operation.

Part of SNT-261

@mlw mlw added this to the 2026.2 milestone Feb 23, 2026
@mlw
mlw requested a review from a team as a code owner February 23, 2026 21:08
@github-actions github-actions Bot added lang/objc++ PRs modifying files in ObjC++ comp/common size/m Size: medium labels Feb 23, 2026
@coderabbitai

coderabbitai Bot commented Feb 23, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

A new PowerMonitor utility is introduced to monitor system power events via IOKit. It includes a public enum defining power event types, a factory-created class using PassKey pattern for construction, IOKit integration for system power notifications, and comprehensive unit tests.

Changes

Cohort / File(s) Summary
Build Configuration
Source/common/BUILD
Added PowerMonitor library target linking IOKit with dependencies on PassKey and SNTLogging, plus corresponding unit test target.
PowerMonitor Public Interface
Source/common/PowerMonitor.h
Defined PowerEvent enum (kCanSleep, kWillNotSleep, kWillSleep, kWillPowerOn, kHasPoweredOn), PowerEventBlock callback type alias, and non-copyable/non-movable PowerMonitor class with factory Create method and PassKey-controlled constructor.
PowerMonitor Implementation
Source/common/PowerMonitor.mm
Implemented factory creation with IOKit registration, constructor initializing IOKit handles, destructor with proper cleanup (deregistration, queue drain, port destruction), static PowerCallback wrapper routing to instance HandlePowerEvent, and power message translation to PowerEvent enum with IOAllowPowerChange handling.
PowerMonitor Tests
Source/common/PowerMonitorTest.mm
Added unit tests validating factory behavior with/without callbacks, safe construction/destruction, and coexistence of multiple monitors.

Sequence Diagram

sequenceDiagram
    participant User as User Code
    participant PM as PowerMonitor::Create
    participant IOKit as IOKit/System
    participant DQ as Dispatch Queue
    participant Callback as Power Event Callback

    User->>PM: Create(PowerEventBlock callback)
    PM->>IOKit: IORegisterForSystemPower()
    IOKit->>PM: Returns io_connect_t, IONotificationPortRef, io_object_t
    PM->>DQ: CFRunLoopAddSource(notification_port)
    PM->>User: Returns std::unique_ptr<PowerMonitor>

    IOKit->>PM: PowerCallback(power_message)
    PM->>PM: HandlePowerEvent(message_type)
    PM->>IOKit: IOAllowPowerChange(if needed)
    PM->>DQ: async dispatch to queue
    DQ->>Callback: Invoke with PowerEvent enum
    Callback->>User: Power state notification
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main addition: a new class (PowerMonitor) that enables reacting to power state changes through callbacks.
Description check ✅ Passed The description directly relates to the changeset, explaining the purpose of the PowerMonitor class and its callback mechanism for power state changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mlw/power-monitor

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
Source/common/PowerMonitor.mm (2)

97-103: IOAllowPowerChange is called after the user callback — a slow callback delays the power transition.

For both kIOMessageCanSystemSleep and kIOMessageSystemWillSleep, the callback runs before IOAllowPowerChange. If the callback performs expensive work (disk flush, network call, etc.), it delays the kernel's power state transition for that duration. This is probably intentional (giving the caller time to prepare), but it's worth documenting that the callback should complete promptly, or the system sleep can be delayed (the kernel has a ~30 s timeout for kIOMessageCanSystemSleep responses).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Source/common/PowerMonitor.mm` around lines 97 - 103, The power-change
acknowledgement is currently issued after invoking callback_ for
kIOMessageCanSystemSleep and kIOMessageSystemWillSleep which lets a slow
callback delay the kernel power transition; move the
IOAllowPowerChange(connect_, reinterpret_cast<long>(message_argument)) call to
occur immediately when handling both kIOMessageCanSystemSleep and
kIOMessageSystemWillSleep (before calling callback_), and add a brief comment
near callback_ and PowerEvent noting that callbacks must return promptly (they
may block system sleep / have kernel timeouts).

95-110: kIOMessageCanSystemSleep always allows sleep — intentional?

Currently, HandlePowerEvent unconditionally calls IOAllowPowerChange for kIOMessageCanSystemSleep. This means the callback is notified but cannot deny sleep (i.e., IOCancelPowerChange is never called). If that's a deliberate simplification, a brief comment would be helpful for future maintainers. If callers may eventually need to veto idle sleep, the PowerEventBlock signature would need to return a decision value.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Source/common/PowerMonitor.mm` around lines 95 - 110, HandlePowerEvent
currently unconditionally calls IOAllowPowerChange for kIOMessageCanSystemSleep
so callers cannot veto sleep; either document this deliberate behavior with a
clear comment in HandlePowerEvent next to the kIOMessageCanSystemSleep case, or
change the PowerEventBlock (callback_) signature to return a decision (e.g.,
bool) and update HandlePowerEvent to call IOAllowPowerChange when callback_
returns true and IOCancelPowerChange when it returns false (adjust all call
sites and tests to the new PowerEventBlock signature).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Source/common/PowerMonitor.mm`:
- Around line 97-103: The power-change acknowledgement is currently issued after
invoking callback_ for kIOMessageCanSystemSleep and kIOMessageSystemWillSleep
which lets a slow callback delay the kernel power transition; move the
IOAllowPowerChange(connect_, reinterpret_cast<long>(message_argument)) call to
occur immediately when handling both kIOMessageCanSystemSleep and
kIOMessageSystemWillSleep (before calling callback_), and add a brief comment
near callback_ and PowerEvent noting that callbacks must return promptly (they
may block system sleep / have kernel timeouts).
- Around line 95-110: HandlePowerEvent currently unconditionally calls
IOAllowPowerChange for kIOMessageCanSystemSleep so callers cannot veto sleep;
either document this deliberate behavior with a clear comment in
HandlePowerEvent next to the kIOMessageCanSystemSleep case, or change the
PowerEventBlock (callback_) signature to return a decision (e.g., bool) and
update HandlePowerEvent to call IOAllowPowerChange when callback_ returns true
and IOCancelPowerChange when it returns false (adjust all call sites and tests
to the new PowerEventBlock signature).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e8c53e2 and c1f7e44.

📒 Files selected for processing (4)
  • Source/common/BUILD
  • Source/common/PowerMonitor.h
  • Source/common/PowerMonitor.mm
  • Source/common/PowerMonitorTest.mm

@mlw
mlw merged commit 202fd5c into main Feb 23, 2026
7 checks passed
@mlw
mlw deleted the mlw/power-monitor branch February 23, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/common lang/objc++ PRs modifying files in ObjC++ size/m Size: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants