Skip to content

Commit

Permalink
Import some parts of LLBSupport from swift-llbuild2
Browse files Browse the repository at this point in the history
  • Loading branch information
aciidgh committed Jun 19, 2020
1 parent c55c5b2 commit 5fb81fc
Show file tree
Hide file tree
Showing 19 changed files with 1,242 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
55 changes: 55 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Code of Conduct
To be a truly great community, Swift.org needs to welcome developers from all walks of life,
with different backgrounds, and with a wide range of experience. A diverse and friendly
community will have more great ideas, more unique perspectives, and produce more great
code. We will work diligently to make the Swift community welcoming to everyone.

To give clarity of what is expected of our members, Swift.org has adopted the code of conduct
defined by [contributor-covenant.org](https://www.contributor-covenant.org). This document is used across many open source
communities, and we think it articulates our values well. The full text is copied below:

### Contributor Code of Conduct v1.3
As contributors and maintainers of this project, and in the interest of fostering an open and
welcoming community, we pledge to respect all people who contribute through reporting
issues, posting feature requests, updating documentation, submitting pull requests or patches,
and other activities.

We are committed to making participation in this project a harassment-free experience for
everyone, regardless of level of experience, gender, gender identity and expression, sexual
orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or
nationality.

Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery
- Personal attacks
- Trolling or insulting/derogatory comments
- Public or private harassment
- Publishing other’s private information, such as physical or electronic addresses, without explicit permission
- Other unethical or unprofessional conduct

Project maintainers have the right and responsibility to remove, edit, or reject comments,
commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of
Conduct, or to ban temporarily or permanently any contributor for other behaviors that they
deem inappropriate, threatening, offensive, or harmful.

By adopting this Code of Conduct, project maintainers commit themselves to fairly and
consistently applying these principles to every aspect of managing this project. Project
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
from the project team.

This code of conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting a project maintainer at [conduct@swift.org](mailto:conduct@swift.org). All complaints will be reviewed and
investigated and will result in a response that is deemed necessary and appropriate to the
circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter
of an incident.

*This policy is adapted from the Contributor Code of Conduct [version 1.3.0](http://contributor-covenant.org/version/1/3/0/).*

### Reporting
A working group of community members is committed to promptly addressing any [reported
issues](mailto:conduct@swift.org). Working group members are volunteers appointed by the project lead, with a
preference for individuals with varied backgrounds and perspectives. Membership is expected
to change regularly, and may grow or shrink.
10 changes: 10 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
By submitting a pull request, you represent that you have the right to license
your contribution to Apple and the community, and agree by submitting the patch
that your contributions are licensed under the [Swift
license](https://swift.org/LICENSE.txt).

---

Before submitting the pull request, please make sure you have tested your
changes and that they follow the Swift project [guidelines for contributing
code](https://swift.org/contributing/#contributing-code).
16 changes: 16 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"object": {
"pins": [
{
"package": "swift-nio",
"repositoryURL": "https://github.com/apple/swift-nio.git",
"state": {
"branch": null,
"revision": "120acb15c39aa3217e9888e515de160378fbcc1e",
"version": "2.18.0"
}
}
]
},
"version": 1
}
22 changes: 22 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// swift-tools-version:5.1
import PackageDescription

let package = Package(
name: "swift-tools-support-async",
products: [
.library(
name: "TSFUtility",
targets: ["TSFUtility"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git", from: "2.8.0"),
],
targets: [
.target(
name: "TSFUtility",
dependencies: [
"NIO",
]
),
]
)
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
# swift-tools-support-async
# swift-tools-support-async

Common infrastructural helpers on top of NIO for [llbuild2](https://github.com/apple/swift-llbuild2) and [swiftpm-on-llbuild2](https://github.com/apple/swiftpm-on-llbuild2) projects. This is **NOT** a general purpose package and is unlikely to ever become stable.

## License

Copyright (c) 2020 Apple Inc. and the Swift project authors.
Licensed under Apache License v2.0 with Runtime Library Exception.

See http://swift.org/LICENSE.txt for license information.

See http://swift.org/CONTRIBUTORS.txt for Swift project authors.
22 changes: 22 additions & 0 deletions Sources/TSFUtility/Array+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors


extension Array {
/// Make several slices out of a given array.
/// - Returns:
/// An array of slices of `maxStride` elements each.
@inlinable
public func llbSliceBy(maxStride: Int) -> [ArraySlice<Element>] {
let elementsCount = self.count
let groupsCount = (elementsCount + maxStride - 1) / maxStride
return (0..<groupsCount).map({ n in
self[n*maxStride..<Swift.min(elementsCount, (n+1)*maxStride)]
})
}
}
95 changes: 95 additions & 0 deletions Sources/TSFUtility/Futures/BatchingFutureOperationQueue.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

import Foundation


/// Run the given computations on a given array in batches, exercising
/// a specified amount of parallelism.
///
/// - Discussion:
/// For some blocking operations (such as file system accesses) executing
/// them on the NIO loops is very expensive since it blocks the event
/// processing machinery. Here we use extra threads for such operations.
public struct LLBBatchingFutureOperationQueue {

/// Threads capable of running futures.
public let group: LLBFuturesDispatchGroup

/// Queue of outstanding operations.
@usableFromInline
let operationQueue: OperationQueue

/// Maximum number of operations executed concurrently.
public let maxOpCount: Int

/// Return the number of operations currently queued.
@inlinable
public var opCount: Int {
return operationQueue.operationCount
}

/// Whether the queue is suspended.
@inlinable
public var isSuspended: Bool {
return operationQueue.isSuspended
}

///
/// - Parameters:
/// - name: Unique string label, for logging.
/// - group: Threads capable of running futures.
/// - maxConcurrentOperationCount:
/// Operations to execute in parallel.
@inlinable
public init(name: String, group: LLBFuturesDispatchGroup, maxConcurrentOperationCount maxOpCount: Int, qualityOfService: QualityOfService = .default) {
self.group = group
self.maxOpCount = maxOpCount
self.operationQueue = OperationQueue(llbWithName: name, maxConcurrentOperationCount: maxOpCount)
self.operationQueue.qualityOfService = qualityOfService
}

@inlinable
public func execute<T>(_ body: @escaping () throws -> T) -> LLBFuture<T> {
let promise = group.next().makePromise(of: T.self)
operationQueue.addOperation {
promise.fulfill(body)
}
return promise.futureResult
}

@inlinable
public func execute<T>(_ body: @escaping () -> LLBFuture<T>) -> LLBFuture<T> {
let promise = group.next().makePromise(of: T.self)
operationQueue.addOperation {
let f = body()
f.cascade(to: promise)

// Wait for completion, to ensure we maintain at most N concurrent operations.
_ = try? f.wait()
}
return promise.futureResult
}

/// Order-preserving parallel execution. Wait for everything to complete.
@inlinable
public func execute<A,T>(_ args: [A], minStride: Int = 1, _ body: @escaping (ArraySlice<A>) throws -> [T]) -> LLBFuture<[T]> {
let futures: [LLBFuture<[T]>] = executeNoWait(args, minStride: minStride, body)
let loop = futures.first?.eventLoop ?? group.next()
return LLBFuture<[T]>.whenAllSucceed(futures, on: loop).map{$0.flatMap{$0}}
}

/// Order-preserving parallel execution.
/// Do not wait for all executions to complete, returning individual futures.
@inlinable
public func executeNoWait<A,T>(_ args: [A], minStride: Int = 1, maxStride: Int = Int.max, _ body: @escaping (ArraySlice<A>) throws -> [T]) -> [LLBFuture<[T]>] {
let batches: [ArraySlice<A>] = args.llbSliceBy(maxStride: max(minStride, min(maxStride, args.count / maxOpCount)))
return batches.map{arg in execute{try body(arg)}}
}

}
56 changes: 56 additions & 0 deletions Sources/TSFUtility/Futures/CancellableFuture.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors


/// A construct which expresses operations which can be asynchronously
/// cancelled. The cancellation is not guaranteed and no ordering guarantees
/// are provided with respect to the order of future's callbacks and the
/// cancel operation returning.
public struct LLBCancellableFuture<T>: LLBCancelProtocol {
/// The underlying future.
public let future: LLBFuture<T>

/// The way to asynchronously cancel the operation backing up the future.
public let canceller: LLBCanceller

/// Initialize the future with a given canceller.
public init(_ future: LLBFuture<T>, canceller specificCanceller: LLBCanceller? = nil) {
self.future = future
let canceller = specificCanceller ?? LLBCanceller()
self.canceller = canceller
self.future.whenComplete { _ in
// Do not invoke the cancel handler if the future
// has already terminated. This is a bit opportunistic
// and can miss some cancellation invocations, but
// we expect the cancellation handlers to be no-op
// when cancelling something that's not there.
canceller.abandon()
}
}

/// Initialize with a given handler which can be
/// subsequently invoked through self.canceller.cancel()
public init(_ future: LLBFuture<T>, handler: LLBCancelProtocol) {
self = LLBCancellableFuture(future, canceller: LLBCanceller(handler))
}

/// Conformance to the `CancelProtocol`.
public func cancel(reason: String?) {
canceller.cancel(reason: reason)
}
}


/// Some surface compatibility with EventLoopFuture to minimize
/// the amount of code change in tests and other places.
extension LLBCancellableFuture {
@inlinable
public func wait() throws -> T {
try future.wait()
}
}

0 comments on commit 5fb81fc

Please sign in to comment.