Skip to content

Conversation

@NSFatalError
Copy link
Owner

@NSFatalError NSFatalError commented Apr 25, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a global actor for concurrency control.
    • Added tests to verify that time-limited operations maintain actor isolation within custom actor contexts.
  • Improvements

    • Updated the Swift version requirement to 6.1.
    • Simplified concurrency utility functions by removing actor isolation constraints from operation closures.
  • Chores

    • Adjusted the order of platform mapping logic in the GitHub Actions workflow (no impact on functionality).

@NSFatalError NSFatalError self-assigned this Apr 25, 2025
@coderabbitai
Copy link

coderabbitai bot commented Apr 25, 2025

Walkthrough

The changes introduce updates to concurrency utilities and their tests, specify a new Swift language version, and adjust a GitHub Actions workflow script. The TaskTimeLimit concurrency utility is refactored to be generic and to improve actor isolation handling by changing operation closure signatures and task group logic. Tests are enhanced with a new global actor and additional test cases to verify actor isolation during time-limited operations. The Swift version is updated to 6.1, and a workflow script is reordered for clarity without changing its logic. Similar removal of actor isolation constraints is applied to withDeadline and withTimeout functions.

Changes

File(s) Change Summary
Sources/PrincipleConcurrency/TaskTimeLimit.swift Converted TaskTimeLimit to a generic enum parameterized by Success: Sendable. Simplified nested Event enum by removing its generic parameter. Changed withTimeLimit function signature: removed isolation from the operation closure type. Added comments clarifying concurrency isolation behavior.
Sources/PrincipleConcurrency/DeadlineExceededError.swift Removed @isolated(any) attribute from the operation closure parameter type in both overloads of withDeadline functions, simplifying the closure signature by removing actor isolation constraints.
Sources/PrincipleConcurrency/TimeoutError.swift Removed @isolated(any) attribute from the operation closure parameter type in both overloads of withTimeout functions, simplifying the closure signature by removing actor isolation constraints.
Tests/PrincipleConcurrencyTests/CustomActor.swift Added a new internal global actor CustomActor with a static shared instance for use in concurrency tests.
Tests/PrincipleConcurrencyTests/TaskTimeLimitTests.swift Added two asynchronous test methods named testIsolation in Deadline and Timeout structs. These tests run tasks on @CustomActor and verify that time-limited operations preserve actor isolation by calling CustomActor.shared.assertIsolated().
.swift-version Updated the specified Swift language version from 6.0 to 6.1.
.github/workflows/pull-request.yml Reordered case branches in a shell script mapping platform names to Xcode destination strings by moving the ios case after maccatalyst and the visionos case after watchos. No changes to logic or output values.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test Function
    participant CustomActor as CustomActor
    participant TaskTimeLimit as TaskTimeLimit Utility

    Test->>CustomActor: Start Task on @CustomActor
    CustomActor->>TaskTimeLimit: Call withDeadline/withTimeout (operation: assertIsolated)
    TaskTimeLimit->>CustomActor: Execute operation with actor isolation
    CustomActor-->>TaskTimeLimit: Operation result
    TaskTimeLimit-->>CustomActor: Return result
    CustomActor-->>Test: Task completes
Loading

Poem

🐇
A hop, a skip, a test anew,
Actors guard what tasks now do.
Swift steps up to six point one,
Isolation checks are run.
Workflows shuffled, scripts align—
Concurrency’s looking fine!


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cbd3afa and b58e8d2.

