Skip to content

Commit

Permalink
Implement TaskFactory (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
ns-vasilev authored Jan 28, 2025
1 parent 74e065d commit 7102a40
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 0 deletions.
105 changes: 105 additions & 0 deletions Sources/Concurrency/TaskFactory/ITaskFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//
// Concurrency
// Copyright © 2025 Space Code. All rights reserved.
//

import Foundation

// MARK: - ITaskFactory

// swiftlint:disable attributes
/// A protocol for creating and managing tasks with different configurations.
/// Provides methods to create tasks tied to the current actor context or detached tasks
/// that run independently of the current actor.
public protocol ITaskFactory {
/// Creates a task tied to the current actor context that can throw errors.
/// - Parameters:
/// - priority: The priority of the task (optional).
/// - operation: An asynchronous operation to execute within the task. The operation
/// inherits the current actor context and is isolated to that actor.
/// - Returns: A `Task` object that wraps the result or error of the operation.
func task<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async throws -> Success
) -> Task<Success, Error>

/// Creates a task tied to the current actor context that does not throw errors.
/// - Parameters:
/// - priority: The priority of the task (optional).
/// - operation: An asynchronous operation to execute within the task. The operation
/// inherits the current actor context and is isolated to that actor.
/// - Returns: A `Task` object that wraps the result of the operation.
func task<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async -> Success
) -> Task<Success, Never>

/// Creates a detached task that runs independently of the current actor context
/// and can throw errors.
/// - Parameters:
/// - priority: The priority of the task (optional).
/// - operation: An asynchronous operation to execute within the task. The operation
/// is isolated and does not inherit the current actor context.
/// - Returns: A `Task` object that wraps the result or error of the operation.
func detached<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async throws -> Success
) -> Task<Success, Error>

/// Creates a detached task that runs independently of the current actor context
/// and does not throw errors.
/// - Parameters:
/// - priority: The priority of the task (optional).
/// - operation: An asynchronous operation to execute within the task. The operation
/// is isolated and does not inherit the current actor context.
/// - Returns: A `Task` object that wraps the result of the operation.
func detached<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async -> Success
) -> Task<Success, Never>
}

/// Default implementations for the `ITaskFactory` protocol.
public extension ITaskFactory {
/// Creates a task tied to the current actor context with a default priority
/// that can throw errors.
/// - Parameter operation: An asynchronous operation to execute within the task.
/// - Returns: A `Task` object that wraps the result or error of the operation.
func task<Success: Sendable>(
@_inheritActorContext operation: sending @escaping @isolated(any) () async throws -> Success
) -> Task<Success, Error> {
task(priority: nil, operation: operation)
}

/// Creates a task tied to the current actor context with a default priority
/// that does not throw errors.
/// - Parameter operation: An asynchronous operation to execute within the task.
/// - Returns: A `Task` object that wraps the result of the operation.
func task<Success: Sendable>(
@_inheritActorContext operation: sending @escaping @isolated(any) () async -> Success
) -> Task<Success, Never> {
task(priority: nil, operation: operation)
}

/// Creates a detached task with a default priority that runs independently
/// of the current actor context and can throw errors.
/// - Parameter operation: An asynchronous operation to execute within the task.
/// - Returns: A `Task` object that wraps the result or error of the operation.
func detached<Success: Sendable>(
@_inheritActorContext operation: sending @escaping @isolated(any) () async throws -> Success
) -> Task<Success, Error> {
detached(priority: nil, operation: operation)
}

/// Creates a detached task with a default priority that runs independently
/// of the current actor context and does not throw errors.
/// - Parameter operation: An asynchronous operation to execute within the task.
/// - Returns: A `Task` object that wraps the result of the operation.
func detached<Success: Sendable>(
@_inheritActorContext operation: sending @escaping @isolated(any) () async -> Success
) -> Task<Success, Never> {
detached(priority: nil, operation: operation)
}
}

// swiftlint:enable attributes
42 changes: 42 additions & 0 deletions Sources/Concurrency/TaskFactory/TaskFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// Concurrency
// Copyright © 2025 Space Code. All rights reserved.
//

public final class TaskFactory: ITaskFactory {
// MARK: Initialization

public init() {}

// MARK: ITaskFactory

// swiftlint:disable attributes
public func task<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async throws -> Success
) -> Task<Success, Error> {
Task(priority: priority, operation: operation)
}

public func task<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async -> Success
) -> Task<Success, Never> {
Task(priority: priority, operation: operation)
}

public func detached<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async throws -> Success
) -> Task<Success, Error> {
Task.detached(priority: priority, operation: operation)
}

public func detached<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async -> Success
) -> Task<Success, Never> {
Task.detached(priority: priority, operation: operation)
}
// swiftlint:enable attributes
}
21 changes: 21 additions & 0 deletions Sources/TestConcurrency/Task.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// Concurrency
// Copyright © 2025 Space Code. All rights reserved.
//

// MARK: - ITask

/// A protocol representing a task that can be awaited until its execution completes.
protocol ITask {
/// Waits for the `Task` to complete by retrieving its result.
func wait() async
}

// MARK: - Task + ITask

/// Extends the `Task` type to conform to the `ITask` protocol.
extension Task: ITask {
func wait() async {
_ = await result
}
}
83 changes: 83 additions & 0 deletions Sources/TestConcurrency/TestTaskFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//
// Concurrency
// Copyright © 2025 Space Code. All rights reserved.
//

import Concurrency
import Foundation

// MARK: - TestTaskFactory

public final class TestTaskFactory: @unchecked Sendable {
// MARK: Properties

private let lock = NSLock()
private var tasks: [ITask] = []

// MARK: Intialization

public init() {}

// MARK: Public

/// Waits until all tasks in the queue have completed execution.
public func waitUntilIdle() async {
while let task = popTask() {
await task.wait()
}
}

// MARK: Private

private func addTask(_ task: ITask) {
lock.lock()
defer { lock.unlock() }
tasks.append(task)
}

private func popTask() -> ITask? {
lock.lock()
defer { lock.unlock() }
return tasks.popLast()
}
}

// MARK: ITaskFactory

extension TestTaskFactory: ITaskFactory {
public func task<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async throws -> Success
) -> Task<Success, Error> {
let task = Task(priority: priority, operation: operation)
addTask(task)
return task
}

public func task<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async -> Success
) -> Task<Success, Never> {
let task = Task(priority: priority, operation: operation)
addTask(task)
return task
}

public func detached<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async throws -> Success
) -> Task<Success, Error> {
let task = Task.detached(priority: priority, operation: operation)
addTask(task)
return task
}

public func detached<Success: Sendable>(
priority: TaskPriority?,
@_inheritActorContext operation: sending @escaping @isolated(any) () async -> Success
) -> Task<Success, Never> {
let task = Task.detached(priority: priority, operation: operation)
addTask(task)
return task
}
}

0 comments on commit 7102a40

Please sign in to comment.