Improve formatting and update readme

This commit is contained in:
Paolo Di Lorenzo
2025-05-30 21:58:25 -04:00
parent f2174f4d33
commit 2325363373
7 changed files with 45 additions and 45 deletions
+6
View File
@@ -1,3 +1,9 @@
# [unreleased]
### Breaking Changes
* `ChessKitEngine` now supports Swift 6 concurrency.
* Starting and stopping engine, as well as sending commands must now be done using `async`/`await`.
# ChessKitEngine 0.5.0
Released Tuesday, September 10, 2024.
+19 -20
View File
@@ -7,8 +7,8 @@ import ChessKitEngineCore
public final class Engine: Sendable {
//MARK: - Public properties
// MARK: - Public properties
/// The type of the engine.
public let type: EngineType
@@ -41,9 +41,9 @@ public final class Engine: Sendable {
get async { await engineConfigurationActor.asyncStream }
}
//MARK: - Private properties
///Actor used to hold mutating data in a thread safe environment.
// MARK: - Private properties
/// Actor used to hold mutating data in a thread safe environment.
private let engineConfigurationActor: EngineConfiguration
/// Messenger used to communicate with engine.
@@ -54,8 +54,8 @@ public final class Engine: Sendable {
qos: .userInteractive
)
//MARK: - Life cycle functions
// MARK: - Life cycle
/// Initializes an engine with the provided ``EngineType`` and optional logging enabled flag.
///
/// - parameter type: The type of engine to use.
@@ -69,14 +69,14 @@ public final class Engine: Sendable {
// This no longer work in an async environment as stop function outlives the deinit function.
// Support for async deinit should be added in a future version of Swift (6.1)
// Support for async deinit should be added in a future version of Swift (6.2)
// https://github.com/swiftlang/swift-evolution/blob/main/proposals/0371-isolated-synchronous-deinit.md
// deinit {
// stop()
// }
//MARK: - Public functions
// MARK: - Public functions
/// Starts the engine.
///
/// You must call this function and wait for ``EngineResponse/readyok``
@@ -92,7 +92,7 @@ public final class Engine: Sendable {
coreCount: Int? = nil,
multipv: Int = 1
) async {
//Setup async stream response if not already set.
// Setup async stream response if not already set.
await engineConfigurationActor.setAsyncStream()
setMessengerResponseHandler(coreCount: coreCount, multipv: multipv)
@@ -222,12 +222,11 @@ public final class Engine: Sendable {
}
//MARK: EngineConfiguration actor
// MARK: - EngineConfiguration actor
//An actor to hold the configuration for the engine class.
//Since engine now conforms to sendable protocol, we need to
//move the mutable data into async safe environment.
//
/// An actor to hold the configuration for the engine class.
/// Since `Engine` conforms to `Sendable` protocol, we need to
/// move the mutable data into async safe environment.
fileprivate actor EngineConfiguration: Sendable {
/// Whether the engine is currently running.
private(set) var isRunning = false
@@ -263,16 +262,16 @@ fileprivate actor EngineConfiguration: Sendable {
}
func setAsyncStream() async {
guard self.asyncStream == nil else { return }
guard asyncStream == nil else { return }
self.asyncStream = AsyncStream { (continuation: AsyncStream<EngineResponse>.Continuation) -> Void in
asyncStream = AsyncStream { (continuation: AsyncStream<EngineResponse>.Continuation) -> Void in
Task{ await setStreamContinuation(continuation) }
}
}
func clearAsyncStream() async {
self.asyncStream = nil
self.streamContinuation = nil
asyncStream = nil
streamContinuation = nil
}
private func setStreamContinuation(_ continuation: AsyncStream<EngineResponse>.Continuation?) async {
@@ -5,7 +5,6 @@
/// Possible engine commands based on the
/// [Universal Chess Interface (UCI)](https://backscattering.de/chess/uci/2006-04.txt).
///
public enum EngineCommand: Equatable, Sendable {
/// `"debug [ on | off ]"`
@@ -166,7 +165,7 @@ public enum EngineCommand: Equatable, Sendable {
}
}
internal static func nextSetupLoopCommand(given response: EngineResponse?) -> EngineCommand? {
static func nextSetupLoopCommand(given response: EngineResponse?) -> EngineCommand? {
// engine setup loop
// <uci> <uciok> <isready> <readok> complete
@@ -5,7 +5,6 @@
/// Possible engine responses based on the
/// [Universal Chess Interface (UCI)](https://backscattering.de/chess/uci/2006-04.txt).
///
public enum EngineResponse: Sendable {
/// `"id name <x>"`, `"id author <x>"`
@@ -60,10 +60,10 @@ NSLock *_lock;
];
dispatch_async(dispatch_get_main_queue(), ^{
//This has to run on a thread that has an active run loop
//otherwise we don't get notified when a read occurs.
//Since we are using async, the only active run loop we can
//guarentee to have an active run loop is the main thread.
// This has to run on a thread that has an active run loop
// otherwise we don't get notified when a read occurs.
// Since we are using async, the only active run loop we can
// guarentee to have an active run loop is the main thread.
[_pipeReadHandle readInBackgroundAndNotify];
});
@@ -22,7 +22,6 @@ import XCTest
///
/// }
/// ```
///
@TestsActor
class BaseEngineTests: XCTestCase {
@@ -58,7 +57,6 @@ class BaseEngineTests: XCTestCase {
)
await startEngine(expectation: expectation)
await fulfillment(of: [expectation], timeout: 5)
}
@@ -74,9 +72,7 @@ class BaseEngineTests: XCTestCase {
)
await startEngine(expectation: expectationStartEngine)
await stopEngine(expectation: expectationStopEngine)
await fulfillment(of: [expectationStartEngine, expectationStopEngine], timeout: 5)
}
@@ -96,12 +92,11 @@ class BaseEngineTests: XCTestCase {
await startEngine(expectation: expectationStartEngine)
await stopEngine(expectation: expectationStopEngine)
await startEngine(expectation: expectationStartEngine)
await fulfillment(of: [expectationStartEngine, expectationStopEngine], timeout: 5)
}
internal func stopEngine(expectation: XCTestExpectation) async {
func stopEngine(expectation: XCTestExpectation) async {
await engine.stop()
if await !engine.isRunning,
@@ -110,7 +105,7 @@ class BaseEngineTests: XCTestCase {
}
}
internal func startEngine(expectation: XCTestExpectation) async {
func startEngine(expectation: XCTestExpectation) async {
await engine.start()
for await response in await engine.responseStream! {
@@ -131,12 +126,12 @@ class BaseEngineTests: XCTestCase {
}
}
//This actor's purpose is to ensure tests for the engine
//class aren't running on main thread.
//Since [EngineMessenger start] function now uses
//`dispatch_async(dispatch_get_main_queue, (), ^{...});`
//which is the main thread to listen for read notifications,
//testing on main thread is counter productive.
/// Ensures tests for the `Engine` class don't run on main thread.
///
/// Since `[EngineMessenger start]` function now uses
/// `dispatch_async(dispatch_get_main_queue, (), ^{...});`
/// which is the main thread to listen for read notifications,
/// testing on main thread is counter productive.
@globalActor
actor TestsActor: GlobalActor {
static var shared = TestsActor()
@@ -28,11 +28,13 @@ final class Lc0Tests: BaseEngineTests {
await startEngine(expectation: expectationStartEngine)
await stopEngine(expectation: expectationStopEngine)
//LC0 has an internal mutex failure "Unhandled exception: mutex lock failed: Invalid argument"
//when trying to stop and start the engine too fast.
//Adding this 100 ms delay circumvent that issue.
//Once this issue is resolved, this override func
//can be removed and use the EngineRestart test on BeseEngineTests
// lc0 has an internal mutex failure "Unhandled exception: mutex lock failed: Invalid argument"
// when trying to stop and start the engine too fast.
// This 100 ms delay circumvents the issue.
//
// Once this issue is resolved, this function
// can be removed, using `BaseEngineTests.testEngineRestart()` instead.
try? await Task.sleep(for: .milliseconds(100))
await startEngine(expectation: expectationStartEngine)