📒 Files selected for processing (1)
  • Sources/PrincipleConcurrency/TaskTimeLimit.swift (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Sources/PrincipleConcurrency/TaskTimeLimit.swift
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
Tests/PrincipleConcurrencyTests/CustomActor.swift (1)

1-7: Check file creation date

The file header indicates a creation date of April 24, 2025, which is in the future. Consider updating to the correct current date.

-//  Created by Kamil Strzelecki on 24/04/2025.
-//  Copyright © 2025 Kamil Strzelecki. All rights reserved.
+//  Created by Kamil Strzelecki on 24/04/2024.
+//  Copyright © 2024 Kamil Strzelecki. All rights reserved.
Sources/PrincipleConcurrency/TaskTimeLimit.swift (1)

92-105: Well-documented workaround for Swift isolation limitations

The unpackOperation function addresses current limitations in Swift's isolation model. The comments with links to Swift evolution proposals and forum discussions are particularly helpful for understanding the rationale.

However, there's an unused parameter that could be removed:

private func unpackOperation<Success: Sendable, R>(
    _ operation: sending @escaping TaskTimeLimit<Success>.Operation,
-   callerIsolation _: isolated (any Actor)?,
    transform: (sending @escaping TaskTimeLimit<Success>.Operation, isolated(any Actor)?) -> sending R
) async -> sending R {

Also note that line 59 with _ = operationIsolation appears to be a no-op but is likely necessary due to current Swift isolation limitations as explained in the comments.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1baec71 and 687b85b.

📒 Files selected for processing (5)
  • .github/workflows/pull-request.yml (1 hunks)
  • .swift-version (1 hunks)
  • Sources/PrincipleConcurrency/TaskTimeLimit.swift (4 hunks)
  • Tests/PrincipleConcurrencyTests/CustomActor.swift (1 hunks)
  • Tests/PrincipleConcurrencyTests/TaskTimeLimitTests.swift (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
Tests/PrincipleConcurrencyTests/TaskTimeLimitTests.swift (2)
Sources/PrincipleConcurrency/DeadlineExceededError.swift (2)
  • withDeadline (30-47)
  • withDeadline (63-78)
Sources/PrincipleConcurrency/TimeoutError.swift (2)
  • withTimeout (30-47)
  • withTimeout (63-78)
Sources/PrincipleConcurrency/TaskTimeLimit.swift (1)
Sources/PrincipleConcurrency/SingleUseTransfer.swift (1)
  • finalize (79-87)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-test (visionos)
🔇 Additional comments (12)
.swift-version (1)

1-1: Swift version updated to 6.1

This update aligns with the concurrent enhancements in the codebase, specifically the actor isolation improvements being implemented in this PR.

.github/workflows/pull-request.yml (2)

73-75: Reordering of case branches looks good

The iOS platform case was moved after maccatalyst, which doesn't affect the functionality since each case is handled independently.


82-84: Reordering of case branches looks good

The visionOS platform case was moved after watchOS, which doesn't affect the functionality since each case is handled independently.

Tests/PrincipleConcurrencyTests/CustomActor.swift (1)

9-13: LGTM: Well-defined global actor for testing

The global actor definition looks good. It provides a simple way to test actor isolation in the time-limited operations.

Since this is used for testing actor isolation, you might want to add an assertIsolated() method to validate isolation. Something like:

func assertIsolated() {
    // Empty method used to verify actor isolation
}
Tests/PrincipleConcurrencyTests/TaskTimeLimitTests.swift (2)

58-66: LGTM: Good test for actor isolation with deadline

This test correctly verifies that actor isolation is preserved when using withDeadline. The test creates a task with @CustomActor isolation and verifies that the actor context is correctly propagated within the time-limited operation.


113-121: LGTM: Good test for actor isolation with timeout

This test correctly verifies that actor isolation is preserved when using withTimeout. The test creates a task with @CustomActor isolation and verifies that the actor context is correctly propagated within the time-limited operation.

Sources/PrincipleConcurrency/TaskTimeLimit.swift (6)

12-12: Generic enum enhances type safety and code reuse

The change from a non-generic private enum to a generic internal enum parameterized by Success: Sendable improves type safety and allows for better code reuse across the module.


14-15: Good use of type alias for improved readability

The new Operation type alias clearly defines the operation signature with explicit isolation annotation, improving code readability and maintainability.


16-16: Appropriate visibility level for Event enum

Making the Event enum fileprivate and non-generic is a good choice as it relies on the outer generic parameter while restricting access to just this file.


30-31: Improved parameter naming and type safety

Renaming the isolation parameter to callerIsolation is more descriptive, and using the new TaskTimeLimit<Success>.Operation type with the sending keyword improves type safety and aligns with Swift's ownership model.


36-39: Consistent generic type parameter and parameter naming

The changes to use TaskTimeLimit<Success>.Event.self and isolation: callerIsolation maintain consistency with the other changes in the file.


49-69:

Details

✅ Verification successful

Complex but necessary isolation handling

The new approach to adding the operation task to the group is more complex but addresses actor isolation concerns more effectively. The pattern of creating an unsafe mutable reference with a proper defer block for restoration is appropriate in this context.

However, the code could be vulnerable to isolation violations if the Swift concurrency model changes in the future.


🏁 Script executed:

#!/bin/bash
# Check if there are any other uses of nonisolated(unsafe) in the codebase
# that might indicate established patterns
rg "nonisolated\(unsafe\)" --type swift

Length of output: 332


Nonisolated(unsafe) usage is intentional and correctly scoped

I ran a search for other nonisolated(unsafe) uses and found only this instance in TaskTimeLimit.swift (the only other mention is a commented example in SingleUseTransfer.swift). Given its singular, well-scoped application here—and the proper defer to restore actor isolation—the pattern is both necessary and correctly implemented. No further changes are required at this time.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
Sources/PrincipleConcurrency/TaskTimeLimit.swift (1)

94-103: Consider documentation for unpackOperation helper.

The unpackOperation function implements a critical piece of the isolation extraction logic. Consider adding a documentation comment explaining its purpose and behavior beyond just providing links to external resources.

 private func unpackOperation<Success: Sendable, R>(
     _ operation: sending @escaping TaskTimeLimit<Success>.Operation,
     callerIsolation _: isolated (any Actor)?,
     transform: (sending @escaping TaskTimeLimit<Success>.Operation, isolated(any Actor)?) -> sending R
 ) async -> sending R {
+    /// Extracts the actor isolation from the operation closure and passes it along with the operation
+    /// to the transform closure. This allows preserving the original isolation context when
+    /// executing the operation in a different task.
     // https://github.com/swiftlang/swift-evolution/blob/main/proposals/0461-async-function-isolation.md
     // https://forums.swift.org/t/closure-isolation-control/70378
     let operationIsolation = extractIsolation(operation)
     return await transform(operation, operationIsolation)
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 687b85b and 01301a5.

📒 Files selected for processing (1)
  • Sources/PrincipleConcurrency/TaskTimeLimit.swift (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
Sources/PrincipleConcurrency/TaskTimeLimit.swift (1)
Sources/PrincipleConcurrency/SingleUseTransfer.swift (1)
  • finalize (79-87)
🔇 Additional comments (4)
Sources/PrincipleConcurrency/TaskTimeLimit.swift (4)

12-15: Well-structured generic implementation.

The change to make TaskTimeLimit generic with Success: Sendable improves type safety and makes the API more flexible. The Operation typealias clearly defines the closure requirements.


16-21: Good encapsulation for Event enum.

Making the Event enum fileprivate and non-generic is appropriate since it relies on the outer generic parameter and only needs to be accessible within this file.


30-31: Improved parameter naming and type safety.

Renaming isolation to callerIsolation makes the purpose clearer. Using the defined typealias for the operation parameter provides better type safety and readability.


49-71:

Details

✅ Verification successful

Verify the isolation handling approach.

The approach using nonisolated(unsafe) with a deferred restore is complex but appears necessary for the isolation requirements. The comments about Swift's isolation behavior are helpful for understanding the implementation.


🏁 Script executed:

#!/bin/bash
# Check if there are any similar patterns in the codebase
rg -U "nonisolated\(unsafe\) var.*\ndefer \{ .* = consume .* \}" --type swift

Length of output: 79


Isolation handling approach approved

The nonisolated(unsafe) var unsafeGroup = group with defer { group = consume unsafeGroup } pattern is unique to this context (no other matches found in the codebase) and appropriately addresses Swift’s actor isolation rules. The inline comment and forum link clearly explain why this complexity is necessary.

• File: Sources/PrincipleConcurrency/TaskTimeLimit.swift
• Lines: 49–71

No further changes required.

Comment on lines 101 to 102
let operationIsolation = extractIsolation(operation)
return await transform(operation, operationIsolation)
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify extractIsolation function implementation.

The code relies on an extractIsolation function that's not shown in this file. Ensure this function correctly extracts the actor isolation from the operation closure.


🏁 Script executed:

#!/bin/bash
# Look for the extractIsolation function implementation
rg -A 5 "func extractIsolation" --type swift

Length of output: 46


Let’s search the entire repo for any definition or reference of extractIsolation:


🏁 Script executed:

#!/bin/bash
# Search for any occurrences of extractIsolation in the codebase
rg -n "extractIsolation"

Length of output: 134


Implement or Import extractIsolation

It looks like extractIsolation(_:) is called on line 101 of Sources/PrincipleConcurrency/TaskTimeLimit.swift but no implementation or import exists in the repo. You’ll need to:

• Add the missing func extractIsolation<T>(_ operation: @escaping () -> T) -> ActorIsolation (or appropriate signature)
• Or import the module/extension that provides it

Without this definition, the actor‐isolation logic in TaskTimeLimit.swift can’t compile or function correctly. Please include or reference the correct implementation.

Comment on lines 59 to 61
// NOTE: #isolation != operationIsolation
// https://forums.swift.org/t/explicitly-captured-isolated-parameter-does-not-change-isolation-of-sendable-sending-closures/79502
_ = operationIsolation
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Ensure isolation comment remains current.

The comment references a Swift forums discussion about isolation behavior. This is informative but Swift's concurrency model is evolving rapidly.


🌐 Web query:

What are the current limitations of Swift actor isolation with captured parameters?

💡 Result:

Current Limitations of Swift Actor Isolation with Captured Parameters

Swift’s actor isolation model, while robust, comes with certain limitations—particularly regarding how parameters, especially captured values like closures or references to self, interact with an actor’s isolation boundary. The following summarizes the main limitations as they stand in Swift 6.

Default Isolation and Capturing self

  • By default, all instance methods and properties of an actor are actor-isolated, meaning they can only be accessed from within the actor's own context or via an await boundary[1][3].
  • When capturing parameters such as self in closures that escape the current actor context, Swift enforces strong restrictions. If the captured parameter is non-Sendable, this is flagged by the compiler because it risks being accessed from multiple isolation contexts, which could cause data races[6][8][9].
  • You cannot refer to actor-isolated state (e.g., properties or methods) in a closure unless that closure is guaranteed to run on the same actor. Attempting to do so without proper isolation leads to compile-time errors:

error: actor-isolated property 'balance' cannot be referenced on non-isolated parameter 'self'[1][3][9].

Nonisolated Keyword and Sendability

  • You can mark actor properties or methods as nonisolated, which allows them to be accessed outside actor isolation. However, such methods can only refer to other non-isolated data; accessing actor-isolated state from a non-isolated context is not allowed[1][3].
  • Any value passed across actor boundaries (including captured in closures or as parameters) must conform to Sendable, or else you get a compile-time error[1][6][8].
  • Returning a non-Sendable type from a nonisolated function is forbidden, as non-isolated declarations can be called from any concurrency domain[1].

Limitations with Isolated Parameters

  • While Swift 6 introduces more flexible actor isolation with isolated parameters, there are syntactic and practical constraints:
    • The pattern for inheriting actor isolation via isolated parameters is verbose and can't be consistently used everywhere, particularly in accessors or other contexts where you can't add parameters[5].
    • When you pass a closure that captures an actor-isolated value (e.g., self or a property), you must ensure that the closure only runs within the correct isolation context, otherwise the compiler warns that later accesses could race[9].
    • The feature for inheriting caller’s actor isolation is under development, and current workarounds (like passing an isolated (any Actor)? parameter) have performance or ergonomic downsides[4][5][7].

Inout Parameters

  • Passing actor-isolated stored properties as inout parameters to synchronous functions is allowed within the actor, but is prohibited for asynchronous functions, as this could cause exclusivity violations if the async call suspends and other tasks try to access the same property[2].

Summary Table: Key Limitations

Scenario Limitation
Capturing actor-isolated self Not allowed across isolation boundaries unless Sendable and handled correctly
Nonisolated methods/properties Can only access non-isolated data; cannot reference actor-isolated state
Non-Sendable types in nonisolated context Forbidden
Passing closures across isolation Closure must not capture non-Sendable or actor-isolated values unless properly isolated
Inheriting actor isolation Syntax is currently verbose and not universally applicable; more ergonomic solutions proposed
Inout parameters in async calls Cannot pass actor-isolated properties as inout to async functions

These constraints are all designed to prevent data races and ensure thread safety, but they do introduce complexity and sometimes ergonomic hurdles for developers dealing with actor isolation and captured parameters in Swift[1][2][3][5][6][9].

Citations:


Update isolation comment to reflect current Swift 6 semantics

The existing note points to an older forum discussion but doesn’t explain why the workaround is still necessary in Swift 6. Please update the inline comment for clarity and to reference the latest evolution proposal.

  • File: Sources/PrincipleConcurrency/TaskTimeLimit.swift
    Lines: 59–61
  • Replace the existing note with an updated explanation and link to SE-420.

Suggested diff:

-                                // NOTE: #isolation != operationIsolation
-                                // https://forums.swift.org/t/explicitly-captured-isolated-parameter-does-not-change-isolation-of-sendable-sending-closures/79502
-                                _ = operationIsolation
+                                // As of Swift 6, capturing an `isolated` parameter does *not* propagate actor isolation into sendable closures.
+                                // An explicit `ActorIsolation` token is required to maintain the actor’s isolation context.
+                                // See SE-420 “Inheritance of Actor Isolation” for upcoming ergonomic improvements:
+                                // https://github.com/apple/swift-evolution/blob/main/proposals/0420-inheritance-of-actor-isolation.md
+                                _ = operationIsolation
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// NOTE: #isolation != operationIsolation
// https://forums.swift.org/t/explicitly-captured-isolated-parameter-does-not-change-isolation-of-sendable-sending-closures/79502
_ = operationIsolation
// As of Swift 6, capturing an `isolated` parameter does *not* propagate actor isolation into sendable closures.
// An explicit `ActorIsolation` token is required to maintain the actor’s isolation context.
// See SE-420 “Inheritance of Actor Isolation” for upcoming ergonomic improvements:
// https://github.com/apple/swift-evolution/blob/main/proposals/0420-inheritance-of-actor-isolation.md
_ = operationIsolation

@NSFatalError NSFatalError merged commit e132d11 into main Apr 25, 2025
1 check passed
@NSFatalError NSFatalError deleted the feature/isolation-extraction branch April 25, 2025 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants