Skip to content

Commit

Permalink
Merge pull request #142 from PureSwift/feature/async
Browse files Browse the repository at this point in the history
Added Swift Concurrency support
colemancda authored Apr 21, 2022
2 parents ca873f2 + c12e305 commit 9a438b8
Showing 130 changed files with 1,534 additions and 1,645 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ jobs:
runs-on: macOS-latest
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v2
- name: Xcode Version
run: |
xcodebuild -version
22 changes: 0 additions & 22 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
@@ -3,28 +3,6 @@ name: Swift
on: [push]

jobs:

macOS-swift:
name: macOS
runs-on: macOS-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Xcode Version
run: |
xcodebuild -version
swift --version
- name: Swift Version
run: swift --version
- name: Build (Debug)
run: swift build -c debug
- name: Build (Release)
run: swift build -c release
- name: Test (Debug)
run: swift test --configuration debug --enable-test-discovery
- name: Test (Release)
run: swift test --configuration release -Xswiftc -enable-testing --enable-test-discovery

linux-swift:
name: Linux x86_64
runs-on: ubuntu-20.04
30 changes: 23 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
// swift-tools-version:5.1
// swift-tools-version:5.5
import PackageDescription

let libraryType: PackageDescription.Product.Library.LibraryType = .static

let package = Package(
name: "Bluetooth",
platforms: [
.macOS(.v10_15),
.iOS(.v13),
.watchOS(.v6),
.tvOS(.v13),
],
products: [
.library(
name: "Bluetooth",
type: libraryType,
targets: [
"Bluetooth",
"BluetoothGAP",
"BluetoothGATT",
"BluetoothHCI"
]
targets: ["Bluetooth"]
),
.library(
name: "BluetoothGAP",
type: libraryType,
targets: ["BluetoothGAP"]
),
.library(
name: "BluetoothGATT",
type: libraryType,
targets: ["BluetoothGATT"]
),
.library(
name: "BluetoothHCI",
type: libraryType,
targets: ["BluetoothHCI"]
)
],
targets: [
63 changes: 0 additions & 63 deletions Package@swift-5.3.swift

This file was deleted.

166 changes: 166 additions & 0 deletions Sources/Bluetooth/AsyncIndefiniteStream.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//
// AsyncIndefiniteStream.swift
//
//
// Created by Alsey Coleman Miller on 4/12/22.
//

import Foundation

/// Async Stream that will produce values until `stop()` is called or task is cancelled.
public struct AsyncIndefiniteStream <Element>: AsyncSequence {

let storage: Storage

public init(
bufferSize: Int = 100,
_ build: @escaping ((Element) -> ()) async throws -> ()
) {
let storage = Storage()
let stream = AsyncThrowingStream<Element, Error>(Element.self, bufferingPolicy: .bufferingNewest(bufferSize)) { continuation in
let task = Task {
do {
try await build({ continuation.yield($0) })
}
catch _ as CancellationError { } // end
catch {
continuation.finish(throwing: error)
}
}
storage.continuation = continuation
continuation.onTermination = { [weak storage] in
switch $0 {
case .cancelled:
storage?.stop()
default:
break
}
}
storage.onTermination = {
// cancel task when `stop` is called
task.cancel()
}
}
storage.stream = stream
self.storage = storage
}

public init(
bufferSize: Int = 100,
onTermination: @escaping () -> (),
_ build: (Continuation) -> ()
) {
let storage = Storage()
storage.onTermination = onTermination
let stream = AsyncThrowingStream<Element, Error>(Element.self, bufferingPolicy: .bufferingNewest(bufferSize)) { continuation in
storage.continuation = continuation
continuation.onTermination = { [weak storage] in
switch $0 {
case .cancelled:
storage?.stop()
default:
break
}
}
build(Continuation(continuation))
}
storage.stream = stream
self.storage = storage
}

public func makeAsyncIterator() -> AsyncIterator {
return storage.makeAsyncIterator()
}

public func stop() {
storage.stop()
}

public var isExecuting: Bool {
storage.isExecuting
}
}

public extension AsyncIndefiniteStream {

struct AsyncIterator: AsyncIteratorProtocol {

private(set) var iterator: AsyncThrowingStream<Element, Error>.AsyncIterator

init(_ iterator: AsyncThrowingStream<Element, Error>.AsyncIterator) {
self.iterator = iterator
}

@inline(__always)
public mutating func next() async throws -> Element? {
return try await iterator.next()
}
}
}

public extension AsyncIndefiniteStream {

struct Continuation {

let continuation: AsyncThrowingStream<Element, Error>.Continuation

init(_ continuation: AsyncThrowingStream<Element, Error>.Continuation) {
self.continuation = continuation
}

public func yield(_ value: Element) {
continuation.yield(value)
}

public func finish(throwing error: Error) {
continuation.finish(throwing: error)
}
}
}

internal extension AsyncIndefiniteStream {

final class Storage {

var isExecuting: Bool {
get {
lock.lock()
let value = _isExecuting
lock.unlock()
return value
}
}

private var _isExecuting = true

let lock = NSLock()

var stream: AsyncThrowingStream<Element, Error>!

var continuation: AsyncThrowingStream<Element, Error>.Continuation!

var onTermination: (() -> ())!

deinit {
stop()
}

init() { }

func stop() {
// end stream
continuation.finish()
// cleanup
lock.lock()
defer { lock.unlock() }
guard _isExecuting else { return }
_isExecuting = false
// cleanup / stop scanning / cancel child task
onTermination()
}

func makeAsyncIterator() -> AsyncIterator {
return AsyncIterator(stream.makeAsyncIterator())
}
}
}
34 changes: 27 additions & 7 deletions Sources/Bluetooth/L2CAPSocket.swift
Original file line number Diff line number Diff line change
@@ -9,17 +9,37 @@
import Foundation

/// L2CAP Socket protocol.
public protocol L2CAPSocketProtocol: AnyObject {
public protocol L2CAPSocket: AnyObject {

/// Reads from the socket.
func recieve(_ bufferSize: Int) throws -> Data?
/// Socket address
var address: BluetoothAddress { get }

/// Write to the socket.
func send(_ data: Data) throws
func send(_ data: Data) async throws

/// Reads from the socket.
func recieve(_ bufferSize: Int) async throws -> Data

/// The socket's security level.
var securityLevel: SecurityLevel { get }
/// Attempt to accept an incoming connection.
func accept() async throws -> Self

/// Attempts to change the socket's security level.
func setSecurityLevel(_ securityLevel: SecurityLevel) throws
func setSecurityLevel(_ securityLevel: SecurityLevel) async throws

/// Get security level
var securityLevel: SecurityLevel { get async throws }

/// Creates a new socket connected to the remote address specified.
static func lowEnergyClient(
address: BluetoothAddress,
destination: BluetoothAddress,
isRandom: Bool
) async throws -> Self

/// Creates a new server,
static func lowEnergyServer(
address: BluetoothAddress,
isRandom: Bool,
backlog: Int
) async throws -> Self
}
Loading

0 comments on commit 9a438b8

Please sign in to comment.