├── .dockerignore ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .jazzy.yml ├── .swiftformat ├── .swiftlint.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── Package.swift ├── README.md ├── Scripts ├── ensure-pristine.sh ├── gyb.py └── make-docs.sh ├── Sources ├── Futures │ ├── Cancellable.swift │ ├── Channel.swift │ ├── Channel │ │ ├── ChannelImpl.swift │ │ ├── ChannelMPSCBufferBounded.swift │ │ ├── ChannelMPSCBufferUnbounded.swift │ │ ├── ChannelMPSCPark.swift │ │ ├── ChannelSPSCBufferBounded.swift │ │ ├── ChannelSPSCBufferUnbounded.swift │ │ ├── ChannelSPSCPark.swift │ │ ├── ChannelSlotBounded.swift │ │ └── ChannelSlotUnbounded.swift │ ├── Context.swift │ ├── Either.swift │ ├── Executor.swift │ ├── Executor │ │ ├── QueueExecutor.swift │ │ ├── RunLoopExecutor.swift │ │ └── ThreadExecutor.swift │ ├── Extensions.swift │ ├── Future.swift │ ├── Future │ │ ├── AbortFuture.swift │ │ ├── AssertNoErrorFuture.swift │ │ ├── BreakpointFuture.swift │ │ ├── CatchErrorFuture.swift │ │ ├── FlatMapFuture.swift │ │ ├── FlattenFuture.swift │ │ ├── FlattenResultFuture.swift │ │ ├── FutureStream.swift │ │ ├── HandleEventsFuture.swift │ │ ├── IgnoreOutputFuture.swift │ │ ├── JoinAllFuture.swift │ │ ├── JoinFuture.swift │ │ ├── LazyFuture.swift │ │ ├── MapFuture.swift │ │ ├── MapKeyPathFuture.swift │ │ ├── MapResultFuture.swift │ │ ├── MatchEitherFuture.swift │ │ ├── MatchOptionalFuture.swift │ │ ├── MatchResultFuture.swift │ │ ├── NeverFuture.swift │ │ ├── PeekFuture.swift │ │ ├── PollOnFuture.swift │ │ ├── PrintFuture.swift │ │ ├── ReadyFuture.swift │ │ ├── ReferenceFuture.swift │ │ ├── ReplaceErrorFuture.swift │ │ ├── ReplaceOutputFuture.swift │ │ ├── SelectAnyFuture.swift │ │ ├── SelectFuture.swift │ │ ├── SetFailureTypeFuture.swift │ │ ├── ThenFuture.swift │ │ ├── TryLazyFuture.swift │ │ └── TryMapFuture.swift │ ├── Internal │ │ ├── AdaptiveQueue.swift │ │ ├── CircularBuffer.swift │ │ ├── Private.swift │ │ ├── TaskRunner.swift │ │ └── TaskScheduler.swift │ ├── Poll.swift │ ├── Promise.swift │ ├── Result.swift │ ├── Sink.swift │ ├── Sink │ │ ├── AssertNoErrorSink.swift │ │ ├── BlockingSink.swift │ │ ├── BufferSink.swift │ │ ├── CollectSink.swift │ │ ├── FlatMapInputSink.swift │ │ ├── MapErrorSink.swift │ │ ├── MapInputSink.swift │ │ ├── SetFailureTypeSink.swift │ │ ├── SinkCloseFuture.swift │ │ ├── SinkFlushFuture.swift │ │ ├── SinkSendAllFuture.swift │ │ └── SinkSendFuture.swift │ ├── Stream.swift │ ├── Stream │ │ ├── AbortStream.swift │ │ ├── AssertNoErrorStream.swift │ │ ├── BreakpointStream.swift │ │ ├── BufferStream.swift │ │ ├── CatchErrorStream.swift │ │ ├── CompactMapStream.swift │ │ ├── CompleteOnErrorStream.swift │ │ ├── ConcatenateStream.swift │ │ ├── DropStream.swift │ │ ├── DropUntilOutputStream.swift │ │ ├── DropWhileStream.swift │ │ ├── EmptyStream.swift │ │ ├── EnumerateStream.swift │ │ ├── FilterStream.swift │ │ ├── FlatMapStream.swift │ │ ├── FlattenResultStream.swift │ │ ├── FlattenStream.swift │ │ ├── ForEachStream.swift │ │ ├── GenerateStream.swift │ │ ├── HandleEventsStream.swift │ │ ├── JoinStream.swift │ │ ├── JustStream.swift │ │ ├── LatestStream.swift │ │ ├── LazyStream.swift │ │ ├── MapKeyPathStream.swift │ │ ├── MapResultStream.swift │ │ ├── MapStream.swift │ │ ├── MatchEitherStream.swift │ │ ├── MatchOptionalStream.swift │ │ ├── MatchResultStream.swift │ │ ├── MergeAllStream.swift │ │ ├── MergeStream.swift │ │ ├── MulticastStream.swift │ │ ├── NeverStream.swift │ │ ├── OptionalStream.swift │ │ ├── OutputStream.swift │ │ ├── PollOnStream.swift │ │ ├── PrefixStream.swift │ │ ├── PrefixUntilOutputStream.swift │ │ ├── PrefixWhileStream.swift │ │ ├── PrintStream.swift │ │ ├── ReferenceStream.swift │ │ ├── RemoveDuplicatesStream.swift │ │ ├── RepeatStream.swift │ │ ├── ReplaceEmptyStream.swift │ │ ├── ReplaceErrorStream.swift │ │ ├── ScanStream.swift │ │ ├── SequenceStream.swift │ │ ├── SetFailureTypeStream.swift │ │ ├── ShareStream.swift │ │ ├── StreamAllSatisfyFuture.swift │ │ ├── StreamContainsFuture.swift │ │ ├── StreamContainsWhereFuture.swift │ │ ├── StreamFirstFuture.swift │ │ ├── StreamFirstWhereFuture.swift │ │ ├── StreamForwardFuture.swift │ │ ├── StreamFuture.swift │ │ ├── StreamIgnoreOutputFuture.swift │ │ ├── StreamLastFuture.swift │ │ ├── StreamLastWhereFuture.swift │ │ ├── StreamReduceFuture.swift │ │ ├── StreamReduceIntoFuture.swift │ │ ├── StreamReplayBuffer.swift │ │ ├── SwitchToLatestStream.swift │ │ ├── TryMapStream.swift │ │ ├── UnfoldStream.swift │ │ ├── YieldStream.swift │ │ └── ZipStream.swift │ ├── Task.swift │ └── Waker.swift ├── FuturesPrivate │ ├── Private.c │ └── include │ │ ├── CAtomic.h │ │ └── FuturesPrivate.h ├── FuturesSync │ ├── Atomic.swift │ ├── Atomic │ │ ├── AtomicBitset.swift │ │ ├── AtomicBitset.swift.gyb │ │ ├── AtomicEnum.swift │ │ ├── AtomicEnum.swift.gyb │ │ ├── AtomicPointer.swift │ │ ├── AtomicPointer.swift.gyb │ │ ├── AtomicReference.swift │ │ ├── AtomicValue.swift │ │ └── AtomicValue.swift.gyb │ ├── AtomicQueue.swift │ ├── AtomicQueue │ │ ├── AtomicBuffer.swift │ │ ├── AtomicList.swift │ │ ├── AtomicMPMCQueue.swift │ │ ├── AtomicMPSCQueue.swift │ │ ├── AtomicSPMCQueue.swift │ │ ├── AtomicSPSCQueue.swift │ │ ├── AtomicUnboundedMPSCQueue.swift │ │ └── AtomicUnboundedSPSCQueue.swift │ ├── Backoff.swift │ ├── Locking.swift │ ├── Locking │ │ ├── PosixConditionLock.swift │ │ ├── PosixLock.swift │ │ ├── SpinLock.swift │ │ └── UnfairLock.swift │ ├── Mutex.swift │ ├── Private.swift │ └── Thread.swift └── FuturesTestSupport │ ├── Assert.swift │ ├── Future.swift │ ├── Module.swift │ ├── Stream.swift │ ├── TestCase.swift │ └── TestError.swift └── Tests ├── FuturesSyncTests ├── AtomicQueueTests.swift ├── AtomicReferenceTests.swift ├── AtomicValueTests.swift ├── AtomicValueTests.swift.gyb └── LockingTests.swift └── FuturesTests ├── CancellableTests.swift ├── ChannelTests.swift ├── ExecutorTests.swift ├── FutureTests.swift ├── ReadmeTests.swift └── StreamTests.swift /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .DS_Store 3 | /.build 4 | /.digests 5 | /.swiftpm 6 | /build 7 | /Carthage 8 | /DerivedData 9 | /docs 10 | /Packages 11 | /xcuserdata 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - next 9 | 10 | jobs: 11 | pretest: 12 | name: Check commit 13 | runs-on: macos-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - run: brew install swiftformat 17 | - run: brew install swiftlint 18 | - run: make pretest 19 | 20 | xcode: 21 | name: Xcode ${{ matrix.xcode }} / Swift ${{ matrix.swift }} 22 | needs: pretest 23 | runs-on: macos-latest 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | xcode: ['11.3', '11.4'] 28 | include: 29 | - xcode: '11.3' 30 | swift: '5.1' 31 | - xcode: '11.4' 32 | swift: '5.2' 33 | steps: 34 | - uses: actions/checkout@v2 35 | - run: sudo xcode-select -s '/Applications/Xcode_${{ matrix.xcode }}.app' 36 | - run: swift --version 37 | - run: make test 38 | 39 | ubuntu: 40 | name: Ubuntu ${{ matrix.os_version }} / Swift ${{ matrix.swift }} 41 | needs: pretest 42 | runs-on: ubuntu-latest 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | os: [xenial, bionic] 47 | swift: ['5.1', '5.2'] 48 | include: 49 | - os: xenial 50 | os_version: '16.04' 51 | - os: bionic 52 | os_version: '18.04' 53 | - swift: '5.1' 54 | image: 'swift:' 55 | - swift: '5.2' 56 | image: 'swiftlang/swift:nightly-' 57 | container: 58 | image: ${{ matrix.image }}${{ matrix.swift }}-${{ matrix.os }} 59 | steps: 60 | - uses: actions/checkout@v2 61 | - run: swift --version 62 | - run: make test 63 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: published 6 | 7 | jobs: 8 | docs: 9 | name: Documentation 10 | runs-on: macos-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | token: ${{ secrets.PUSH_TOKEN }} 15 | - run: sudo gem install --no-document jazzy 16 | - run: make docs 17 | env: 18 | CI: 'true' 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*.xcodeproj 3 | /.build 4 | /.digests 5 | /.swiftpm 6 | /build 7 | /Carthage 8 | /DerivedData 9 | /docs 10 | /Packages 11 | /xcuserdata 12 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | --swiftversion 5.0 2 | --allman false 3 | --decimalgrouping 3,4 4 | --exclude Package.swift 5 | --ifdef no-indent 6 | --indent 4 7 | --patternlet inline 8 | --nospaceoperators 9 | --self remove 10 | --selfrequired 11 | --wraparguments before-first 12 | --wrapcollections before-first 13 | --header \n {file}\n Futures\n\n Copyright © 2019 Akis Kesoglou. Licensed under the MIT license.\n 14 | --rules andOperator,anyObjectProtocol,blankLinesAroundMark,blankLinesAtEndOfScope,blankLinesAtStartOfScope,blankLinesBetweenScopes,braces,consecutiveBlankLines,consecutiveSpaces,duplicateImports,elseOnSameLine,emptyBraces,fileHeader,hoistPatternLet,indent,leadingDelimiters,linebreakAtEndOfFile,linebreaks,numberFormatting,redundantBreak,redundantExtensionACL,redundantFileprivate,redundantGet,redundantInit,redundantLet,redundantLetError,redundantNilInit,redundantObjc,redundantParens,redundantPattern,redundantRawValues,redundantSelf,redundantVoidReturnType,semicolons,sortedImports,spaceAroundBraces,spaceAroundBrackets,spaceAroundComments,spaceAroundGenerics,spaceAroundParens,spaceInsideBraces,spaceInsideBrackets,spaceInsideComments,spaceInsideGenerics,spaceInsideParens,strongOutlets,strongifiedSelf,todos,trailingClosures,trailingCommas,trailingSpace,typeSugar,unusedArguments,void,wrapArguments,yodaConditions 15 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | excluded: 2 | - .build/ 3 | - Tests/LinuxMain.swift 4 | 5 | disabled_rules: 6 | - empty_count 7 | - file_length 8 | - function_body_length 9 | - identifier_name 10 | - line_length 11 | - todo 12 | - type_body_length 13 | 14 | opt_in_rules: 15 | - anyobject_protocol 16 | - closure_end_indentation 17 | - closure_spacing 18 | - collection_alignment 19 | - contains_over_first_not_nil 20 | - convenience_type 21 | - empty_string 22 | - empty_xctest_method 23 | - explicit_init 24 | - fallthrough 25 | - fatal_error_message 26 | - first_where 27 | - force_unwrapping 28 | - identical_operands 29 | - implicitly_unwrapped_optional 30 | - joined_default_parameter 31 | - let_var_whitespace 32 | - last_where 33 | - literal_expression_end_indentation 34 | - modifier_order 35 | - multiline_function_chains 36 | - multiline_literal_brackets 37 | - multiline_parameters 38 | - multiline_parameters_brackets 39 | - nimble_operator 40 | - number_separator 41 | - object_literal 42 | - operator_usage_whitespace 43 | - overridden_super_call 44 | - override_in_extension 45 | - private_action 46 | - private_outlet 47 | - prohibited_interface_builder 48 | - prohibited_super_call 49 | - quick_discouraged_call 50 | - quick_discouraged_focused_test 51 | - quick_discouraged_pending_test 52 | - redundant_nil_coalescing 53 | - redundant_type_annotation 54 | - sorted_first_last 55 | - sorted_imports 56 | - static_operator 57 | - switch_case_on_newline 58 | - unavailable_function 59 | - unneeded_parentheses_in_closure_argument 60 | - untyped_error_in_catch 61 | - unused_declaration 62 | - unused_import 63 | - vertical_whitespace_closing_braces 64 | - vertical_whitespace_opening_braces 65 | - xct_specific_matcher 66 | - yoda_condition # sic 67 | 68 | cyclomatic_complexity: 69 | ignores_case_statements: true 70 | 71 | large_tuple: 72 | warning: 4 73 | error: 5 74 | 75 | modifier_order: 76 | preferred_modifier_order: [ 77 | acl, setterACL, final, override, dynamic, mutators, lazy, required, convenience, typeMethods, owned 78 | ] 79 | 80 | nesting: 81 | type_level: 3 82 | 83 | trailing_comma: 84 | mandatory_comma: true 85 | 86 | type_name: 87 | min_length: 1 88 | validates_start_with_lowercase: false 89 | allowed_symbols: 90 | - '_' 91 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG TOOLCHAIN 2 | FROM $TOOLCHAIN 3 | VOLUME ["/src"] 4 | WORKDIR /src 5 | RUN /usr/bin/swift --version 6 | ENTRYPOINT ["/usr/bin/swift"] 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019, Akis Kesoglou 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Futures", 7 | platforms: [ 8 | .macOS(.v10_12), 9 | .iOS(.v10), 10 | .tvOS(.v10), 11 | .watchOS(.v3), 12 | ], 13 | products: [ 14 | // Primitives for asynchronous programming; futures, streams, channels. 15 | .library(name: "Futures", targets: ["Futures"]), 16 | 17 | // Primitives for thread synchronization; atomics, queues, locks. 18 | .library(name: "FuturesSync", targets: ["FuturesSync"]), 19 | ], 20 | targets: [ 21 | .target( 22 | name: "Futures", 23 | dependencies: [ 24 | "FuturesSync", 25 | ] 26 | ), 27 | .target( 28 | name: "FuturesSync", 29 | dependencies: [ 30 | "FuturesPrivate", 31 | ] 32 | ), 33 | .target( 34 | name: "FuturesPrivate", 35 | dependencies: [] 36 | ), 37 | .target( 38 | name: "FuturesTestSupport", 39 | dependencies: [ 40 | "Futures", 41 | "FuturesSync", 42 | ] 43 | ), 44 | 45 | .testTarget( 46 | name: "FuturesTests", 47 | dependencies: [ 48 | "Futures", 49 | "FuturesTestSupport", 50 | ] 51 | ), 52 | .testTarget( 53 | name: "FuturesSyncTests", 54 | dependencies: [ 55 | "FuturesSync", 56 | "FuturesTestSupport", 57 | ] 58 | ), 59 | ] 60 | ) 61 | -------------------------------------------------------------------------------- /Scripts/ensure-pristine.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | if test -n "$(git status -z --porcelain)"; then 6 | echo 'ERROR: Found uncommitted changes and/or untracked files.' 7 | echo ' Run `make precommit` in your working directory, commit' 8 | echo ' the changes and push again.' 9 | echo '' 10 | git status -s 11 | exit 1 12 | fi 13 | -------------------------------------------------------------------------------- /Scripts/make-docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Much of this is an adaptation of the corresponding script in swift-nio: 4 | # https://github.com/apple/swift-nio 5 | # ============================================================================ 6 | 7 | set -e 8 | 9 | MODULES=(Futures FuturesSync) 10 | 11 | REPO_SLUG=dfunckt/swift-futures 12 | REPO_URL=https://github.com/${REPO_SLUG} 13 | BASE_URL=https://dfunckt.github.io/swift-futures 14 | OUTPUT_DIR="docs/" 15 | 16 | VERSION=$(git describe --exact-match --abbrev=0 --tags || git rev-parse --abbrev-ref HEAD) 17 | echo "Building docs for version '${VERSION}'" 18 | 19 | JAZZY_ARGS=( 20 | --config .jazzy.yml 21 | --github_url "${REPO_URL}" 22 | --github-file-prefix "${REPO_URL}/tree/${VERSION}" 23 | --xcodebuild-arguments USE_SWIFT_RESPONSE_FILE=NO 24 | ) 25 | 26 | make_module_readme() { 27 | local readme_path="$1" 28 | cat > "${readme_path}" <<"EOF" 29 | # Futures 30 | 31 | Futures comprises several modules: 32 | 33 | EOF 34 | for m in "${MODULES[@]}"; do 35 | echo "- [${m}](../${m}/index.html)" >>"${readme_path}" 36 | done 37 | } 38 | 39 | make_module_docs() { 40 | local module="$1" 41 | local module_readme="$2" 42 | args=( 43 | "${JAZZY_ARGS[@]}" 44 | --module "${module}" 45 | --module-version "${VERSION}" 46 | --title "${module} Reference (${VERSION})" 47 | --readme "${module_readme}" 48 | --root-url "${BASE_URL}/${OUTPUT_DIR}${VERSION}/${module}" 49 | --output "${OUTPUT_DIR}${VERSION}/${module}/" 50 | ) 51 | jazzy "${args[@]}" 52 | } 53 | 54 | publish() { 55 | local branch_name=$(git rev-parse --abbrev-ref HEAD) 56 | local git_author=$(git --no-pager show -s --format='%an <%ae>' HEAD) 57 | 58 | git fetch --depth=1 origin +gh-pages:gh-pages 59 | git checkout gh-pages 60 | 61 | rm -rf "${OUTPUT_DIR}latest" 62 | cp -r "${OUTPUT_DIR}${VERSION}" "${OUTPUT_DIR}latest" 63 | 64 | git add --all "${OUTPUT_DIR}" 65 | 66 | local latest_url="${OUTPUT_DIR}${VERSION}/Futures/index.html" 67 | echo '' >index.html 68 | git add index.html 69 | 70 | touch .nojekyll 71 | git add .nojekyll 72 | 73 | local changes=$(git diff-index --name-only HEAD) 74 | if test -n "$changes"; then 75 | git commit --author="${git_author}" -m "Publish API reference for ${VERSION}" 76 | git push origin gh-pages 77 | else 78 | echo "no changes detected" 79 | fi 80 | 81 | git checkout -f "${branch_name}" 82 | } 83 | 84 | mkdir -p "${OUTPUT_DIR}${VERSION}" 85 | 86 | for module in "${MODULES[@]}"; do 87 | readme="${OUTPUT_DIR}${VERSION}/${module}.md" 88 | make_module_readme "$readme" 89 | make_module_docs "$module" "$readme" 90 | done 91 | 92 | if test $CI = true; then 93 | publish 94 | fi 95 | -------------------------------------------------------------------------------- /Sources/Futures/Channel/ChannelMPSCBufferBounded.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelMPSCBufferBounded.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesSync 9 | 10 | extension Channel._Private { 11 | public struct MPSCBufferBounded: _ChannelBufferImplProtocol { 12 | @usableFromInline let _buffer: AtomicMPSCQueue 13 | 14 | @inlinable 15 | init(capacity: Int) { 16 | _buffer = .init(capacity: capacity) 17 | } 18 | 19 | @inlinable 20 | public static var isPassthrough: Bool { 21 | return false 22 | } 23 | 24 | @inlinable 25 | public static var isBounded: Bool { 26 | return true 27 | } 28 | 29 | @inlinable 30 | public var capacity: Int { 31 | return _buffer.capacity 32 | } 33 | 34 | @inlinable 35 | public func push(_ item: Item) { 36 | let result = _buffer.tryPush(item) 37 | assert(result, "expected push to succeed, but buffer is at capacity") 38 | } 39 | 40 | @inlinable 41 | public func pop() -> Item? { 42 | return _buffer.pop() 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Futures/Channel/ChannelMPSCBufferUnbounded.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelMPSCBufferUnbounded.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesSync 9 | 10 | extension Channel._Private { 11 | public struct MPSCBufferUnbounded: _ChannelBufferImplProtocol { 12 | @usableFromInline let _buffer = AtomicUnboundedMPSCQueue() 13 | 14 | @inlinable 15 | init() {} 16 | 17 | @inlinable 18 | public static var isPassthrough: Bool { 19 | return false 20 | } 21 | 22 | @inlinable 23 | public static var isBounded: Bool { 24 | return false 25 | } 26 | 27 | @inlinable 28 | public var capacity: Int { 29 | return Int.max 30 | } 31 | 32 | @inlinable 33 | public func push(_ item: Item) { 34 | _buffer.push(item) 35 | } 36 | 37 | @inlinable 38 | public func pop() -> Item? { 39 | return _buffer.pop() 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Futures/Channel/ChannelMPSCPark.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelMPSCPark.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesSync 9 | 10 | extension Channel._Private { 11 | public struct MPSCPark: _ChannelParkImplProtocol { 12 | @usableFromInline let _wakers = AtomicWakerQueue() 13 | @usableFromInline let _wakersFlush = AtomicWakerQueue() 14 | 15 | @inlinable 16 | init() {} 17 | 18 | @inlinable 19 | public func park(_ waker: WakerProtocol) -> Cancellable { 20 | _wakers.push(waker) 21 | } 22 | 23 | @inlinable 24 | public func notifyOne() { 25 | _wakers.signal() 26 | } 27 | 28 | @inlinable 29 | public func notifyAll() { 30 | _wakers.broadcast() 31 | } 32 | 33 | @inlinable 34 | public func parkFlush(_ waker: WakerProtocol) -> Cancellable { 35 | _wakersFlush.push(waker) 36 | } 37 | 38 | @inlinable 39 | public func notifyFlush() { 40 | _wakersFlush.broadcast() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/Futures/Channel/ChannelSPSCBufferBounded.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelSPSCBufferBounded.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesSync 9 | 10 | extension Channel._Private { 11 | public struct SPSCBufferBounded: _ChannelBufferImplProtocol { 12 | @usableFromInline let _buffer: AtomicSPSCQueue 13 | 14 | @inlinable 15 | init(capacity: Int) { 16 | _buffer = .init(capacity: capacity) 17 | } 18 | 19 | @inlinable 20 | public static var isPassthrough: Bool { 21 | return false 22 | } 23 | 24 | @inlinable 25 | public static var isBounded: Bool { 26 | return true 27 | } 28 | 29 | @inlinable 30 | public var capacity: Int { 31 | return _buffer.capacity 32 | } 33 | 34 | @inlinable 35 | public func push(_ item: Item) { 36 | let result = _buffer.tryPush(item) 37 | assert(result, "expected push to succeed, but buffer is at capacity") 38 | } 39 | 40 | @inlinable 41 | public func pop() -> Item? { 42 | return _buffer.pop() 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Futures/Channel/ChannelSPSCBufferUnbounded.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelSPSCBufferUnbounded.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesSync 9 | 10 | extension Channel._Private { 11 | public struct SPSCBufferUnbounded: _ChannelBufferImplProtocol { 12 | @usableFromInline let _buffer = AtomicUnboundedSPSCQueue() 13 | 14 | @inlinable 15 | init() {} 16 | 17 | @inlinable 18 | public static var isPassthrough: Bool { 19 | return false 20 | } 21 | 22 | @inlinable 23 | public static var isBounded: Bool { 24 | return false 25 | } 26 | 27 | @inlinable 28 | public var capacity: Int { 29 | return Int.max 30 | } 31 | 32 | @inlinable 33 | public func push(_ item: Item) { 34 | _buffer.push(item) 35 | } 36 | 37 | @inlinable 38 | public func pop() -> Item? { 39 | return _buffer.pop() 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Futures/Channel/ChannelSPSCPark.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelSPSCPark.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Channel._Private { 9 | public struct SPSCPark: _ChannelParkImplProtocol { 10 | @usableFromInline 11 | struct Waker { 12 | @usableFromInline let _waker: AtomicWaker 13 | 14 | @inlinable 15 | init(_ waker: AtomicWaker) { 16 | _waker = waker 17 | } 18 | } 19 | 20 | @usableFromInline let _waker = AtomicWaker() 21 | 22 | @inlinable 23 | init() {} 24 | 25 | @inlinable 26 | public func park(_ waker: WakerProtocol) -> Cancellable { 27 | _waker.register(waker) 28 | return Waker(_waker) 29 | } 30 | 31 | @inlinable 32 | public func notifyOne() { 33 | _waker.signal() 34 | } 35 | 36 | @inlinable 37 | public func notifyAll() { 38 | _waker.signal() 39 | } 40 | 41 | @inlinable 42 | public func parkFlush(_ waker: WakerProtocol) -> Cancellable { 43 | _waker.register(waker) 44 | return Waker(_waker) 45 | } 46 | 47 | @inlinable 48 | public func notifyFlush() { 49 | _waker.signal() 50 | } 51 | } 52 | } 53 | 54 | extension Channel._Private.SPSCPark.Waker: Cancellable { 55 | @inlinable 56 | func cancel() { 57 | _waker.clear() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Futures/Channel/ChannelSlotBounded.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelSlotBounded.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesSync 9 | 10 | extension Channel._Private { 11 | public struct SlotBounded: _ChannelBufferImplProtocol { 12 | @usableFromInline let _element = Mutex(Item?.none) 13 | 14 | @inlinable 15 | init() {} 16 | 17 | @inlinable 18 | public static var isPassthrough: Bool { 19 | return false 20 | } 21 | 22 | @inlinable 23 | public static var isBounded: Bool { 24 | return true 25 | } 26 | 27 | @inlinable 28 | public var capacity: Int { 29 | return 1 30 | } 31 | 32 | @inlinable 33 | public func push(_ item: Item) { 34 | _element.value = item 35 | } 36 | 37 | @inlinable 38 | public func pop() -> Item? { 39 | return _element.move() 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Futures/Channel/ChannelSlotUnbounded.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChannelSlotUnbounded.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesSync 9 | 10 | extension Channel._Private { 11 | public struct SlotUnbounded: _ChannelBufferImplProtocol { 12 | @usableFromInline let _element = Mutex(Item?.none) 13 | 14 | @inlinable 15 | init() {} 16 | 17 | @inlinable 18 | public static var isPassthrough: Bool { 19 | return true 20 | } 21 | 22 | @inlinable 23 | public static var isBounded: Bool { 24 | return false 25 | } 26 | 27 | @inlinable 28 | public var capacity: Int { 29 | return 1 30 | } 31 | 32 | @inlinable 33 | public func push(_ item: Item) { 34 | _element.value = item 35 | } 36 | 37 | @inlinable 38 | public func pop() -> Item? { 39 | return _element.move() 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Futures/Context.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Context.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesSync 9 | 10 | public struct Context { 11 | @usableFromInline let _runner: _TaskRunner 12 | @usableFromInline let _waker: WakerProtocol 13 | 14 | @inlinable 15 | init(runner: _TaskRunner, waker: WakerProtocol) { 16 | _runner = runner 17 | _waker = waker 18 | } 19 | 20 | @inlinable 21 | public var waker: WakerProtocol { 22 | return _waker 23 | } 24 | 25 | @inlinable 26 | public func withWaker(_ newWaker: WakerProtocol) -> Context { 27 | return .init(runner: _runner, waker: newWaker) 28 | } 29 | 30 | @inlinable 31 | public func submit(_ future: F) where F.Output == Void { 32 | _runner.schedule(future) 33 | } 34 | 35 | @inlinable 36 | public func spawn(_ future: F) -> Task { 37 | return Task.create(future: future, runner: _runner) 38 | } 39 | 40 | @inlinable 41 | public func yield() -> Poll { 42 | _waker.signal() 43 | // yielding a task is a form of spinning, 44 | // so give other threads a chance as well. 45 | Atomic.preemptionYield(0) 46 | return .pending 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Futures/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Swift.Sequence { 9 | @inlinable 10 | public func makeStream() -> Stream._Private.Sequence { 11 | return .init(sequence: self) 12 | } 13 | } 14 | 15 | extension Swift.Array: StreamConvertible {} 16 | extension Swift.ContiguousArray: StreamConvertible {} 17 | extension Swift.Set: StreamConvertible {} 18 | extension Swift.Dictionary: StreamConvertible {} 19 | -------------------------------------------------------------------------------- /Sources/Futures/Future/AbortFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AbortFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum Abort where U.FutureType.Output == Void { 10 | public typealias Signal = () -> U 11 | 12 | case pending(Base, Signal) 13 | case polling(Base, U.FutureType) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, signal: @escaping Signal) { 18 | self = .pending(base, signal) 19 | } 20 | } 21 | } 22 | 23 | extension Future._Private.Abort: FutureProtocol { 24 | public typealias Output = Base.Output? 25 | 26 | @inlinable 27 | public mutating func poll(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(let base, let signal): 31 | let future = signal().makeFuture() 32 | self = .polling(base, future) 33 | continue 34 | 35 | case .polling(var base, var future): 36 | switch future.poll(&context) { 37 | case .ready: 38 | self = .done 39 | return .ready(nil) 40 | case .pending: 41 | switch base.poll(&context) { 42 | case .ready(let output): 43 | self = .done 44 | return .ready(output) 45 | case .pending: 46 | self = .polling(base, future) 47 | return .pending 48 | } 49 | } 50 | 51 | case .done: 52 | fatalError("cannot poll after completion") 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/Futures/Future/AssertNoErrorFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssertNoErrorFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct AssertNoError: FutureProtocol where Base.Output == Result { 10 | @usableFromInline var _base: Map 11 | 12 | @inlinable 13 | public init(base: Base, prefix: String, file: StaticString, line: UInt) { 14 | let message: String 15 | if prefix.isEmpty { 16 | message = "Unexpected error at \(file):\(line)" 17 | } else { 18 | message = "\(prefix) Unexpected error at \(file):\(line)" 19 | } 20 | _base = .init(base: base) { 21 | switch $0 { 22 | case .success(let output): 23 | return output 24 | case .failure(let error): 25 | fatalError("\(message): \(error)") 26 | } 27 | } 28 | } 29 | 30 | @inlinable 31 | public mutating func poll(_ context: inout Context) -> Poll { 32 | return _base.poll(&context) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Futures/Future/BreakpointFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BreakpointFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum Breakpoint { 10 | public typealias Ready = (Base.Output) -> Bool 11 | public typealias Pending = () -> Bool 12 | 13 | case pending(Base, Ready, Pending) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, ready: @escaping Ready, pending: @escaping Pending) { 18 | self = .pending(base, ready, pending) 19 | } 20 | } 21 | } 22 | 23 | extension Future._Private.Breakpoint { 24 | @inlinable 25 | public init(base: Base) where Base.Output == Result { 26 | self.init(base: base, ready: { $0._isFailure }, pending: { false }) 27 | } 28 | } 29 | 30 | extension Future._Private.Breakpoint: FutureProtocol { 31 | public typealias Output = Base.Output 32 | 33 | @inlinable 34 | public mutating func poll(_ context: inout Context) -> Poll { 35 | switch self { 36 | case .pending(var base, let ready, let pending): 37 | switch base.poll(&context) { 38 | case .ready(let output): 39 | self = .done 40 | if ready(output) { 41 | invokeDebugger() 42 | } 43 | return .ready(output) 44 | 45 | case .pending: 46 | self = .pending(base, ready, pending) 47 | if pending() { 48 | invokeDebugger() 49 | } 50 | return .pending 51 | } 52 | 53 | case .done: 54 | fatalError("cannot poll after completion") 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Futures/Future/CatchErrorFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CatchErrorFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum CatchError where Base.Output == Result { 10 | public typealias ErrorHandler = (Failure) -> U 11 | 12 | case pending(Base, ErrorHandler) 13 | case waiting(U.FutureType) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, errorHandler: @escaping ErrorHandler) { 18 | self = .pending(base, errorHandler) 19 | } 20 | } 21 | } 22 | 23 | extension Future._Private.CatchError: FutureProtocol { 24 | public typealias Output = U.FutureType.Output 25 | 26 | @inlinable 27 | public mutating func poll(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(var base, let errorHandler): 31 | switch base.poll(&context) { 32 | case .ready(let output): 33 | switch output { 34 | case .success(let output): 35 | self = .done 36 | return .ready(output) 37 | case .failure(let error): 38 | let f = errorHandler(error) 39 | self = .waiting(f.makeFuture()) 40 | continue 41 | } 42 | case .pending: 43 | self = .pending(base, errorHandler) 44 | return .pending 45 | } 46 | 47 | case .waiting(var future): 48 | switch future.poll(&context) { 49 | case .ready(let output): 50 | self = .done 51 | return .ready(output) 52 | case .pending: 53 | self = .waiting(future) 54 | return .pending 55 | } 56 | 57 | case .done: 58 | fatalError("cannot poll after completion") 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/Futures/Future/FlatMapFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlatMapFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum FlatMap { 10 | public typealias Transform = (Base.Output) -> U 11 | 12 | case pending(Base, Transform) 13 | case waiting(U.FutureType) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, transform: @escaping Transform) { 18 | self = .pending(base, transform) 19 | } 20 | } 21 | } 22 | 23 | extension Future._Private.FlatMap: FutureProtocol { 24 | public typealias Output = U.FutureType.Output 25 | 26 | @inlinable 27 | public mutating func poll(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(var base, let transform): 31 | switch base.poll(&context) { 32 | case .ready(let output): 33 | let future = transform(output).makeFuture() 34 | self = .waiting(future) 35 | continue 36 | case .pending: 37 | self = .pending(base, transform) 38 | return .pending 39 | } 40 | 41 | case .waiting(var future): 42 | switch future.poll(&context) { 43 | case .ready(let output): 44 | self = .done 45 | return .ready(output) 46 | case .pending: 47 | self = .waiting(future) 48 | return .pending 49 | } 50 | 51 | case .done: 52 | fatalError("cannot poll after completion") 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/Futures/Future/FlattenFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlattenFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct Flatten: FutureProtocol where Base.Output: FutureConvertible { 10 | public typealias Output = Base.Output.FutureType.Output 11 | 12 | @usableFromInline var _base: FlatMap 13 | 14 | @inlinable 15 | public init(base: Base) { 16 | _base = .init(base: base) { 17 | $0.makeFuture() 18 | } 19 | } 20 | 21 | @inlinable 22 | public mutating func poll(_ context: inout Context) -> Poll { 23 | return _base.poll(&context) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Futures/Future/FlattenResultFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlattenResultFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct FlattenResult: FutureProtocol where Base.Output == Result, Failure> { 10 | public typealias Output = Result 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base) { 16 | _base = .init(base: base) { 17 | $0.flatten() 18 | } 19 | } 20 | 21 | @inlinable 22 | public mutating func poll(_ context: inout Context) -> Poll { 23 | return _base.poll(&context) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Futures/Future/FutureStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FutureStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum Stream { 10 | case pending(Base) 11 | case complete 12 | case done 13 | 14 | @inlinable 15 | public init(base: Base) { 16 | self = .pending(base) 17 | } 18 | } 19 | } 20 | 21 | extension Future._Private.Stream: StreamProtocol { 22 | public typealias Output = Base.Output 23 | 24 | @inlinable 25 | public mutating func pollNext(_ context: inout Context) -> Poll { 26 | switch self { 27 | case .pending(var base): 28 | switch base.poll(&context) { 29 | case .ready(let result): 30 | self = .complete 31 | return .ready(result) 32 | case .pending: 33 | self = .pending(base) 34 | return .pending 35 | } 36 | 37 | case .complete: 38 | self = .done 39 | return .ready(nil) 40 | 41 | case .done: 42 | fatalError("cannot poll after completion") 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Futures/Future/HandleEventsFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HandleEventsFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum HandleEvents { 10 | public typealias Ready = (Base.Output) -> Void 11 | public typealias Pending = () -> Void 12 | 13 | case pending(Base, Ready, Pending) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, ready: @escaping Ready, pending: @escaping Pending) { 18 | self = .pending(base, ready, pending) 19 | } 20 | } 21 | } 22 | 23 | extension Future._Private.HandleEvents: FutureProtocol { 24 | public typealias Output = Base.Output 25 | 26 | @inlinable 27 | public mutating func poll(_ context: inout Context) -> Poll { 28 | switch self { 29 | case .pending(var base, let ready, let pending): 30 | switch base.poll(&context) { 31 | case .ready(let output): 32 | self = .done 33 | ready(output) 34 | return .ready(output) 35 | 36 | case .pending: 37 | self = .pending(base, ready, pending) 38 | pending() 39 | return .pending 40 | } 41 | 42 | case .done: 43 | fatalError("cannot poll after completion") 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Futures/Future/IgnoreOutputFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IgnoreOutputFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum IgnoreOutput { 10 | case pending(Base) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base) { 15 | self = .pending(base) 16 | } 17 | } 18 | } 19 | 20 | extension Future._Private.IgnoreOutput: FutureProtocol { 21 | @inlinable 22 | public mutating func poll(_ context: inout Context) -> Poll { 23 | switch self { 24 | case .pending(var base): 25 | switch base.poll(&context) { 26 | case .ready: 27 | self = .done 28 | return .ready 29 | case .pending: 30 | self = .pending(base) 31 | return .pending 32 | } 33 | 34 | case .done: 35 | fatalError("cannot poll after completion") 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/Futures/Future/JoinAllFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JoinAllFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct JoinAll { 10 | @usableFromInline 11 | enum Inner { 12 | case pending(Int, Base) 13 | case done 14 | 15 | @inlinable 16 | init(index: Int, base: Base) { 17 | self = .pending(index, base) 18 | } 19 | } 20 | 21 | private var _futures = _TaskScheduler() 22 | private var _results: [Base.Output?] 23 | 24 | public init(_ bases: Base...) { 25 | self.init(bases) 26 | } 27 | 28 | public init(_ bases: C) where C.Element == Base { 29 | _futures.schedule(bases.lazy.enumerated().map(Inner.init)) 30 | _results = .init(repeating: nil, count: _futures.count) 31 | } 32 | } 33 | } 34 | 35 | extension Future._Private.JoinAll.Inner: FutureProtocol { 36 | @usableFromInline typealias Output = (Int, Base.Output) 37 | 38 | @inlinable 39 | mutating func poll(_ context: inout Context) -> Poll { 40 | switch self { 41 | case .pending(let index, var base): 42 | switch base.poll(&context) { 43 | case .ready(let output): 44 | self = .done 45 | return .ready((index, output)) 46 | case .pending: 47 | self = .pending(index, base) 48 | return .pending 49 | } 50 | 51 | case .done: 52 | fatalError("cannot poll after completion") 53 | } 54 | } 55 | } 56 | 57 | extension Future._Private.JoinAll: FutureProtocol { 58 | public typealias Output = [Base.Output] 59 | 60 | public mutating func poll(_ context: inout Context) -> Poll { 61 | while true { 62 | switch _futures.pollNext(&context) { 63 | case .ready(.some(let (index, result))): 64 | _results[index] = result 65 | continue 66 | 67 | case .ready(.none): 68 | var results = [Base.Output]() 69 | results.reserveCapacity(_results.count) 70 | results.append(contentsOf: _results.lazy.compactMap { $0 }) 71 | _results = [] 72 | return .ready(results) 73 | 74 | case .pending: 75 | return .pending 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Sources/Futures/Future/LazyFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LazyFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum Lazy { 10 | public typealias Constructor = () -> U 11 | 12 | case pending(Constructor) 13 | case waiting(U.FutureType) 14 | case done 15 | 16 | @inlinable 17 | public init(_ body: @escaping Constructor) { 18 | self = .pending(body) 19 | } 20 | } 21 | } 22 | 23 | extension Future._Private.Lazy: FutureProtocol { 24 | public typealias Output = U.FutureType.Output 25 | 26 | @inlinable 27 | public mutating func poll(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(let constructor): 31 | let f = constructor() 32 | self = .waiting(f.makeFuture()) 33 | continue 34 | 35 | case .waiting(var future): 36 | switch future.poll(&context) { 37 | case .ready(let output): 38 | self = .done 39 | return .ready(output) 40 | case .pending: 41 | self = .waiting(future) 42 | return .pending 43 | } 44 | 45 | case .done: 46 | fatalError("cannot poll after completion") 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Futures/Future/MapFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum Map { 10 | public typealias Transform = (Base.Output) -> Output 11 | 12 | case pending(Base, Transform) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, transform: @escaping Transform) { 17 | self = .pending(base, transform) 18 | } 19 | } 20 | } 21 | 22 | extension Future._Private.Map: FutureProtocol { 23 | @inlinable 24 | public mutating func poll(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base, let transform): 27 | switch base.poll(&context) { 28 | case .ready(let output): 29 | self = .done 30 | return .ready(transform(output)) 31 | 32 | case .pending: 33 | self = .pending(base, transform) 34 | return .pending 35 | } 36 | 37 | case .done: 38 | fatalError("cannot poll after completion") 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/Futures/Future/MapKeyPathFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapKeyPathFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct MapKeyPath: FutureProtocol { 10 | public typealias Selector = KeyPath 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base, keyPath: Selector) { 16 | _base = .init(base: base) { 17 | $0[keyPath: keyPath] 18 | } 19 | } 20 | 21 | @inlinable 22 | public mutating func poll(_ context: inout Context) -> Poll { 23 | return _base.poll(&context) 24 | } 25 | } 26 | } 27 | 28 | extension Future._Private { 29 | public struct MapKeyPath2: FutureProtocol { 30 | public typealias Output = (Output0, Output1) 31 | public typealias Selector = KeyPath 32 | 33 | @usableFromInline var _base: Map 34 | 35 | @inlinable 36 | public init(base: Base, keyPath0: Selector, keyPath1: Selector) { 37 | _base = .init(base: base) { 38 | return ( 39 | $0[keyPath: keyPath0], 40 | $0[keyPath: keyPath1] 41 | ) 42 | } 43 | } 44 | 45 | @inlinable 46 | public mutating func poll(_ context: inout Context) -> Poll { 47 | return _base.poll(&context) 48 | } 49 | } 50 | } 51 | 52 | extension Future._Private { 53 | public struct MapKeyPath3: FutureProtocol { 54 | public typealias Output = (Output0, Output1, Output2) 55 | public typealias Selector = KeyPath 56 | 57 | @usableFromInline var _base: Map 58 | 59 | @inlinable 60 | public init(base: Base, keyPath0: Selector, keyPath1: Selector, keyPath2: Selector) { 61 | _base = .init(base: base) { 62 | return ( 63 | $0[keyPath: keyPath0], 64 | $0[keyPath: keyPath1], 65 | $0[keyPath: keyPath2] 66 | ) 67 | } 68 | } 69 | 70 | @inlinable 71 | public mutating func poll(_ context: inout Context) -> Poll { 72 | return _base.poll(&context) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Sources/Futures/Future/MapResultFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapResultFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct MapValue: FutureProtocol where Base.Output == Result { 10 | public typealias Output = Result 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base, success: @escaping (Success) -> NewSuccess) { 16 | _base = .init(base: base) { 17 | $0.map(success) 18 | } 19 | } 20 | 21 | @inlinable 22 | public mutating func poll(_ context: inout Context) -> Poll { 23 | return _base.poll(&context) 24 | } 25 | } 26 | } 27 | 28 | extension Future._Private { 29 | public struct MapError: FutureProtocol where Base.Output == Result, NewFailure: Error { 30 | public typealias Output = Result 31 | 32 | @usableFromInline var _base: Map 33 | 34 | @inlinable 35 | public init(base: Base, failure: @escaping (Failure) -> NewFailure) { 36 | _base = .init(base: base) { 37 | $0.mapError(failure) 38 | } 39 | } 40 | 41 | @inlinable 42 | public mutating func poll(_ context: inout Context) -> Poll { 43 | return _base.poll(&context) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Futures/Future/MatchEitherFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MatchEitherFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct MatchEither: FutureProtocol where Base.Output == Either { 10 | public typealias LeftHandler = (Left) -> Output 11 | public typealias RightHandler = (Right) -> Output 12 | 13 | @usableFromInline var _base: Map 14 | 15 | @inlinable 16 | public init(base: Base, left: @escaping LeftHandler, right: @escaping RightHandler) { 17 | _base = .init(base: base) { 18 | $0.match(left: left, right: right) 19 | } 20 | } 21 | 22 | @inlinable 23 | public mutating func poll(_ context: inout Context) -> Poll { 24 | return _base.poll(&context) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Futures/Future/MatchOptionalFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MatchOptionalFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct MatchOptional: FutureProtocol where Base.Output == Wrapped? { 10 | public typealias SomeHandler = (Wrapped) -> Output 11 | public typealias NoneHandler = () -> Output 12 | 13 | @usableFromInline var _base: Map 14 | 15 | @inlinable 16 | public init(base: Base, some: @escaping SomeHandler, none: @escaping NoneHandler) { 17 | _base = .init(base: base) { 18 | $0.match(some: some, none: none) 19 | } 20 | } 21 | 22 | @inlinable 23 | public mutating func poll(_ context: inout Context) -> Poll { 24 | return _base.poll(&context) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Futures/Future/MatchResultFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MatchResultFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct MatchResult: FutureProtocol where Base.Output == Result { 10 | public typealias SuccessHandler = (Success) -> Output 11 | public typealias FailureHandler = (Failure) -> Output 12 | 13 | @usableFromInline var _base: Map 14 | 15 | @inlinable 16 | public init(base: Base, success: @escaping SuccessHandler, failure: @escaping FailureHandler) { 17 | _base = .init(base: base) { 18 | $0.match(success: success, failure: failure) 19 | } 20 | } 21 | 22 | @inlinable 23 | public mutating func poll(_ context: inout Context) -> Poll { 24 | return _base.poll(&context) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Futures/Future/NeverFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeverFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct Never: FutureProtocol { 10 | @inlinable 11 | public init() {} 12 | 13 | @inlinable 14 | public func poll(_: inout Context) -> Poll { 15 | return .pending 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Futures/Future/PeekFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PeekFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct Peek: FutureProtocol { 10 | public typealias Output = Base.Output 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base, inspect: @escaping (Base.Output) -> Void) { 16 | _base = .init(base: base) { 17 | inspect($0) 18 | return $0 19 | } 20 | } 21 | 22 | @inlinable 23 | public mutating func poll(_ context: inout Context) -> Poll { 24 | return _base.poll(&context) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Futures/Future/PollOnFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PollOnFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum PollOn { 10 | case pending(Promise, E, Base) 11 | case waiting(Promise, E) 12 | case done 13 | 14 | @inlinable 15 | public init(base: Base, executor: E) { 16 | self = .pending(.init(), executor, base) 17 | } 18 | } 19 | } 20 | 21 | extension Future._Private.PollOn: FutureProtocol { 22 | public typealias Output = Result 23 | 24 | @inlinable 25 | public mutating func poll(_ context: inout Context) -> Poll { 26 | while true { 27 | switch self { 28 | case .pending(let promise, let executor, let base): 29 | let resolver = promise.resolve(when: base) 30 | switch executor.trySubmit(resolver) { 31 | case .success: 32 | // keep the executor alive while the future is alive 33 | self = .waiting(promise, executor) 34 | continue 35 | case .failure(let error): 36 | self = .done 37 | return .ready(.failure(error)) 38 | } 39 | 40 | case .waiting(let promise, _): 41 | switch promise.poll(&context) { 42 | case .ready(let output): 43 | self = .done 44 | return .ready(.success(output)) 45 | case .pending: 46 | return .pending 47 | } 48 | 49 | case .done: 50 | fatalError("cannot poll after completion") 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/Futures/Future/PrintFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrintFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum Print { 10 | case pending(Base, String, TextOutputStream) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base, prefix: String, to stream: TextOutputStream?) { 15 | self = .pending(base, prefix, stream ?? StandardOutputStream()) 16 | } 17 | } 18 | } 19 | 20 | extension Future._Private.Print: FutureProtocol { 21 | public typealias Output = Base.Output 22 | 23 | @inlinable 24 | public mutating func poll(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base, let prefix, var stream): 27 | let result = base.poll(&context) 28 | if case .ready = result { 29 | self = .done 30 | } else { 31 | self = .pending(base, prefix, stream) 32 | } 33 | let message = _makeMessage(prefix: prefix, result: result) 34 | stream.write(message) 35 | return result 36 | 37 | case .done: 38 | fatalError("cannot poll after completion") 39 | } 40 | } 41 | 42 | @inlinable 43 | func _makeMessage(prefix: String, result: Poll) -> String { 44 | var parts = [String]() 45 | if !prefix.isEmpty { 46 | parts.append(prefix) 47 | } 48 | switch result { 49 | case .ready(let output): 50 | parts.append(".ready(\(output))") 51 | case .pending: 52 | parts.append(".pending") 53 | } 54 | return parts.joined(separator: " ") + "\n" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/Futures/Future/ReadyFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReadyFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum Ready { 10 | case pending(Output) 11 | case done 12 | 13 | @inlinable 14 | public init(output: Output) { 15 | self = .pending(output) 16 | } 17 | } 18 | } 19 | 20 | extension Future._Private.Ready where Output == Void { 21 | @inlinable 22 | public init() { 23 | self = .pending(()) 24 | } 25 | } 26 | 27 | extension Future._Private.Ready: FutureProtocol { 28 | @inlinable 29 | public mutating func poll(_: inout Context) -> Poll { 30 | switch self { 31 | case .pending(let output): 32 | self = .done 33 | return .ready(output) 34 | case .done: 35 | fatalError("cannot poll after completion") 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/Futures/Future/ReferenceFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReferenceFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public final class Reference: FutureProtocol { 10 | @usableFromInline var _base: Base 11 | 12 | @inlinable 13 | public init(base: Base) { 14 | _base = base 15 | } 16 | 17 | @inlinable 18 | public func poll(_ context: inout Context) -> Poll { 19 | return _base.poll(&context) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Futures/Future/ReplaceErrorFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReplaceErrorFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct ReplaceError: FutureProtocol where Base.Output == Result { 10 | @usableFromInline var _base: Map 11 | 12 | @inlinable 13 | public init(base: Base, output: Output) { 14 | _base = .init(base: base) { 15 | $0.match( 16 | success: { $0 }, 17 | failure: { _ in output } 18 | ) 19 | } 20 | } 21 | 22 | @inlinable 23 | public mutating func poll(_ context: inout Context) -> Poll { 24 | return _base.poll(&context) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Futures/Future/ReplaceOutputFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReplaceOutputFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct ReplaceOutput: FutureProtocol { 10 | @usableFromInline var _base: Map 11 | 12 | @inlinable 13 | public init(base: Base, output: Output) { 14 | _base = .init(base: base) { _ in 15 | output 16 | } 17 | } 18 | 19 | @inlinable 20 | public mutating func poll(_ context: inout Context) -> Poll { 21 | return _base.poll(&context) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Futures/Future/SelectAnyFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectAnyFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct SelectAny { 10 | private var _futures: _TaskScheduler 11 | 12 | public init(_ bases: Base...) { 13 | self.init(bases) 14 | } 15 | 16 | public init(_ bases: C) where C.Element == Base { 17 | _futures = .init() 18 | _futures.schedule(bases) 19 | precondition(!_futures.isEmpty) 20 | } 21 | } 22 | } 23 | 24 | extension Future._Private.SelectAny: FutureProtocol { 25 | public typealias Output = Base.Output 26 | 27 | public mutating func poll(_ context: inout Context) -> Poll { 28 | switch _futures.pollNext(&context) { 29 | case .ready(.some(let output)): 30 | return .ready(output) 31 | case .pending: 32 | return .pending 33 | case .ready(.none): 34 | // there's always going to be at least one 35 | // future in the scheduler until we're done. 36 | fatalError("unreachable") 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Futures/Future/SelectFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum Select { 10 | case pending(A, B) 11 | case done 12 | 13 | @inlinable 14 | public init(_ a: A, _ b: B) { 15 | self = .pending(a, b) 16 | } 17 | } 18 | } 19 | 20 | extension Future._Private.Select: FutureProtocol { 21 | public typealias Output = Either 22 | 23 | @inlinable 24 | public mutating func poll(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var a, var b): 27 | switch a.poll(&context) { 28 | case .ready(let output): 29 | self = .done 30 | return .ready(.left(output)) 31 | case .pending: 32 | switch b.poll(&context) { 33 | case .ready(let output): 34 | self = .done 35 | return .ready(.right(output)) 36 | case .pending: 37 | self = .pending(a, b) 38 | return .pending 39 | } 40 | } 41 | case .done: 42 | fatalError("cannot poll after completion") 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/Futures/Future/SetFailureTypeFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SetFailureTypeFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct SetFailureType: FutureProtocol { 10 | public typealias Output = Result 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public mutating func poll(_ context: inout Context) -> Poll { 16 | return _base.poll(&context) 17 | } 18 | } 19 | } 20 | 21 | extension Future._Private.SetFailureType where Base.Output == Success { 22 | @inlinable 23 | public init(base: Base) { 24 | _base = .init(base: base) { 25 | .success($0) 26 | } 27 | } 28 | } 29 | 30 | extension Future._Private.SetFailureType where Base.Output == Result { 31 | @inlinable 32 | public init(base: Base) { 33 | _base = .init(base: base) { 34 | // swiftlint:disable:next force_cast 35 | $0.mapError { $0 as! Failure } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/Futures/Future/TryLazyFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TryLazyFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public enum TryLazy { 10 | public typealias Constructor = () throws -> U 11 | 12 | case pending(Constructor) 13 | case waiting(U.FutureType) 14 | case done 15 | 16 | @inlinable 17 | public init(_ body: @escaping Constructor) { 18 | self = .pending(body) 19 | } 20 | } 21 | } 22 | 23 | extension Future._Private.TryLazy: FutureProtocol { 24 | public typealias Output = Result 25 | 26 | @inlinable 27 | public mutating func poll(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(let constructor): 31 | do { 32 | let f = try constructor() 33 | self = .waiting(f.makeFuture()) 34 | continue 35 | } catch { 36 | self = .done 37 | return .ready(.failure(error)) 38 | } 39 | 40 | case .waiting(var future): 41 | switch future.poll(&context) { 42 | case .ready(let output): 43 | self = .done 44 | return .ready(.success(output)) 45 | case .pending: 46 | self = .waiting(future) 47 | return .pending 48 | } 49 | 50 | case .done: 51 | fatalError("cannot poll after completion") 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Futures/Future/TryMapFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TryMapFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Future._Private { 9 | public struct TryMap: FutureProtocol { 10 | public typealias Output = Result 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base, catching block: @escaping (Base.Output) throws -> T) { 16 | _base = .init(base: base) { 17 | do { 18 | return try .success(block($0)) 19 | } catch { 20 | return .failure(error) 21 | } 22 | } 23 | } 24 | 25 | @inlinable 26 | public mutating func poll(_ context: inout Context) -> Poll { 27 | return _base.poll(&context) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Futures/Internal/TaskRunner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TaskRunner.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | @usableFromInline 9 | final class _TaskRunner { 10 | /// A user-displayable identifier. Can be useful for debugging. 11 | @usableFromInline let label: String 12 | 13 | private let _futures = _TaskScheduler>() 14 | 15 | // Buffers futures that are submitted either externally via an executor 16 | // or internally by a future during polling via `Context`. On every tick, 17 | // the buffer is flushed into the scheduler in one go. 18 | @usableFromInline var _incoming = AdaptiveQueue>() 19 | 20 | @inlinable 21 | init(label: String) { 22 | self.label = label 23 | } 24 | 25 | /// The total number of futures currently being tracked. 26 | @usableFromInline 27 | var count: Int { 28 | return _incoming.count + _futures.count 29 | } 30 | 31 | /// Schedules the given future to be executed on the next tick. 32 | @inlinable 33 | func schedule(_ future: F) where F: FutureProtocol, F.Output == Void { 34 | _incoming.push(.init(future)) 35 | } 36 | 37 | /// Schedules the given future to be executed on the next tick. 38 | @inlinable 39 | func schedule(_ future: AnyFuture) { 40 | _incoming.push(future) 41 | } 42 | 43 | /// Performs a single iteration over the list of ready-to-run futures, 44 | /// polling each one in turn. Returns when no more progress can be made. 45 | /// If the count of tracked futures drops to zero during the iteration, 46 | /// this method returns `true`. 47 | @usableFromInline 48 | @discardableResult 49 | func run(_ context: inout Context) -> Bool { 50 | if !_incoming.isEmpty { 51 | // Schedule futures that have been submitted externally 52 | // via an executor since the last tick 53 | _futures.schedule(_incoming.moveElements()) 54 | } 55 | 56 | _futures.register(context.waker) 57 | 58 | while true { 59 | let result = _futures.pollNext(&context) 60 | 61 | if !_incoming.isEmpty { 62 | // New futures have been submitted by the future; 63 | // schedule them and re-poll. 64 | _futures.schedule(_incoming.moveElements()) 65 | continue 66 | } 67 | 68 | // No new futures enqueued, see if we're done. 69 | switch result { 70 | case .ready(.some): 71 | // There are more futures that are ready to be polled. 72 | continue 73 | case .ready(nil): 74 | // All futures have completed; scheduler is empty. 75 | return true 76 | case .pending: 77 | // No ready futures. 78 | return false 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Sources/Futures/Poll.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Poll.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | /// A value that encapsulates the result of polling a future. See 9 | /// `FutureProtocol.poll(_:)`. 10 | public enum Poll { 11 | case ready(T) 12 | case pending 13 | } 14 | 15 | extension Poll: Equatable where T: Equatable {} 16 | extension Poll: Hashable where T: Hashable {} 17 | 18 | extension Poll where T == Void { 19 | @inlinable 20 | public static var ready: Poll { 21 | return .ready(()) 22 | } 23 | } 24 | 25 | extension Poll { 26 | @inlinable 27 | public var result: T? { 28 | switch self { 29 | case .ready(let result): 30 | return result 31 | case .pending: 32 | return nil 33 | } 34 | } 35 | 36 | @inlinable 37 | public var isReady: Bool { 38 | switch self { 39 | case .ready: 40 | return true 41 | case .pending: 42 | return false 43 | } 44 | } 45 | 46 | @inlinable 47 | public var isPending: Bool { 48 | switch self { 49 | case .ready: 50 | return false 51 | case .pending: 52 | return true 53 | } 54 | } 55 | } 56 | 57 | extension Poll { 58 | @inlinable 59 | public func match(ready: (T) throws -> R, pending: () throws -> R) rethrows -> R { 60 | switch self { 61 | case .ready(let value): 62 | return try ready(value) 63 | case .pending: 64 | return try pending() 65 | } 66 | } 67 | 68 | @inlinable 69 | public func map(_ fn: (T) -> U) -> Poll { 70 | switch self { 71 | case .ready(let value): 72 | return .ready(fn(value)) 73 | case .pending: 74 | return .pending 75 | } 76 | } 77 | 78 | @inlinable 79 | public func flatMap(_ fn: (T) -> Poll) -> Poll { 80 | switch self { 81 | case .ready(let value): 82 | return fn(value) 83 | case .pending: 84 | return .pending 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Sources/Futures/Sink/AssertNoErrorSink.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssertNoErrorSink.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Sink._Private { 9 | public struct AssertNoError { 10 | @usableFromInline var _base: Base 11 | @usableFromInline var _message: String 12 | 13 | @inlinable 14 | public init(base: Base, prefix: String, file: StaticString, line: UInt) { 15 | _base = base 16 | if prefix.isEmpty { 17 | _message = "Unexpected result at \(file):\(line)" 18 | } else { 19 | _message = "\(prefix) Unexpected result at \(file):\(line)" 20 | } 21 | } 22 | } 23 | } 24 | 25 | extension Sink._Private.AssertNoError: SinkProtocol { 26 | public typealias Input = Base.Input 27 | public typealias Failure = Never 28 | 29 | @inlinable 30 | public mutating func pollSend(_ context: inout Context, _ item: Input) -> PollSink { 31 | return _base.pollSend(&context, item).mapError { 32 | fatalError("\(_message): \($0)") 33 | } 34 | } 35 | 36 | @inlinable 37 | public mutating func pollFlush(_ context: inout Context) -> PollSink { 38 | return _base.pollFlush(&context).mapError { 39 | fatalError("\(_message): \($0)") 40 | } 41 | } 42 | 43 | @inlinable 44 | public mutating func pollClose(_ context: inout Context) -> PollSink { 45 | return _base.pollClose(&context).mapError { 46 | fatalError("\(_message): \($0)") 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/Futures/Sink/BlockingSink.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BlockingSink.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Sink._Private { 9 | public struct Blocking { 10 | @usableFromInline let _base: Box 11 | 12 | @inlinable 13 | public init(base: Base) { 14 | _base = .init(base) 15 | } 16 | } 17 | } 18 | 19 | extension Sink._Private.Blocking { 20 | @inlinable 21 | public mutating func send(_ item: Base.Input) -> SinkResult { 22 | var f = _base.value.send(item) 23 | return ThreadExecutor.current.run(until: &f).map { 24 | _base.value = $0 25 | } 26 | } 27 | 28 | @inlinable 29 | public mutating func flush() -> SinkResult { 30 | var f = _base.value.flush() 31 | return ThreadExecutor.current.run(until: &f).map { 32 | _base.value = $0 33 | } 34 | } 35 | 36 | @inlinable 37 | public mutating func close() -> SinkResult { 38 | var f = _base.value.close() 39 | return ThreadExecutor.current.run(until: &f).map { 40 | _base.value = $0 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/Futures/Sink/BufferSink.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BufferSink.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Sink._Private { 9 | public struct Buffer { 10 | @usableFromInline var _base: Base 11 | @usableFromInline var _buffer: CircularBuffer 12 | @usableFromInline var _item: Base.Input? 13 | 14 | @inlinable 15 | public init(base: Base, count: Int) { 16 | precondition(count > 0) 17 | _base = base 18 | _buffer = .init(capacity: count) 19 | } 20 | } 21 | } 22 | 23 | extension Sink._Private.Buffer: SinkProtocol { 24 | public typealias Input = Base.Input 25 | public typealias Failure = Base.Failure 26 | 27 | @usableFromInline 28 | @_transparent 29 | mutating func _sendItem(_ context: inout Context, item: Input) -> PollSink? { 30 | switch _base.pollSend(&context, item) { 31 | case .ready(.success): 32 | return nil 33 | case .ready(.failure(let completion)): 34 | return .ready(.failure(completion)) 35 | case .pending: 36 | _item = item 37 | return .pending 38 | } 39 | } 40 | 41 | @inlinable 42 | mutating func _sendBuffer(_ context: inout Context) -> PollSink { 43 | if let item = _item.move(), let failure = _sendItem(&context, item: item) { 44 | return failure 45 | } 46 | while let item = _buffer.pop() { 47 | if let failure = _sendItem(&context, item: item) { 48 | return failure 49 | } 50 | } 51 | return .ready(.success(())) 52 | } 53 | 54 | @inlinable 55 | public mutating func pollSend(_ context: inout Context, _ item: Input) -> PollSink { 56 | return _sendBuffer(&context).flatMap { 57 | if _buffer.tryPush(item) { 58 | return .ready(.success(())) 59 | } 60 | return .pending 61 | } 62 | } 63 | 64 | @inlinable 65 | public mutating func pollFlush(_ context: inout Context) -> PollSink { 66 | return _sendBuffer(&context).flatMap { 67 | assert(_buffer.isEmpty) 68 | assert(_item == nil) 69 | return _base.pollFlush(&context) 70 | } 71 | } 72 | 73 | @inlinable 74 | public mutating func pollClose(_ context: inout Context) -> PollSink { 75 | return _sendBuffer(&context).flatMap { 76 | assert(_buffer.isEmpty) 77 | assert(_item == nil) 78 | return _base.pollFlush(&context) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/Futures/Sink/CollectSink.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectSink.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Sink._Private { 9 | public final class Collect { 10 | @usableFromInline var _elements = [Item]() 11 | 12 | @inlinable 13 | public init() {} 14 | 15 | @inlinable 16 | public init(initialElements: S) where S.Element == Item { 17 | _elements.append(contentsOf: initialElements) 18 | } 19 | } 20 | } 21 | 22 | extension Sink._Private.Collect: SinkProtocol { 23 | public typealias Input = Item 24 | public typealias Failure = Never 25 | 26 | @inlinable 27 | public var elements: [Item] { 28 | return _elements 29 | } 30 | 31 | @inlinable 32 | public func pollSend(_: inout Context, _ item: Input) -> PollSink { 33 | _elements.append(item) 34 | return .ready(.success(())) 35 | } 36 | 37 | @inlinable 38 | public func pollFlush(_: inout Context) -> PollSink { 39 | return .ready(.success(())) 40 | } 41 | 42 | @inlinable 43 | public func pollClose(_: inout Context) -> PollSink { 44 | return .ready(.success(())) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Futures/Sink/MapErrorSink.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapErrorSink.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Sink._Private { 9 | public struct MapError { 10 | public typealias Adapt = (Base.Failure) -> Failure 11 | 12 | @usableFromInline var _base: Base 13 | @usableFromInline let _adapt: Adapt 14 | 15 | @inlinable 16 | public init(base: Base, adapt: @escaping Adapt) { 17 | _base = base 18 | _adapt = adapt 19 | } 20 | } 21 | } 22 | 23 | extension Sink._Private.MapError: SinkProtocol { 24 | public typealias Input = Base.Input 25 | 26 | @inlinable 27 | public mutating func pollSend(_ context: inout Context, _ item: Input) -> PollSink { 28 | return _base.pollSend(&context, item).mapError { 29 | .failure(.failure(_adapt($0))) 30 | } 31 | } 32 | 33 | @inlinable 34 | public mutating func pollFlush(_ context: inout Context) -> PollSink { 35 | return _base.pollFlush(&context).mapError { 36 | .failure(.failure(_adapt($0))) 37 | } 38 | } 39 | 40 | @inlinable 41 | public mutating func pollClose(_ context: inout Context) -> PollSink { 42 | return _base.pollClose(&context).mapError { 43 | .failure(.failure(_adapt($0))) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Futures/Sink/MapInputSink.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapInputSink.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Sink._Private { 9 | public struct MapInput { 10 | public typealias Adapt = (Input) -> Base.Input 11 | 12 | @usableFromInline var _base: Base 13 | @usableFromInline let _adapt: Adapt 14 | @usableFromInline var _item: Base.Input? 15 | 16 | @inlinable 17 | public init(base: Base, adapt: @escaping Adapt) { 18 | _base = base 19 | _adapt = adapt 20 | } 21 | } 22 | } 23 | 24 | extension Sink._Private.MapInput: SinkProtocol { 25 | public typealias Failure = Base.Failure 26 | 27 | @inlinable 28 | mutating func _sendItem(_ context: inout Context) -> PollSink { 29 | guard let item = _item.move() else { 30 | return .ready(.success(())) 31 | } 32 | switch _base.pollSend(&context, item) { 33 | case .ready(.success): 34 | return .ready(.success(())) 35 | case .ready(.failure(let completion)): 36 | return .ready(.failure(completion)) 37 | case .pending: 38 | _item = item 39 | return .pending 40 | } 41 | } 42 | 43 | @inlinable 44 | public mutating func pollSend(_ context: inout Context, _ item: Input) -> PollSink { 45 | return _sendItem(&context).flatMap { 46 | assert(_item == nil) 47 | _item = _adapt(item) 48 | return _sendItem(&context) 49 | } 50 | } 51 | 52 | @inlinable 53 | public mutating func pollFlush(_ context: inout Context) -> PollSink { 54 | return _sendItem(&context).flatMap { 55 | assert(_item == nil) 56 | return _base.pollFlush(&context) 57 | } 58 | } 59 | 60 | @inlinable 61 | public mutating func pollClose(_ context: inout Context) -> PollSink { 62 | return _sendItem(&context).flatMap { 63 | assert(_item == nil) 64 | return _base.pollClose(&context) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Sources/Futures/Sink/SetFailureTypeSink.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SetFailureTypeSink.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Sink._Private { 9 | public struct SetFailureType where Base.Failure == Never { 10 | @usableFromInline var _base: Base 11 | 12 | @inlinable 13 | public init(base: Base) { 14 | _base = base 15 | } 16 | } 17 | } 18 | 19 | extension Sink._Private.SetFailureType: SinkProtocol { 20 | public typealias Input = Base.Input 21 | 22 | @inlinable 23 | public mutating func pollSend(_ context: inout Context, _ item: Input) -> PollSink { 24 | return _base.pollSend(&context, item).mapError { 25 | .failure(.failure($0 as! Failure)) // swiftlint:disable:this force_cast 26 | } 27 | } 28 | 29 | @inlinable 30 | public mutating func pollFlush(_ context: inout Context) -> PollSink { 31 | return _base.pollFlush(&context).mapError { 32 | .failure(.failure($0 as! Failure)) // swiftlint:disable:this force_cast 33 | } 34 | } 35 | 36 | @inlinable 37 | public mutating func pollClose(_ context: inout Context) -> PollSink { 38 | return _base.pollClose(&context).mapError { 39 | .failure(.failure($0 as! Failure)) // swiftlint:disable:this force_cast 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Futures/Sink/SinkCloseFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SinkCloseFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Sink._Private { 9 | public enum Close { 10 | case polling(Base) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base) { 15 | self = .polling(base) 16 | } 17 | } 18 | } 19 | 20 | extension Sink._Private.Close: FutureProtocol { 21 | public typealias Output = Result> 22 | 23 | @inlinable 24 | public mutating func poll(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .polling(var base): 27 | switch base.pollClose(&context) { 28 | case .ready(.success): 29 | self = .done 30 | return .ready(.success(base)) 31 | case .ready(.failure(let completion)): 32 | self = .done 33 | return .ready(.failure(completion)) 34 | case .pending: 35 | self = .polling(base) 36 | return .pending 37 | } 38 | case .done: 39 | fatalError("cannot poll after completion") 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Futures/Sink/SinkFlushFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SinkFlushFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Sink._Private { 9 | public enum Flush { 10 | case polling(Base) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base) { 15 | self = .polling(base) 16 | } 17 | } 18 | } 19 | 20 | extension Sink._Private.Flush: FutureProtocol { 21 | public typealias Output = Result> 22 | 23 | @inlinable 24 | public mutating func poll(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .polling(var base): 27 | switch base.pollFlush(&context) { 28 | case .ready(.success): 29 | self = .done 30 | return .ready(.success(base)) 31 | case .ready(.failure(let completion)): 32 | self = .done 33 | return .ready(.failure(completion)) 34 | case .pending: 35 | self = .polling(base) 36 | return .pending 37 | } 38 | case .done: 39 | fatalError("cannot poll after completion") 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Futures/Sink/SinkSendFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SinkSendFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Sink._Private { 9 | public enum Send { 10 | case polling(Base, Base.Input) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base, item: Base.Input) { 15 | self = .polling(base, item) 16 | } 17 | } 18 | } 19 | 20 | extension Sink._Private.Send: FutureProtocol { 21 | public typealias Output = Result> 22 | 23 | @inlinable 24 | public mutating func poll(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .polling(var base, let item): 27 | switch base.pollSend(&context, item) { 28 | case .ready(.success): 29 | self = .done 30 | return .ready(.success(base)) 31 | case .ready(.failure(let completion)): 32 | self = .done 33 | return .ready(.failure(completion)) 34 | case .pending: 35 | self = .polling(base, item) 36 | return .pending 37 | } 38 | case .done: 39 | fatalError("cannot poll after completion") 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/AbortStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AbortStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Abort where U.FutureType.Output == Void { 10 | public typealias Signal = () -> U 11 | 12 | case pending(Base, Signal) 13 | case polling(Base, U.FutureType) 14 | case done 15 | } 16 | } 17 | 18 | extension Stream._Private.Abort: StreamProtocol { 19 | public typealias Output = Base.Output 20 | 21 | @inlinable 22 | public init(base: Base, signal: @escaping Signal) { 23 | self = .pending(base, signal) 24 | } 25 | 26 | @inlinable 27 | public mutating func pollNext(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(let base, let signal): 31 | let future = signal().makeFuture() 32 | self = .polling(base, future) 33 | continue 34 | 35 | case .polling(var base, var future): 36 | switch future.poll(&context) { 37 | case .ready: 38 | self = .done 39 | return .ready(nil) 40 | case .pending: 41 | switch base.pollNext(&context) { 42 | case .ready(.some(let output)): 43 | self = .polling(base, future) 44 | return .ready(output) 45 | case .ready(.none): 46 | self = .done 47 | return .ready(nil) 48 | case .pending: 49 | self = .polling(base, future) 50 | return .pending 51 | } 52 | } 53 | 54 | case .done: 55 | fatalError("cannot poll after completion") 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/AssertNoErrorStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssertNoErrorStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct AssertNoError: StreamProtocol where Base.Output == Result { 10 | @usableFromInline var _base: Map 11 | 12 | @inlinable 13 | public init(base: Base, prefix: String, file: StaticString, line: UInt) { 14 | let message: String 15 | if prefix.isEmpty { 16 | message = "Unexpected error at \(file):\(line)" 17 | } else { 18 | message = "\(prefix) Unexpected error at \(file):\(line)" 19 | } 20 | _base = .init(base: base) { 21 | switch $0 { 22 | case .success(let output): 23 | return output 24 | case .failure(let error): 25 | fatalError("\(message): \(error)") 26 | } 27 | } 28 | } 29 | 30 | @inlinable 31 | public mutating func pollNext(_ context: inout Context) -> Poll { 32 | return _base.pollNext(&context) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/BreakpointStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BreakpointStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Breakpoint { 10 | public typealias Ready = (Base.Output) -> Bool 11 | public typealias Pending = () -> Bool 12 | public typealias Complete = () -> Bool 13 | 14 | case pending(Base, Ready, Pending, Complete) 15 | case done 16 | 17 | @inlinable 18 | public init(base: Base, ready: @escaping Ready, pending: @escaping Pending, complete: @escaping Complete) { 19 | self = .pending(base, ready, pending, complete) 20 | } 21 | 22 | @inlinable 23 | public init(base: Base) where Base.Output == Result { 24 | self.init( 25 | base: base, 26 | ready: { $0._isFailure }, 27 | pending: { false }, 28 | complete: { false } 29 | ) 30 | } 31 | } 32 | } 33 | 34 | extension Stream._Private.Breakpoint: StreamProtocol { 35 | public typealias Output = Base.Output 36 | 37 | @inlinable 38 | public mutating func pollNext(_ context: inout Context) -> Poll { 39 | switch self { 40 | case .pending(var base, let ready, let pending, let complete): 41 | switch base.pollNext(&context) { 42 | case .ready(.some(let output)): 43 | self = .pending(base, ready, pending, complete) 44 | if ready(output) { 45 | invokeDebugger() 46 | } 47 | return .ready(output) 48 | 49 | case .ready(.none): 50 | self = .done 51 | if complete() { 52 | invokeDebugger() 53 | } 54 | return .ready(nil) 55 | 56 | case .pending: 57 | self = .pending(base, ready, pending, complete) 58 | if pending() { 59 | invokeDebugger() 60 | } 61 | return .pending 62 | } 63 | 64 | case .done: 65 | fatalError("cannot poll after completion") 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/BufferStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BufferStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct Buffer { 10 | @usableFromInline 11 | enum _State { 12 | case pending(Base, CircularBuffer) 13 | case complete 14 | case done 15 | } 16 | 17 | @usableFromInline var _state: _State 18 | 19 | @inlinable 20 | public init(base: Base, count: Int) { 21 | _state = .pending(base, .init(capacity: count)) 22 | } 23 | } 24 | } 25 | 26 | extension Stream._Private.Buffer: StreamProtocol { 27 | public typealias Output = [Base.Output] 28 | 29 | @inlinable 30 | public mutating func pollNext(_ context: inout Context) -> Poll { 31 | switch _state { 32 | case .pending(var base, var buffer): 33 | while true { 34 | switch base.pollNext(&context) { 35 | case .ready(.some(let output)): 36 | if buffer.tryPush(output) { 37 | continue 38 | } 39 | let elements = buffer.moveElements() 40 | let enqueued = buffer.tryPush(output) 41 | assert(enqueued) 42 | _state = .pending(base, buffer) 43 | return .ready(elements) 44 | 45 | case .ready(.none): 46 | _state = .complete 47 | return .ready(buffer.moveElements()) 48 | 49 | case .pending: 50 | _state = .pending(base, buffer) 51 | return .pending 52 | } 53 | } 54 | 55 | case .complete: 56 | _state = .done 57 | return .ready(nil) 58 | 59 | case .done: 60 | fatalError("cannot poll after completion") 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/CatchErrorStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CatchErrorStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum CatchError where Base.Output == Result { 10 | public typealias ErrorHandler = (Failure) -> U 11 | 12 | case pending(Base, ErrorHandler) 13 | case waiting(Base, ErrorHandler, U.StreamType) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, errorHandler: @escaping ErrorHandler) { 18 | self = .pending(base, errorHandler) 19 | } 20 | } 21 | } 22 | 23 | extension Stream._Private.CatchError: StreamProtocol { 24 | public typealias Output = U.StreamType.Output 25 | 26 | @inlinable 27 | public mutating func pollNext(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(var base, let errorHandler): 31 | switch base.pollNext(&context) { 32 | case .ready(.some(let output)): 33 | switch output { 34 | case .success(let output): 35 | self = .pending(base, errorHandler) 36 | return .ready(output) 37 | case .failure(let error): 38 | let f = errorHandler(error) 39 | self = .waiting(base, errorHandler, f.makeStream()) 40 | continue 41 | } 42 | case .ready(.none): 43 | self = .done 44 | return .ready(nil) 45 | case .pending: 46 | self = .pending(base, errorHandler) 47 | return .pending 48 | } 49 | 50 | case .waiting(let base, let errorHandler, var stream): 51 | switch stream.pollNext(&context) { 52 | case .ready(.some(let output)): 53 | self = .waiting(base, errorHandler, stream) 54 | return .ready(output) 55 | case .ready(.none): 56 | self = .pending(base, errorHandler) 57 | continue 58 | case .pending: 59 | self = .waiting(base, errorHandler, stream) 60 | return .pending 61 | } 62 | 63 | case .done: 64 | fatalError("cannot poll after completion") 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/CompactMapStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CompactMapStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum CompactMap { 10 | public typealias Transform = (Base.Output) -> Output? 11 | 12 | case pending(Base, Transform) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, transform: @escaping Transform) { 17 | self = .pending(base, transform) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.CompactMap: StreamProtocol { 23 | @inlinable 24 | public mutating func pollNext(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base, let transform): 27 | while true { 28 | switch base.pollNext(&context) { 29 | case .ready(.some(let output)): 30 | if let output = transform(output) { 31 | self = .pending(base, transform) 32 | return .ready(output) 33 | } 34 | continue 35 | 36 | case .ready(.none): 37 | self = .done 38 | return .ready(nil) 39 | 40 | case .pending: 41 | self = .pending(base, transform) 42 | return .pending 43 | } 44 | } 45 | 46 | case .done: 47 | fatalError("cannot poll after completion") 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/CompleteOnErrorStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CompleteOnErrorStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum CompleteOnError where Base.Output == Result { 10 | case pending(Base) 11 | case completed 12 | case done 13 | 14 | @inlinable 15 | public init(base: Base) { 16 | self = .pending(base) 17 | } 18 | } 19 | } 20 | 21 | extension Stream._Private.CompleteOnError: StreamProtocol { 22 | public typealias Output = Base.Output 23 | 24 | @inlinable 25 | public mutating func pollNext(_ context: inout Context) -> Poll { 26 | switch self { 27 | case .pending(var base): 28 | switch base.pollNext(&context) { 29 | case .ready(.some(let output)): 30 | switch output { 31 | case .success: 32 | self = .pending(base) 33 | case .failure: 34 | self = .completed 35 | } 36 | return .ready(output) 37 | 38 | case .ready(.none): 39 | self = .done 40 | return .ready(nil) 41 | 42 | case .pending: 43 | self = .pending(base) 44 | return .pending 45 | } 46 | 47 | case .completed: 48 | self = .done 49 | return .ready(nil) 50 | 51 | case .done: 52 | fatalError("cannot poll after completion") 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/ConcatenateStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConcatenateStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Concatenate where Suffix.Output == Prefix.Output { 10 | case pollPrefix(Prefix, Suffix) 11 | case pollSuffix(Suffix) 12 | case done 13 | 14 | @inlinable 15 | public init(prefix: Prefix, suffix: Suffix) { 16 | self = .pollPrefix(prefix, suffix) 17 | } 18 | } 19 | } 20 | 21 | extension Stream._Private.Concatenate: StreamProtocol { 22 | public typealias Output = Suffix.Output 23 | 24 | @inlinable 25 | public mutating func pollNext(_ context: inout Context) -> Poll { 26 | while true { 27 | switch self { 28 | case .pollPrefix(var prefix, let suffix): 29 | switch prefix.pollNext(&context) { 30 | case .ready(.some(let output)): 31 | self = .pollPrefix(prefix, suffix) 32 | return .ready(output) 33 | case .ready(.none): 34 | self = .pollSuffix(suffix) 35 | continue 36 | case .pending: 37 | self = .pollPrefix(prefix, suffix) 38 | return .pending 39 | } 40 | 41 | case .pollSuffix(var suffix): 42 | switch suffix.pollNext(&context) { 43 | case .ready(.some(let output)): 44 | self = .pollSuffix(suffix) 45 | return .ready(output) 46 | case .ready(.none): 47 | self = .done 48 | return .ready(nil) 49 | case .pending: 50 | self = .pollSuffix(suffix) 51 | return .pending 52 | } 53 | 54 | case .done: 55 | fatalError("cannot poll after completion") 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/DropStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Drop { 10 | case dropping(Base, Int) 11 | case flushing(Base) 12 | case done 13 | 14 | @inlinable 15 | public init(base: Base, count: Int) { 16 | self = .dropping(base, count) 17 | } 18 | } 19 | } 20 | 21 | extension Stream._Private.Drop: StreamProtocol { 22 | public typealias Output = Base.Output 23 | 24 | @inlinable 25 | public mutating func pollNext(_ context: inout Context) -> Poll { 26 | while true { 27 | switch self { 28 | case .dropping(let base, 0): 29 | self = .flushing(base) 30 | continue 31 | 32 | case .dropping(var base, let count): 33 | switch base.pollNext(&context) { 34 | case .ready(.some): 35 | self = .dropping(base, count - 1) 36 | continue 37 | case .ready(.none): 38 | self = .done 39 | return .ready(nil) 40 | case .pending: 41 | self = .dropping(base, count) 42 | return .pending 43 | } 44 | 45 | case .flushing(var base): 46 | switch base.pollNext(&context) { 47 | case .ready(.some(let output)): 48 | self = .flushing(base) 49 | return .ready(output) 50 | case .ready(.none): 51 | self = .done 52 | return .ready(nil) 53 | case .pending: 54 | self = .flushing(base) 55 | return .pending 56 | } 57 | 58 | case .done: 59 | fatalError("cannot poll after completion") 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/DropUntilOutputStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropUntilOutputStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum DropUntilOutput { 10 | case pending(Base, Signal) 11 | case flushing(Base) 12 | case done 13 | 14 | @inlinable 15 | public init(base: Base, future: Signal) { 16 | self = .pending(base, future) 17 | } 18 | } 19 | } 20 | 21 | extension Stream._Private.DropUntilOutput: StreamProtocol { 22 | public typealias Output = Base.Output 23 | 24 | @inlinable 25 | public mutating func pollNext(_ context: inout Context) -> Poll { 26 | switch self { 27 | case .pending(var base, var future): 28 | while true { 29 | switch base.pollNext(&context) { 30 | case .ready(.some(let output)): 31 | switch future.poll(&context) { 32 | case .ready: 33 | self = .flushing(base) 34 | return .ready(output) 35 | case .pending: 36 | continue 37 | } 38 | case .ready(.none): 39 | self = .done 40 | return .ready(nil) 41 | case .pending: 42 | self = .pending(base, future) 43 | return .pending 44 | } 45 | } 46 | 47 | case .flushing(var base): 48 | switch base.pollNext(&context) { 49 | case .ready(.some(let output)): 50 | self = .flushing(base) 51 | return .ready(output) 52 | case .ready(.none): 53 | self = .done 54 | return .ready(nil) 55 | case .pending: 56 | self = .flushing(base) 57 | return .pending 58 | } 59 | 60 | case .done: 61 | fatalError("cannot poll after completion") 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/DropWhileStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropWhileStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum DropWhile { 10 | public typealias Predicate = (Base.Output) -> Bool 11 | 12 | case dropping(Base, Predicate) 13 | case flushing(Base) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, predicate: @escaping Predicate) { 18 | self = .dropping(base, predicate) 19 | } 20 | } 21 | } 22 | 23 | extension Stream._Private.DropWhile: StreamProtocol { 24 | public typealias Output = Base.Output 25 | 26 | @inlinable 27 | public mutating func pollNext(_ context: inout Context) -> Poll { 28 | switch self { 29 | case .dropping(var base, let predicate): 30 | while true { 31 | switch base.pollNext(&context) { 32 | case .ready(.some(let output)): 33 | if predicate(output) { 34 | continue 35 | } 36 | self = .flushing(base) 37 | return .ready(output) 38 | case .ready(.none): 39 | self = .done 40 | return .ready(nil) 41 | case .pending: 42 | self = .dropping(base, predicate) 43 | return .pending 44 | } 45 | } 46 | 47 | case .flushing(var base): 48 | switch base.pollNext(&context) { 49 | case .ready(.some(let output)): 50 | self = .flushing(base) 51 | return .ready(output) 52 | case .ready(.none): 53 | self = .done 54 | return .ready(nil) 55 | case .pending: 56 | self = .flushing(base) 57 | return .pending 58 | } 59 | 60 | case .done: 61 | fatalError("cannot poll after completion") 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/EmptyStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmptyStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct Empty: StreamProtocol { 10 | @inlinable 11 | public init() {} 12 | 13 | @inlinable 14 | public mutating func pollNext(_: inout Context) -> Poll { 15 | return .ready(nil) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/EnumerateStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnumerateStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct Enumerate: StreamProtocol { 10 | public typealias Output = (offset: Int, output: Base.Output) 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base) { 16 | var offset = 0 17 | _base = .init(base: base) { 18 | defer { offset += 1 } 19 | return (offset, $0) 20 | } 21 | } 22 | 23 | @inlinable 24 | public mutating func pollNext(_ context: inout Context) -> Poll { 25 | return _base.pollNext(&context) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/FilterStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FilterStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Filter { 10 | public typealias Predicate = (Base.Output) -> Bool 11 | 12 | case pending(Base, Predicate) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, isIncluded: @escaping Predicate) { 17 | self = .pending(base, isIncluded) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.Filter: StreamProtocol { 23 | public typealias Output = Base.Output 24 | 25 | @inlinable 26 | public mutating func pollNext(_ context: inout Context) -> Poll { 27 | switch self { 28 | case .pending(var base, let isIncluded): 29 | while true { 30 | switch base.pollNext(&context) { 31 | case .ready(.some(let output)): 32 | if isIncluded(output) { 33 | self = .pending(base, isIncluded) 34 | return .ready(output) 35 | } 36 | continue 37 | 38 | case .ready(.none): 39 | self = .done 40 | return .ready(nil) 41 | 42 | case .pending: 43 | self = .pending(base, isIncluded) 44 | return .pending 45 | } 46 | } 47 | 48 | case .done: 49 | fatalError("cannot poll after completion") 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/FlatMapStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlatMapStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum FlatMap { 10 | public typealias Transform = (Base.Output) -> U 11 | 12 | case pending(Base, Transform) 13 | case waiting(Base, Transform, U.StreamType) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, transform: @escaping Transform) { 18 | self = .pending(base, transform) 19 | } 20 | } 21 | } 22 | 23 | extension Stream._Private.FlatMap: StreamProtocol { 24 | public typealias Output = U.StreamType.Output 25 | 26 | @inlinable 27 | public mutating func pollNext(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(var base, let transform): 31 | switch base.pollNext(&context) { 32 | case .ready(.some(let output)): 33 | let stream = transform(output).makeStream() 34 | self = .waiting(base, transform, stream) 35 | continue 36 | case .ready(.none): 37 | self = .done 38 | return .ready(nil) 39 | case .pending: 40 | self = .pending(base, transform) 41 | return .pending 42 | } 43 | 44 | case .waiting(let base, let transform, var stream): 45 | switch stream.pollNext(&context) { 46 | case .ready(.some(let output)): 47 | self = .waiting(base, transform, stream) 48 | return .ready(output) 49 | case .ready(.none): 50 | self = .pending(base, transform) 51 | continue 52 | case .pending: 53 | self = .waiting(base, transform, stream) 54 | return .pending 55 | } 56 | 57 | case .done: 58 | fatalError("cannot poll after completion") 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/FlattenResultStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlattenResultStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct FlattenResult: StreamProtocol where Base.Output == Result, Failure> { 10 | public typealias Output = Result 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base) { 16 | _base = .init(base: base) { 17 | $0.flatten() 18 | } 19 | } 20 | 21 | @inlinable 22 | public mutating func pollNext(_ context: inout Context) -> Poll { 23 | return _base.pollNext(&context) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/FlattenStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FlattenStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct Flatten: StreamProtocol where Base.Output: StreamConvertible { 10 | public typealias Output = Base.Output.StreamType.Output 11 | 12 | @usableFromInline var _base: FlatMap 13 | 14 | @inlinable 15 | public init(base: Base) { 16 | _base = .init(base: base) { 17 | $0.makeStream() 18 | } 19 | } 20 | 21 | @inlinable 22 | public mutating func pollNext(_ context: inout Context) -> Poll { 23 | return _base.pollNext(&context) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/ForEachStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ForEachStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct ForEach: StreamProtocol { 10 | public typealias Output = Base.Output 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base, inspect: @escaping (Base.Output) -> Void) { 16 | _base = .init(base: base) { 17 | inspect($0) 18 | return $0 19 | } 20 | } 21 | 22 | @inlinable 23 | public mutating func pollNext(_ context: inout Context) -> Poll { 24 | return _base.pollNext(&context) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/GenerateStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GenerateStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Generate { 10 | public typealias Generator = (Output) -> Output? 11 | 12 | case initial(Generator, Output) 13 | case pending(Generator, Output) 14 | case done 15 | 16 | @inlinable 17 | public init(first: Output, next: @escaping Generator) { 18 | self = .initial(next, first) 19 | } 20 | } 21 | } 22 | 23 | extension Stream._Private.Generate: StreamProtocol { 24 | @inlinable 25 | public mutating func pollNext(_: inout Context) -> Poll { 26 | switch self { 27 | case .initial(let next, let output): 28 | self = .pending(next, output) 29 | return .ready(output) 30 | 31 | case .pending(let next, let previousOutput): 32 | if let output = next(previousOutput) { 33 | self = .pending(next, output) 34 | return .ready(output) 35 | } 36 | self = .done 37 | return .ready(nil) 38 | 39 | case .done: 40 | fatalError("cannot poll after completion") 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/HandleEventsStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HandleEventsStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum HandleEvents { 10 | public typealias Ready = (Base.Output) -> Void 11 | public typealias Pending = () -> Void 12 | public typealias Complete = () -> Void 13 | 14 | case pending(Base, Ready, Pending, Complete) 15 | case done 16 | 17 | @inlinable 18 | public init(base: Base, ready: @escaping Ready, pending: @escaping Pending, complete: @escaping Complete) { 19 | self = .pending(base, ready, pending, complete) 20 | } 21 | } 22 | } 23 | 24 | extension Stream._Private.HandleEvents: StreamProtocol { 25 | public typealias Output = Base.Output 26 | 27 | @inlinable 28 | public mutating func pollNext(_ context: inout Context) -> Poll { 29 | switch self { 30 | case .pending(var base, let ready, let pending, let complete): 31 | switch base.pollNext(&context) { 32 | case .ready(.some(let output)): 33 | self = .pending(base, ready, pending, complete) 34 | ready(output) 35 | return .ready(output) 36 | 37 | case .ready(.none): 38 | self = .done 39 | complete() 40 | return .ready(nil) 41 | 42 | case .pending: 43 | self = .pending(base, ready, pending, complete) 44 | pending() 45 | return .pending 46 | } 47 | 48 | case .done: 49 | fatalError("cannot poll after completion") 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/JustStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JustStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Just { 10 | case pending(Output) 11 | case complete 12 | case done 13 | 14 | @inlinable 15 | public init(element: Output) { 16 | self = .pending(element) 17 | } 18 | } 19 | } 20 | 21 | extension Stream._Private.Just: StreamProtocol { 22 | @inlinable 23 | public mutating func pollNext(_: inout Context) -> Poll { 24 | switch self { 25 | case .pending(let output): 26 | self = .complete 27 | return .ready(output) 28 | case .complete: 29 | self = .done 30 | return .ready(nil) 31 | case .done: 32 | fatalError("cannot poll after completion") 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/LatestStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LatestStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Latest { 10 | case pending(Base) 11 | case complete 12 | case done 13 | 14 | @inlinable 15 | public init(base: Base) { 16 | self = .pending(base) 17 | } 18 | } 19 | } 20 | 21 | extension Stream._Private.Latest: StreamProtocol { 22 | public typealias Output = Base.Output 23 | 24 | @inlinable 25 | public mutating func pollNext(_ context: inout Context) -> Poll { 26 | switch self { 27 | case .pending(var base): 28 | var lastOutput: Output? 29 | 30 | while true { 31 | switch base.pollNext(&context) { 32 | case .ready(.some(let output)): 33 | lastOutput = output 34 | continue 35 | case .ready(.none): 36 | if let output = lastOutput { 37 | self = .complete 38 | return .ready(output) 39 | } 40 | self = .done 41 | return .ready(nil) 42 | case .pending: 43 | self = .pending(base) 44 | if let output = lastOutput { 45 | return .ready(output) 46 | } 47 | return .pending 48 | } 49 | } 50 | 51 | case .complete: 52 | return .ready(nil) 53 | 54 | case .done: 55 | fatalError("cannot poll after completion") 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/LazyStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LazyStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Lazy { 10 | public typealias Constructor = () -> U 11 | 12 | case pending(Constructor) 13 | case waiting(U.StreamType) 14 | case done 15 | 16 | @inlinable 17 | public init(_ body: @escaping Constructor) { 18 | self = .pending(body) 19 | } 20 | } 21 | } 22 | 23 | extension Stream._Private.Lazy: StreamProtocol { 24 | public typealias Output = U.StreamType.Output 25 | 26 | @inlinable 27 | public mutating func pollNext(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(let constructor): 31 | let s = constructor() 32 | self = .waiting(s.makeStream()) 33 | continue 34 | 35 | case .waiting(var stream): 36 | switch stream.pollNext(&context) { 37 | case .ready(.some(let output)): 38 | self = .waiting(stream) 39 | return .ready(output) 40 | case .ready(.none): 41 | self = .done 42 | return .ready(nil) 43 | case .pending: 44 | self = .waiting(stream) 45 | return .pending 46 | } 47 | 48 | case .done: 49 | fatalError("cannot poll after completion") 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/MapKeyPathStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapKeyPathStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct MapKeyPath: StreamProtocol { 10 | public typealias Selector = KeyPath 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base, keyPath: Selector) { 16 | _base = .init(base: base) { 17 | $0[keyPath: keyPath] 18 | } 19 | } 20 | 21 | @inlinable 22 | public mutating func pollNext(_ context: inout Context) -> Poll { 23 | return _base.pollNext(&context) 24 | } 25 | } 26 | } 27 | 28 | extension Stream._Private { 29 | public struct MapKeyPath2: StreamProtocol { 30 | public typealias Output = (Output0, Output1) 31 | public typealias Selector = KeyPath 32 | 33 | @usableFromInline var _base: Map 34 | 35 | @inlinable 36 | public init(base: Base, keyPath0: Selector, keyPath1: Selector) { 37 | _base = .init(base: base) { 38 | return ( 39 | $0[keyPath: keyPath0], 40 | $0[keyPath: keyPath1] 41 | ) 42 | } 43 | } 44 | 45 | @inlinable 46 | public mutating func pollNext(_ context: inout Context) -> Poll { 47 | return _base.pollNext(&context) 48 | } 49 | } 50 | } 51 | 52 | extension Stream._Private { 53 | public struct MapKeyPath3: StreamProtocol { 54 | public typealias Output = (Output0, Output1, Output2) 55 | public typealias Selector = KeyPath 56 | 57 | @usableFromInline var _base: Map 58 | 59 | @inlinable 60 | public init(base: Base, keyPath0: Selector, keyPath1: Selector, keyPath2: Selector) { 61 | _base = .init(base: base) { 62 | return ( 63 | $0[keyPath: keyPath0], 64 | $0[keyPath: keyPath1], 65 | $0[keyPath: keyPath2] 66 | ) 67 | } 68 | } 69 | 70 | @inlinable 71 | public mutating func pollNext(_ context: inout Context) -> Poll { 72 | return _base.pollNext(&context) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/MapResultStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapResultStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct MapValue: StreamProtocol where Base.Output == Result { 10 | public typealias Output = Result 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base, success: @escaping (Success) -> NewSuccess) { 16 | _base = .init(base: base) { 17 | $0.map(success) 18 | } 19 | } 20 | 21 | @inlinable 22 | public mutating func pollNext(_ context: inout Context) -> Poll { 23 | return _base.pollNext(&context) 24 | } 25 | } 26 | } 27 | 28 | extension Stream._Private { 29 | public struct MapError: StreamProtocol where Base.Output == Result, NewFailure: Error { 30 | public typealias Output = Result 31 | 32 | @usableFromInline var _base: Map 33 | 34 | @inlinable 35 | public init(base: Base, failure: @escaping (Failure) -> NewFailure) { 36 | _base = .init(base: base) { 37 | $0.mapError(failure) 38 | } 39 | } 40 | 41 | @inlinable 42 | public mutating func pollNext(_ context: inout Context) -> Poll { 43 | return _base.pollNext(&context) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/MapStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Map { 10 | public typealias Transform = (Base.Output) -> Output 11 | 12 | case pending(Base, Transform) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, transform: @escaping Transform) { 17 | self = .pending(base, transform) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.Map where Base.Output == Output? { 23 | @inlinable 24 | public init(replacingNilFrom base: Base, with output: Output) { 25 | self = .pending(base) { $0 ?? output } 26 | } 27 | } 28 | 29 | extension Stream._Private.Map: StreamProtocol { 30 | @inlinable 31 | public mutating func pollNext(_ context: inout Context) -> Poll { 32 | switch self { 33 | case .pending(var base, let transform): 34 | switch base.pollNext(&context) { 35 | case .ready(.some(let output)): 36 | self = .pending(base, transform) 37 | return .ready(transform(output)) 38 | 39 | case .ready(.none): 40 | self = .done 41 | return .ready(nil) 42 | 43 | case .pending: 44 | self = .pending(base, transform) 45 | return .pending 46 | } 47 | 48 | case .done: 49 | fatalError("cannot poll after completion") 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/MatchEitherStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MatchEitherStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct MatchEither: StreamProtocol where Base.Output == Either { 10 | public typealias LeftHandler = (Left) -> Output 11 | public typealias RightHandler = (Right) -> Output 12 | 13 | @usableFromInline var _base: Map 14 | 15 | @inlinable 16 | public init(base: Base, left: @escaping LeftHandler, right: @escaping RightHandler) { 17 | _base = .init(base: base) { 18 | $0.match(left: left, right: right) 19 | } 20 | } 21 | 22 | @inlinable 23 | public mutating func pollNext(_ context: inout Context) -> Poll { 24 | return _base.pollNext(&context) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/MatchOptionalStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MatchOptionalStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct MatchOptional: StreamProtocol where Base.Output == Wrapped? { 10 | public typealias SomeHandler = (Wrapped) -> Output 11 | public typealias NoneHandler = () -> Output 12 | 13 | @usableFromInline var _base: Map 14 | 15 | @inlinable 16 | public init(base: Base, some: @escaping SomeHandler, none: @escaping NoneHandler) { 17 | _base = .init(base: base) { 18 | $0.match(some: some, none: none) 19 | } 20 | } 21 | 22 | @inlinable 23 | public mutating func pollNext(_ context: inout Context) -> Poll { 24 | return _base.pollNext(&context) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/MatchResultStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MatchResultStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct MatchResult: StreamProtocol where Base.Output == Result { 10 | public typealias SuccessHandler = (Success) -> Output 11 | public typealias FailureHandler = (Failure) -> Output 12 | 13 | @usableFromInline var _base: Map 14 | 15 | @inlinable 16 | public init(base: Base, success: @escaping SuccessHandler, failure: @escaping FailureHandler) { 17 | _base = .init(base: base) { 18 | $0.match(success: success, failure: failure) 19 | } 20 | } 21 | 22 | @inlinable 23 | public mutating func pollNext(_ context: inout Context) -> Poll { 24 | return _base.pollNext(&context) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/MergeAllStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MergeAllStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct MergeAll { 10 | private typealias F = Future 11 | private let _futures = _TaskScheduler() 12 | 13 | public init(_ bases: Base...) { 14 | _futures.schedule(bases.lazy.map(F.init)) 15 | } 16 | 17 | public init(_ bases: C) where C.Element == Base { 18 | _futures.schedule(bases.lazy.map(F.init)) 19 | } 20 | } 21 | } 22 | 23 | extension Stream._Private.MergeAll: StreamProtocol { 24 | public typealias Output = Base.Output 25 | 26 | public func pollNext(_ context: inout Context) -> Poll { 27 | while true { 28 | switch _futures.pollNext(&context) { 29 | case .ready(.some((.some(let output), let stream))): 30 | _futures.schedule(.init(base: stream)) 31 | return .ready(output) 32 | case .ready(.some((.none, _))): 33 | continue 34 | case .ready(.none): 35 | return .ready(nil) 36 | case .pending: 37 | return .pending 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/NeverStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeverStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct Never: StreamProtocol { 10 | @inlinable 11 | public init() {} 12 | 13 | @inlinable 14 | public mutating func pollNext(_: inout Context) -> Poll { 15 | return .pending 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/OptionalStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OptionalStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | // swiftformat:disable:next typeSugar 10 | public enum Optional: StreamProtocol { 11 | // swiftlint:disable:previous syntactic_sugar 12 | case some(Wrapped) 13 | case none 14 | case done 15 | 16 | @inlinable 17 | public init(value: Wrapped?) { 18 | switch value { 19 | case .some(let value): 20 | self = .some(value) 21 | case .none: 22 | self = .none 23 | } 24 | } 25 | 26 | @inlinable 27 | public mutating func pollNext(_: inout Context) -> Poll { 28 | switch self { 29 | case .some(let value): 30 | self = .none 31 | return .ready(value) 32 | case .none: 33 | self = .done 34 | return .ready(nil) 35 | case .done: 36 | fatalError("cannot poll after completion") 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/OutputStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OutputStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Output { 10 | case pending(Base, Int, CountableRange) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base, range: CountableRange) { 15 | self = .pending(base, 0, range) 16 | } 17 | } 18 | } 19 | 20 | extension Stream._Private.Output: StreamProtocol { 21 | public typealias Output = Base.Output 22 | 23 | @inlinable 24 | public mutating func pollNext(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base, var index, let range): 27 | while true { 28 | switch base.pollNext(&context) { 29 | case .ready(.some(let output)): 30 | if range.contains(index) { 31 | self = .pending(base, index + 1, range) 32 | return .ready(output) 33 | } 34 | index += 1 35 | continue 36 | 37 | case .ready(.none): 38 | self = .done 39 | return .ready(nil) 40 | 41 | case .pending: 42 | self = .pending(base, index, range) 43 | return .pending 44 | } 45 | } 46 | 47 | case .done: 48 | fatalError("cannot poll after completion") 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/PollOnStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PollOnStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum PollOn { 10 | public typealias Next = Futures.Future._Private.PollOn> 11 | 12 | case pending(E, Base) 13 | case waiting(E, Next) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, executor: E) { 18 | self = .pending(executor, base) 19 | } 20 | } 21 | } 22 | 23 | extension Stream._Private.PollOn: StreamProtocol { 24 | public typealias Output = Result 25 | 26 | @inlinable 27 | public mutating func pollNext(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(let executor, let base): 31 | self = .waiting(executor, base.makeFuture().poll(on: executor)) 32 | continue 33 | 34 | case .waiting(let executor, var future): 35 | switch future.poll(&context) { 36 | case .ready(.success(let (output, stream))): 37 | switch output { 38 | case .some(let output): 39 | self = .pending(executor, stream) 40 | return .ready(.success(output)) 41 | case .none: 42 | self = .done 43 | return .ready(nil) 44 | } 45 | case .ready(.failure(let error)): 46 | self = .done 47 | return .ready(.failure(error)) 48 | case .pending: 49 | self = .waiting(executor, future) 50 | return .pending 51 | } 52 | 53 | case .done: 54 | fatalError("cannot poll after completion") 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/PrefixStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrefixStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Prefix { 10 | case pending(Base, Int, Int) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base, maxLength: Int) { 15 | self = .pending(base, 0, maxLength) 16 | } 17 | } 18 | } 19 | 20 | extension Stream._Private.Prefix: StreamProtocol { 21 | public typealias Output = Base.Output 22 | 23 | @inlinable 24 | public mutating func pollNext(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base, let index, let maxLength): 27 | if index == maxLength { 28 | self = .done 29 | return .ready(nil) 30 | } 31 | switch base.pollNext(&context) { 32 | case .ready(.some(let output)): 33 | self = .pending(base, index + 1, maxLength) 34 | return .ready(output) 35 | case .ready(.none): 36 | self = .done 37 | return .ready(nil) 38 | case .pending: 39 | self = .pending(base, index, maxLength) 40 | return .pending 41 | } 42 | 43 | case .done: 44 | fatalError("cannot poll after completion") 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/PrefixUntilOutputStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrefixUntilOutputStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum PrefixUntilOutput { 10 | case pending(Base, Other) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base, future: Other) { 15 | self = .pending(base, future) 16 | } 17 | } 18 | } 19 | 20 | extension Stream._Private.PrefixUntilOutput: StreamProtocol { 21 | public typealias Output = Base.Output 22 | 23 | @inlinable 24 | public mutating func pollNext(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base, var future): 27 | switch future.poll(&context) { 28 | case .ready: 29 | self = .done 30 | return .ready(nil) 31 | case .pending: 32 | switch base.pollNext(&context) { 33 | case .ready(.some(let output)): 34 | self = .pending(base, future) 35 | return .ready(output) 36 | 37 | case .ready(.none): 38 | self = .done 39 | return .ready(nil) 40 | 41 | case .pending: 42 | self = .pending(base, future) 43 | return .pending 44 | } 45 | } 46 | 47 | case .done: 48 | fatalError("cannot poll after completion") 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/PrefixWhileStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrefixWhileStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum PrefixWhile { 10 | public typealias Predicate = (Base.Output) -> Bool 11 | 12 | case pending(Base, Predicate) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, predicate: @escaping Predicate) { 17 | self = .pending(base, predicate) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.PrefixWhile: StreamProtocol { 23 | public typealias Output = Base.Output 24 | 25 | @inlinable 26 | public mutating func pollNext(_ context: inout Context) -> Poll { 27 | switch self { 28 | case .pending(var base, let predicate): 29 | while true { 30 | switch base.pollNext(&context) { 31 | case .ready(.some(let output)): 32 | if predicate(output) { 33 | self = .pending(base, predicate) 34 | return .ready(output) 35 | } 36 | self = .done 37 | return .ready(nil) 38 | 39 | case .ready(.none): 40 | self = .done 41 | return .ready(nil) 42 | 43 | case .pending: 44 | self = .pending(base, predicate) 45 | return .pending 46 | } 47 | } 48 | 49 | case .done: 50 | fatalError("cannot poll after completion") 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/PrintStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrintStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Print { 10 | case pending(Base, String, TextOutputStream) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base, prefix: String, to stream: TextOutputStream?) { 15 | self = .pending(base, prefix, stream ?? StandardOutputStream()) 16 | } 17 | } 18 | } 19 | 20 | extension Stream._Private.Print: StreamProtocol { 21 | public typealias Output = Base.Output 22 | 23 | @inlinable 24 | public mutating func pollNext(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base, let prefix, var stream): 27 | let result = base.pollNext(&context) 28 | switch result { 29 | case .ready(.none): 30 | self = .done 31 | default: 32 | self = .pending(base, prefix, stream) 33 | } 34 | let message = _makeMessage(prefix: prefix, result: result) 35 | stream.write(message) 36 | return result 37 | 38 | case .done: 39 | fatalError("cannot poll after completion") 40 | } 41 | } 42 | 43 | @inlinable 44 | func _makeMessage(prefix: String, result: Poll) -> String { 45 | var parts = [String]() 46 | if !prefix.isEmpty { 47 | parts.append(prefix) 48 | } 49 | switch result { 50 | case .ready(.some(let output)): 51 | parts.append(".ready(\(output))") 52 | case .ready(.none): 53 | parts.append(".ready(nil)") 54 | case .pending: 55 | parts.append(".pending") 56 | } 57 | return parts.joined(separator: " ") + "\n" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/ReferenceStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReferenceStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public final class Reference: StreamProtocol { 10 | @usableFromInline var _base: Base 11 | 12 | @inlinable 13 | public init(base: Base) { 14 | _base = base 15 | } 16 | 17 | @inlinable 18 | public func pollNext(_ context: inout Context) -> Poll { 19 | return _base.pollNext(&context) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/RemoveDuplicatesStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RemoveDuplicatesStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum RemoveDuplicates { 10 | public typealias Predicate = (Base.Output, Base.Output) -> Bool 11 | 12 | case pending(Base, Predicate) 13 | case comparing(Base, Predicate, Base.Output) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, predicate: @escaping Predicate) { 18 | self = .pending(base, predicate) 19 | } 20 | } 21 | } 22 | 23 | extension Stream._Private.RemoveDuplicates where Base.Output: Equatable { 24 | @inlinable 25 | public init(base: Base) { 26 | self = .pending(base) { 27 | $0 == $1 28 | } 29 | } 30 | } 31 | 32 | extension Stream._Private.RemoveDuplicates: StreamProtocol { 33 | public typealias Output = Base.Output 34 | 35 | @inlinable 36 | public mutating func pollNext(_ context: inout Context) -> Poll { 37 | switch self { 38 | case .pending(var base, let predicate): 39 | switch base.pollNext(&context) { 40 | case .ready(.some(let output)): 41 | self = .comparing(base, predicate, output) 42 | return .ready(output) 43 | case .ready(.none): 44 | self = .done 45 | return .ready(nil) 46 | case .pending: 47 | self = .pending(base, predicate) 48 | return .pending 49 | } 50 | 51 | case .comparing(var base, let predicate, let previousOutput): 52 | while true { 53 | switch base.pollNext(&context) { 54 | case .ready(.some(let output)): 55 | if predicate(previousOutput, output) { 56 | continue 57 | } 58 | self = .comparing(base, predicate, output) 59 | return .ready(output) 60 | case .ready(.none): 61 | self = .done 62 | return .ready(nil) 63 | case .pending: 64 | self = .comparing(base, predicate, previousOutput) 65 | return .pending 66 | } 67 | } 68 | 69 | case .done: 70 | fatalError("cannot poll after completion") 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/RepeatStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepeatStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct Repeat: StreamProtocol { 10 | @usableFromInline let _element: Output 11 | 12 | @inlinable 13 | public init(element: Output) { 14 | _element = element 15 | } 16 | 17 | @inlinable 18 | public func pollNext(_: inout Context) -> Poll { 19 | return .ready(_element) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/ReplaceEmptyStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReplaceEmptyStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum ReplaceEmpty { 10 | case pending(Base, Output) 11 | case notEmpty(Base) 12 | case complete 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, output: Output) { 17 | self = .pending(base, output) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.ReplaceEmpty: StreamProtocol { 23 | public typealias Output = Base.Output 24 | 25 | @inlinable 26 | public mutating func pollNext(_ context: inout Context) -> Poll { 27 | switch self { 28 | case .pending(var base, let output): 29 | switch base.pollNext(&context) { 30 | case .ready(.some(let output)): 31 | self = .notEmpty(base) 32 | return .ready(output) 33 | case .ready(.none): 34 | self = .complete 35 | return .ready(output) 36 | case .pending: 37 | self = .pending(base, output) 38 | return .pending 39 | } 40 | 41 | case .notEmpty(var base): 42 | switch base.pollNext(&context) { 43 | case .ready(.some(let output)): 44 | self = .notEmpty(base) 45 | return .ready(output) 46 | case .ready(.none): 47 | self = .done 48 | return .ready(nil) 49 | case .pending: 50 | self = .notEmpty(base) 51 | return .pending 52 | } 53 | 54 | case .complete: 55 | self = .done 56 | return .ready(nil) 57 | 58 | case .done: 59 | fatalError("cannot poll after completion") 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/ReplaceErrorStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReplaceErrorStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct ReplaceError: StreamProtocol where Base.Output == Result { 10 | @usableFromInline var _base: Map 11 | 12 | @inlinable 13 | public init(base: Base, output: Output) { 14 | _base = .init(base: base) { 15 | $0.match( 16 | success: { $0 }, 17 | failure: { _ in output } 18 | ) 19 | } 20 | } 21 | 22 | @inlinable 23 | public mutating func pollNext(_ context: inout Context) -> Poll { 24 | return _base.pollNext(&context) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/ScanStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScanStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Scan { 10 | public typealias Accumulate = (Output, Base.Output) -> Output 11 | 12 | case pending(Base, Output, Accumulate) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, initialResult: Output, nextPartialResult: @escaping Accumulate) { 17 | self = .pending(base, initialResult, nextPartialResult) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.Scan: StreamProtocol { 23 | @inlinable 24 | public mutating func pollNext(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base, var output, let accumulate): 27 | switch base.pollNext(&context) { 28 | case .ready(.some(let result)): 29 | output = accumulate(output, result) 30 | self = .pending(base, output, accumulate) 31 | return .ready(output) 32 | 33 | case .ready(.none): 34 | self = .done 35 | return .ready(nil) 36 | 37 | case .pending: 38 | self = .pending(base, output, accumulate) 39 | return .pending 40 | } 41 | 42 | case .done: 43 | fatalError("cannot poll after completion") 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/SequenceStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SequenceStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Sequence { 10 | case pending(C.Iterator) 11 | case done 12 | 13 | @inlinable 14 | public init(sequence: C) { 15 | self = .pending(sequence.makeIterator()) 16 | } 17 | } 18 | } 19 | 20 | extension Stream._Private.Sequence: StreamProtocol { 21 | public typealias Output = C.Element 22 | 23 | @inlinable 24 | public mutating func pollNext(_: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var iter): 27 | switch iter.next() { 28 | case .some(let output): 29 | self = .pending(iter) 30 | return .ready(output) 31 | case .none: 32 | self = .done 33 | return .ready(nil) 34 | } 35 | case .done: 36 | fatalError("cannot poll after completion") 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/SetFailureTypeStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SetFailureTypeStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct SetFailureType: StreamProtocol { 10 | public typealias Output = Result 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public mutating func pollNext(_ context: inout Context) -> Poll { 16 | return _base.pollNext(&context) 17 | } 18 | } 19 | } 20 | 21 | extension Stream._Private.SetFailureType where Base.Output == Success { 22 | @inlinable 23 | public init(base: Base) { 24 | _base = .init(base: base) { 25 | .success($0) 26 | } 27 | } 28 | } 29 | 30 | extension Stream._Private.SetFailureType where Base.Output == Result { 31 | @inlinable 32 | public init(base: Base) { 33 | _base = .init(base: base) { 34 | // swiftlint:disable:next force_cast 35 | $0.mapError { $0 as! Failure } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamAllSatisfyFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamAllSatisfyFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum AllSatisfy { 10 | public typealias Predicate = (Base.Output) -> Bool 11 | 12 | case pending(Base, Predicate) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, predicate: @escaping Predicate) { 17 | self = .pending(base, predicate) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.AllSatisfy: FutureProtocol { 23 | public typealias Output = Bool 24 | 25 | @inlinable 26 | public mutating func poll(_ context: inout Context) -> Poll { 27 | switch self { 28 | case .pending(var base, let predicate): 29 | while true { 30 | switch base.pollNext(&context) { 31 | case .ready(.some(let output)): 32 | if predicate(output) { 33 | continue 34 | } 35 | self = .pending(base, predicate) 36 | return .ready(false) 37 | 38 | case .ready(.none): 39 | self = .done 40 | return .ready(true) 41 | 42 | case .pending: 43 | self = .pending(base, predicate) 44 | return .pending 45 | } 46 | } 47 | 48 | case .done: 49 | fatalError("cannot poll after completion") 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamContainsFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamContainsFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Contains where Base.Output: Equatable { 10 | case pending(Base, Base.Output) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base, output: Base.Output) { 15 | self = .pending(base, output) 16 | } 17 | } 18 | } 19 | 20 | extension Stream._Private.Contains: FutureProtocol { 21 | public typealias Output = Bool 22 | 23 | @inlinable 24 | public mutating func poll(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base, let output): 27 | while true { 28 | switch base.pollNext(&context) { 29 | case .ready(.some(let result)): 30 | if output == result { 31 | self = .done 32 | return .ready(true) 33 | } 34 | continue 35 | 36 | case .ready(.none): 37 | self = .done 38 | return .ready(false) 39 | 40 | case .pending: 41 | self = .pending(base, output) 42 | return .pending 43 | } 44 | } 45 | 46 | case .done: 47 | fatalError("cannot poll after completion") 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamContainsWhereFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamContainsWhereFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum ContainsWhere { 10 | public typealias Predicate = (Base.Output) -> Bool 11 | 12 | case pending(Base, Predicate) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, predicate: @escaping Predicate) { 17 | self = .pending(base, predicate) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.ContainsWhere: FutureProtocol { 23 | public typealias Output = Bool 24 | 25 | @inlinable 26 | public mutating func poll(_ context: inout Context) -> Poll { 27 | switch self { 28 | case .pending(var base, let predicate): 29 | while true { 30 | switch base.pollNext(&context) { 31 | case .ready(.some(let output)): 32 | if predicate(output) { 33 | self = .done 34 | return .ready(true) 35 | } 36 | continue 37 | 38 | case .ready(.none): 39 | self = .done 40 | return .ready(false) 41 | 42 | case .pending: 43 | self = .pending(base, predicate) 44 | return .pending 45 | } 46 | } 47 | 48 | case .done: 49 | fatalError("cannot poll after completion") 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamFirstFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamFirstFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum First { 10 | case pending(Base) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base) { 15 | self = .pending(base) 16 | } 17 | } 18 | } 19 | 20 | extension Stream._Private.First: FutureProtocol { 21 | public typealias Output = Base.Output? 22 | 23 | @inlinable 24 | public mutating func poll(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base): 27 | switch base.pollNext(&context) { 28 | case .ready(let result): 29 | self = .done 30 | return .ready(result) 31 | case .pending: 32 | self = .pending(base) 33 | return .pending 34 | } 35 | 36 | case .done: 37 | fatalError("cannot poll after completion") 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamFirstWhereFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamFirstWhereFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum FirstWhere { 10 | public typealias Predicate = (Base.Output) -> Bool 11 | 12 | case pending(Base, Predicate) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, predicate: @escaping Predicate) { 17 | self = .pending(base, predicate) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.FirstWhere: FutureProtocol { 23 | public typealias Output = Base.Output? 24 | 25 | @inlinable 26 | public mutating func poll(_ context: inout Context) -> Poll { 27 | switch self { 28 | case .pending(var base, let predicate): 29 | while true { 30 | switch base.pollNext(&context) { 31 | case .ready(.some(let output)): 32 | if predicate(output) { 33 | self = .done 34 | return .ready(output) 35 | } 36 | continue 37 | 38 | case .ready(.none): 39 | self = .done 40 | return .ready(nil) 41 | 42 | case .pending: 43 | self = .pending(base, predicate) 44 | return .pending 45 | } 46 | } 47 | 48 | case .done: 49 | fatalError("cannot poll after completion") 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Future { 10 | case pending(Base) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base) { 15 | self = .pending(base) 16 | } 17 | } 18 | } 19 | 20 | extension Stream._Private.Future: FutureProtocol { 21 | public typealias Output = (output: Base.Output?, stream: Base) 22 | 23 | @inlinable 24 | public mutating func poll(_ context: inout Context) -> Poll { 25 | switch self { 26 | case .pending(var base): 27 | switch base.pollNext(&context) { 28 | case .ready(let output): 29 | self = .done 30 | return .ready((output, base)) 31 | case .pending: 32 | self = .pending(base) 33 | return .pending 34 | } 35 | 36 | case .done: 37 | fatalError("cannot poll after completion") 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamIgnoreOutputFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamIgnoreOutputFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum IgnoreOutput { 10 | case pending(Base) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base) { 15 | self = .pending(base) 16 | } 17 | } 18 | } 19 | 20 | extension Stream._Private.IgnoreOutput: FutureProtocol { 21 | @inlinable 22 | public mutating func poll(_ context: inout Context) -> Poll { 23 | switch self { 24 | case .pending(var base): 25 | while true { 26 | switch base.pollNext(&context) { 27 | case .ready(.some): 28 | continue 29 | case .ready(.none): 30 | self = .done 31 | return .ready(()) 32 | case .pending: 33 | self = .pending(base) 34 | return .pending 35 | } 36 | } 37 | 38 | case .done: 39 | fatalError("cannot poll after completion") 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamLastFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamLastFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Last { 10 | case pending(Base) 11 | case flushing(Base, Base.Output) 12 | case done 13 | 14 | @inlinable 15 | public init(base: Base) { 16 | self = .pending(base) 17 | } 18 | } 19 | } 20 | 21 | extension Stream._Private.Last: FutureProtocol { 22 | public typealias Output = Base.Output? 23 | 24 | @inlinable 25 | public mutating func poll(_ context: inout Context) -> Poll { 26 | while true { 27 | switch self { 28 | case .pending(var base): 29 | switch base.pollNext(&context) { 30 | case .ready(.some(let output)): 31 | self = .flushing(base, output) 32 | continue 33 | case .ready(.none): 34 | self = .done 35 | return .ready(nil) 36 | case .pending: 37 | self = .pending(base) 38 | return .pending 39 | } 40 | 41 | case .flushing(var base, var lastOutput): 42 | while true { 43 | switch base.pollNext(&context) { 44 | case .ready(.some(let output)): 45 | lastOutput = output 46 | continue 47 | case .ready(.none): 48 | self = .done 49 | return .ready(lastOutput) 50 | case .pending: 51 | self = .flushing(base, lastOutput) 52 | return .pending 53 | } 54 | } 55 | 56 | case .done: 57 | fatalError("cannot poll after completion") 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamLastWhereFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamLastWhereFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum LastWhere { 10 | public typealias Predicate = (Base.Output) -> Bool 11 | 12 | case pending(Base, Predicate) 13 | case flushing(Base, Predicate, Base.Output) 14 | case done 15 | 16 | @inlinable 17 | public init(base: Base, predicate: @escaping Predicate) { 18 | self = .pending(base, predicate) 19 | } 20 | } 21 | } 22 | 23 | extension Stream._Private.LastWhere: FutureProtocol { 24 | public typealias Output = Base.Output? 25 | 26 | @inlinable 27 | public mutating func poll(_ context: inout Context) -> Poll { 28 | while true { 29 | switch self { 30 | case .pending(var base, let predicate): 31 | switch base.pollNext(&context) { 32 | case .ready(.some(let output)): 33 | if predicate(output) { 34 | self = .flushing(base, predicate, output) 35 | continue 36 | } 37 | self = .done 38 | return .ready(nil) 39 | case .ready(.none): 40 | self = .done 41 | return .ready(nil) 42 | case .pending: 43 | self = .pending(base, predicate) 44 | return .pending 45 | } 46 | 47 | case .flushing(var base, let predicate, var lastOutput): 48 | while true { 49 | switch base.pollNext(&context) { 50 | case .ready(.some(let output)): 51 | if predicate(output) { 52 | lastOutput = output 53 | } 54 | continue 55 | case .ready(.none): 56 | self = .done 57 | return .ready(lastOutput) 58 | case .pending: 59 | self = .flushing(base, predicate, lastOutput) 60 | return .pending 61 | } 62 | } 63 | 64 | case .done: 65 | fatalError("cannot poll after completion") 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamReduceFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamReduceFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Reduce { 10 | public typealias Accumulate = (Output, Base.Output) -> Output 11 | 12 | case pending(Base, Output, Accumulate) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, initialResult: Output, nextPartialResult: @escaping Accumulate) { 17 | self = .pending(base, initialResult, nextPartialResult) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.Reduce { 23 | @inlinable 24 | public init(replacingOutputFrom base: Base, with output: Output) { 25 | self = .pending(base, output) { o, _ in o } 26 | } 27 | } 28 | 29 | extension Stream._Private.Reduce where Output == Int { 30 | @inlinable 31 | public init(countingElementsFrom base: Base) { 32 | self = .pending(base, 0) { count, _ in count + 1 } 33 | } 34 | } 35 | 36 | extension Stream._Private.Reduce: FutureProtocol { 37 | @inlinable 38 | public mutating func poll(_ context: inout Context) -> Poll { 39 | switch self { 40 | case .pending(var base, var previousOutput, let accumulate): 41 | while true { 42 | switch base.pollNext(&context) { 43 | case .ready(.some(let output)): 44 | previousOutput = accumulate(previousOutput, output) 45 | continue 46 | case .ready(.none): 47 | self = .done 48 | return .ready(previousOutput) 49 | case .pending: 50 | self = .pending(base, previousOutput, accumulate) 51 | return .pending 52 | } 53 | } 54 | 55 | case .done: 56 | fatalError("cannot poll after completion") 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamReduceIntoFuture.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamReduceIntoFuture.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum ReduceInto { 10 | public typealias Reduce = (inout Output, Base.Output) -> Void 11 | 12 | case pending(Base, Output, Reduce) 13 | case done 14 | 15 | @inlinable 16 | public init(base: Base, state: Output, reducer: @escaping Reduce) { 17 | self = .pending(base, state, reducer) 18 | } 19 | } 20 | } 21 | 22 | extension Stream._Private.ReduceInto where Output == [Base.Output] { 23 | @inlinable 24 | public init(collectingOutputFrom base: Base) { 25 | self = .pending(base, []) { $0.append($1) } 26 | } 27 | } 28 | 29 | extension Stream._Private.ReduceInto: FutureProtocol { 30 | @inlinable 31 | public mutating func poll(_ context: inout Context) -> Poll { 32 | switch self { 33 | case .pending(var base, var state, let reducer): 34 | while true { 35 | switch base.pollNext(&context) { 36 | case .ready(.some(let output)): 37 | reducer(&state, output) 38 | continue 39 | case .ready(.none): 40 | self = .done 41 | return .ready(state) 42 | case .pending: 43 | self = .pending(base, state, reducer) 44 | return .pending 45 | } 46 | } 47 | 48 | case .done: 49 | fatalError("cannot poll after completion") 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/StreamReplayBuffer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamReplayBuffer.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | @usableFromInline 10 | enum _ReplayBuffer { 11 | case none 12 | case just(Output?) 13 | case some(CircularBuffer) 14 | case all([Output]) 15 | } 16 | } 17 | 18 | extension Stream._Private._ReplayBuffer { 19 | @inlinable 20 | init(strategy: Stream.ReplayStrategy) { 21 | switch strategy { 22 | case .none: 23 | self = .none 24 | case .latest: 25 | self = .just(nil) 26 | case .last(let count): 27 | self = .some(.init(capacity: count)) 28 | case .all: 29 | self = .all(.init()) 30 | } 31 | } 32 | 33 | @inlinable 34 | mutating func push(_ element: Output) { 35 | switch self { 36 | case .none: 37 | break 38 | case .just: 39 | self = .just(element) 40 | case .some(var buf): 41 | buf.push(element, expand: false) 42 | self = .some(buf) 43 | case .all(var elements): 44 | elements.append(element) 45 | self = .all(elements) 46 | } 47 | } 48 | 49 | @inlinable 50 | func copyElements() -> [Output] { 51 | switch self { 52 | case .none: 53 | return [] 54 | case .just(.none): 55 | return [] 56 | case .just(.some(let element)): 57 | return [element] 58 | case .some(let buf): 59 | return buf.copyElements() 60 | case .all(let elements): 61 | return elements 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/TryMapStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TryMapStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public struct TryMap: StreamProtocol { 10 | public typealias Output = Result 11 | 12 | @usableFromInline var _base: Map 13 | 14 | @inlinable 15 | public init(base: Base, catching block: @escaping (Base.Output) throws -> T) { 16 | _base = .init(base: base) { 17 | do { 18 | return try .success(block($0)) 19 | } catch { 20 | return .failure(error) 21 | } 22 | } 23 | } 24 | 25 | @inlinable 26 | public mutating func pollNext(_ context: inout Context) -> Poll { 27 | return _base.pollNext(&context) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/UnfoldStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnfoldStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Unfold { 10 | public typealias Next = (U.FutureType.Output) -> U? 11 | 12 | case initial(Next, U.FutureType.Output) 13 | case pending(Next, U.FutureType.Output) 14 | case waiting(Next, U.FutureType) 15 | case done 16 | 17 | @inlinable 18 | public init(initial: Output, next: @escaping Next) { 19 | self = .initial(next, initial) 20 | } 21 | } 22 | } 23 | 24 | extension Stream._Private.Unfold: StreamProtocol { 25 | public typealias Output = U.FutureType.Output 26 | 27 | @inlinable 28 | public mutating func pollNext(_ context: inout Context) -> Poll { 29 | while true { 30 | switch self { 31 | case .initial(let next, let initial): 32 | self = .pending(next, initial) 33 | return .ready(initial) 34 | 35 | case .pending(let next, let previousOutput): 36 | if let future = next(previousOutput)?.makeFuture() { 37 | self = .waiting(next, future) 38 | continue 39 | } 40 | self = .done 41 | return .ready(nil) 42 | 43 | case .waiting(let next, var future): 44 | switch future.poll(&context) { 45 | case .ready(let output): 46 | self = .pending(next, output) 47 | return .ready(output) 48 | case .pending: 49 | self = .waiting(next, future) 50 | return .pending 51 | } 52 | 53 | case .done: 54 | fatalError("cannot poll after completion") 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Futures/Stream/YieldStream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YieldStream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | extension Stream._Private { 9 | public enum Yield { 10 | case pending(Base, Int, Int) 11 | case done 12 | 13 | @inlinable 14 | public init(base: Base, maxElements: Int) { 15 | precondition(maxElements > 0) 16 | self = .pending(base, 0, maxElements) 17 | } 18 | } 19 | } 20 | 21 | extension Stream._Private.Yield: StreamProtocol { 22 | public typealias Output = Base.Output 23 | 24 | @inlinable 25 | public mutating func pollNext(_ context: inout Context) -> Poll { 26 | switch self { 27 | case .pending(var base, let count, let maxElements): 28 | if count == maxElements { 29 | self = .pending(base, 0, maxElements) 30 | return context.yield() 31 | } 32 | switch base.pollNext(&context) { 33 | case .ready(.some(let output)): 34 | self = .pending(base, count + 1, maxElements) 35 | return .ready(output) 36 | case .ready(.none): 37 | self = .done 38 | return .ready(nil) 39 | case .pending: 40 | self = .pending(base, 0, maxElements) 41 | return .pending 42 | } 43 | 44 | case .done: 45 | fatalError("cannot poll after completion") 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/FuturesPrivate/Private.c: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Sources/FuturesPrivate/include/FuturesPrivate.h: -------------------------------------------------------------------------------- 1 | // 2 | // FuturesPrivate.h 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. All rights reserved. 6 | // 7 | 8 | #ifndef FuturesPrivate_h 9 | #define FuturesPrivate_h 10 | 11 | #include "CAtomic.h" 12 | 13 | #endif /* FuturesPrivate_h */ 14 | -------------------------------------------------------------------------------- /Sources/FuturesSync/Atomic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Atomic.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesPrivate 9 | 10 | @_exported import enum FuturesPrivate.AtomicLoadMemoryOrder 11 | @_exported import enum FuturesPrivate.AtomicMemoryOrder 12 | @_exported import enum FuturesPrivate.AtomicStoreMemoryOrder 13 | 14 | public enum Atomic {} 15 | 16 | extension Atomic { 17 | /// Generic memory order-dependent fence synchronization primitive. 18 | /// 19 | /// Establishes memory synchronization ordering of non-atomic and relaxed 20 | /// atomic accesses, as instructed by `order`, without an associated 21 | /// atomic operation. 22 | /// 23 | /// For example, all non-atomic and relaxed atomic stores that happen 24 | /// before a memory_order_release fence in thread A will be synchronized 25 | /// with non-atomic and relaxed atomic loads from the same locations made 26 | /// in thread B after an memory_order_acquire fence. 27 | /// 28 | /// - Parameters: 29 | /// - order: the memory ordering executed by this fence 30 | @_transparent 31 | public static func threadFence(order: AtomicMemoryOrder = .seqcst) { 32 | CAtomicThreadFence(order) 33 | } 34 | 35 | /// Indicates to the hardware that the current thread is performing a task, 36 | /// for example a spinlock, that can be swapped out. Hardware can use this 37 | /// hint to suspend and resume threads. 38 | @_transparent 39 | public static func hardwarePause() { 40 | CAtomicHardwarePause() 41 | } 42 | 43 | /// Causes the calling thread to relinquish the CPU. The thread is moved 44 | /// to the end of the queue for its static priority and a new thread gets 45 | /// to run. 46 | /// 47 | /// - Parameters: 48 | /// - timeout: The time interval to suppress this thread's priority 49 | /// for, in milliseconds. 50 | @_transparent 51 | public static func preemptionYield(_ timeout: UInt64) { 52 | CAtomicPreemptionYield(timeout) 53 | } 54 | } 55 | 56 | // MARK: - Private - 57 | 58 | public protocol _CAtomicValue { 59 | associatedtype AtomicRawValue 60 | associatedtype AtomicPointer 61 | } 62 | 63 | public protocol _CAtomicInteger: _CAtomicValue 64 | where AtomicRawValue: FixedWidthInteger {} 65 | -------------------------------------------------------------------------------- /Sources/FuturesSync/AtomicQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AtomicQueue.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | public protocol AtomicQueueProtocol { 9 | associatedtype Element 10 | func tryPush(_ element: Element) -> Bool 11 | func pop() -> Element? 12 | } 13 | 14 | public protocol AtomicUnboundedQueueProtocol: AtomicQueueProtocol { 15 | func push(_ element: Element) 16 | } 17 | 18 | extension AtomicUnboundedQueueProtocol { 19 | @inlinable 20 | public func tryPush(_ element: Element) -> Bool { 21 | push(element) 22 | return true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/FuturesSync/AtomicQueue/AtomicMPMCQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AtomicMPMCQueue.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesPrivate 9 | 10 | /// A bounded FIFO queue that is safe to share among multiple producers and 11 | /// consumers. 12 | public struct AtomicMPMCQueue: AtomicQueueProtocol { 13 | @usableFromInline let _buffer: _AtomicBuffer 14 | 15 | @inlinable 16 | public init(capacity: Int) { 17 | _buffer = .create(capacity: capacity) 18 | } 19 | 20 | @inlinable 21 | public var capacity: Int { 22 | _buffer._capacity 23 | } 24 | 25 | @inlinable 26 | public func tryPush(_ element: Element) -> Bool { 27 | return _buffer._tryPushConcurrent(element) 28 | } 29 | 30 | @inlinable 31 | public func pop() -> Element? { 32 | return _buffer._popConcurrent() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/FuturesSync/AtomicQueue/AtomicMPSCQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AtomicMPSCQueue.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesPrivate 9 | 10 | /// A bounded FIFO queue that is safe to share among multiple producers and 11 | /// a single consumer. 12 | public struct AtomicMPSCQueue: AtomicQueueProtocol { 13 | @usableFromInline let _buffer: _AtomicBuffer 14 | 15 | @inlinable 16 | public init(capacity: Int) { 17 | _buffer = .create(capacity: capacity) 18 | } 19 | 20 | @inlinable 21 | public var capacity: Int { 22 | _buffer._capacity 23 | } 24 | 25 | @inlinable 26 | public func tryPush(_ element: Element) -> Bool { 27 | return _buffer._tryPushConcurrent(element) 28 | } 29 | 30 | @inlinable 31 | public func pop() -> Element? { 32 | return _buffer._pop() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/FuturesSync/AtomicQueue/AtomicSPMCQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AtomicSPMCQueue.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesPrivate 9 | 10 | /// A bounded FIFO queue that is safe to share among a single producer and 11 | /// multiple consumers. 12 | public struct AtomicSPMCQueue: AtomicQueueProtocol { 13 | @usableFromInline let _buffer: _AtomicBuffer 14 | 15 | @inlinable 16 | public init(capacity: Int) { 17 | _buffer = .create(capacity: capacity) 18 | } 19 | 20 | @inlinable 21 | public var capacity: Int { 22 | _buffer._capacity 23 | } 24 | 25 | @inlinable 26 | public func tryPush(_ element: Element) -> Bool { 27 | return _buffer._tryPush(element) 28 | } 29 | 30 | @inlinable 31 | public func pop() -> Element? { 32 | return _buffer._popConcurrent() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/FuturesSync/AtomicQueue/AtomicSPSCQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AtomicSPSCQueue.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesPrivate 9 | 10 | /// A bounded FIFO queue that is safe to share among a single producer and a 11 | /// single consumer. 12 | public struct AtomicSPSCQueue: AtomicQueueProtocol { 13 | @usableFromInline let _buffer: _AtomicBuffer 14 | 15 | @inlinable 16 | public init(capacity: Int) { 17 | _buffer = .create(capacity: capacity) 18 | } 19 | 20 | @inlinable 21 | public var capacity: Int { 22 | _buffer._capacity 23 | } 24 | 25 | @inlinable 26 | public func tryPush(_ element: Element) -> Bool { 27 | return _buffer._tryPush(element) 28 | } 29 | 30 | @inlinable 31 | public func pop() -> Element? { 32 | return _buffer._pop() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/FuturesSync/AtomicQueue/AtomicUnboundedMPSCQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AtomicUnboundedMPSCQueue.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesPrivate 9 | 10 | /// A FIFO queue that is safe to share among multiple producers and a single 11 | /// consumer. 12 | public final class AtomicUnboundedMPSCQueue: AtomicUnboundedQueueProtocol { 13 | // This is an implementation of "non-intrusive MPSC queue" from 1024cores.net. 14 | 15 | public typealias Element = T 16 | 17 | @usableFromInline typealias AtomicNode = AtomicReference<_Node> 18 | 19 | @usableFromInline 20 | final class _Node { 21 | @usableFromInline var _next: AtomicNode.RawValue = 0 22 | @usableFromInline var _value: T? 23 | 24 | @inlinable 25 | init(_ value: T?) { 26 | AtomicNode.initialize(&_next, to: nil) 27 | _value = value 28 | } 29 | 30 | @inlinable 31 | deinit { 32 | AtomicNode.destroy(&_next) 33 | } 34 | } 35 | 36 | @usableFromInline var _head: AtomicNode.RawValue = 0 // producers 37 | @usableFromInline var _tail: _Node // consumer 38 | 39 | @inlinable 40 | public init() { 41 | let empty = _Node(nil) 42 | AtomicNode.initialize(&_head, to: empty) 43 | _tail = empty 44 | } 45 | 46 | @inlinable 47 | deinit { 48 | var cur: Optional = _tail 49 | while let current = cur { 50 | cur = AtomicNode.exchange(¤t._next, nil) 51 | } 52 | AtomicNode.destroy(&_head) 53 | } 54 | 55 | @inlinable 56 | public var isEmpty: Bool { 57 | return AtomicNode.load(&_head, order: .relaxed) === _tail 58 | } 59 | 60 | @inlinable 61 | public func push(_ value: T) { 62 | let node = _Node(value) 63 | if let prev = AtomicNode.exchange(&_head, node, order: .acqrel) { 64 | AtomicNode.store(&prev._next, node, order: .release) 65 | } else { 66 | fatalError("unreachable") 67 | } 68 | } 69 | 70 | @inlinable 71 | public func pop() -> Element? { 72 | var backoff = Backoff() 73 | let tail = _tail 74 | while true { 75 | if let next = AtomicNode.load(&tail._next, order: .acquire) { 76 | _tail = next 77 | assert(tail._value == nil) 78 | assert(next._value != nil) 79 | tail._value = next._value.move() 80 | return tail._value 81 | } 82 | if AtomicNode.load(&_head, order: .acquire) === tail { 83 | return nil // empty 84 | } 85 | // the queue is in an inconsistent state. spin a little expecting 86 | // push will soon complete and we'll be able to pop an item out. 87 | backoff.snooze() 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/FuturesSync/Backoff.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Backoff.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | // Ported over from crossbeam: https://github.com/crossbeam-rs/crossbeam 9 | 10 | @usableFromInline let _MAX_SPINS: UInt = 6 // 2^6 = 64 11 | @usableFromInline let _MAX_YIELDS: UInt = 10 // 2^10 = 1024 12 | 13 | /// Helper for implementing spin loops. 14 | /// 15 | /// An example of a busy-wait loop. The current thread will efficiently spin, 16 | /// yielding control at appropriate times, until `ready` becomes `true`. 17 | /// 18 | /// func waitUntil(_ ready: AtomicBool) { 19 | /// var backoff = Backoff() 20 | /// while !ready.load() { 21 | /// backoff.snooze() 22 | /// } 23 | /// } 24 | /// 25 | /// let ready = AtomicBool(false) 26 | /// DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) { 27 | /// ready.store(true) 28 | /// } 29 | /// 30 | /// assert(ready.load() == false) 31 | /// waitUntil(ready) 32 | /// assert(ready.load() == true) 33 | /// 34 | /// An example of retrying an operation until it succeeds. 35 | /// 36 | /// extension AtomicInt { 37 | /// func fetchMul(by b: Int) -> Int { 38 | /// var backoff = Backoff() 39 | /// while true { 40 | /// let value = self.load() 41 | /// if self.compareExchangeWeak(value, value * b) == value { 42 | /// return value 43 | /// } 44 | /// backoff.snooze() 45 | /// } 46 | /// } 47 | /// } 48 | /// 49 | /// let a = AtomicInt(6) 50 | /// assert(a.fetchMul(by: 7) == 6) 51 | /// assert(a.load() == 42) 52 | /// 53 | public struct Backoff { 54 | @usableFromInline var _step: UInt = 0 55 | 56 | @inlinable 57 | public init() {} 58 | 59 | /// A boolean denoting whether backoff completed and it is no longer 60 | /// useful to spin, indicating contention. 61 | /// 62 | /// If `isComplete` is `true`, the caller should arrange for getting 63 | /// notified when the condition the loop waits on is satisfied and park 64 | /// the current thread. 65 | @inlinable 66 | public var isComplete: Bool { 67 | _step > _MAX_YIELDS 68 | } 69 | 70 | @inlinable 71 | public mutating func snooze() { 72 | if _step <= _MAX_SPINS { 73 | for _ in 0..<(1 << _step) { 74 | Atomic.hardwarePause() 75 | } 76 | } else { 77 | Atomic.preemptionYield(UInt64(_step)) 78 | } 79 | if _step <= _MAX_YIELDS { 80 | _step += 1 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Sources/FuturesSync/Locking.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Locking.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | public protocol LockingProtocol: AnyObject { 9 | /// Attempts to acquire the lock without blocking a thread's execution 10 | /// and returns a Boolean value that indicates whether the attempt was 11 | /// successful. 12 | func tryAcquire() -> Bool 13 | 14 | /// Attempts to acquire the lock, blocking a thread's execution until 15 | /// the lock can be acquired. 16 | func acquire() 17 | 18 | /// Relinquishes a previously acquired lock. 19 | func release() 20 | } 21 | 22 | extension LockingProtocol { 23 | @inlinable 24 | @inline(__always) 25 | public func sync(_ fn: () throws -> R) rethrows -> R { 26 | acquire() 27 | defer { release() } 28 | return try fn() 29 | } 30 | 31 | @inlinable 32 | @inline(__always) 33 | public func trySync(_ fn: () throws -> R) rethrows -> R? { 34 | guard tryAcquire() else { return nil } 35 | defer { release() } 36 | return try fn() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/FuturesSync/Locking/PosixLock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PosixLock.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | #if canImport(Darwin) 9 | import Darwin.POSIX.pthread 10 | #else 11 | import Glibc 12 | #endif 13 | 14 | private func _makeMutexAttributes(type: CInt) -> pthread_mutexattr_t { 15 | var rc: CInt 16 | var attr = pthread_mutexattr_t() 17 | rc = pthread_mutexattr_init(&attr) 18 | precondition(rc == 0) 19 | rc = pthread_mutexattr_settype(&attr, type) 20 | precondition(rc == 0) 21 | return attr 22 | } 23 | 24 | @usableFromInline internal let normalMutexAttributes = _makeMutexAttributes( 25 | type: CInt(PTHREAD_MUTEX_ERRORCHECK) 26 | ) 27 | 28 | @usableFromInline internal let recursiveMutexAttributes = _makeMutexAttributes( 29 | type: CInt(PTHREAD_MUTEX_RECURSIVE) 30 | ) 31 | 32 | /// A mutually exclusive (or mutex) lock. 33 | /// 34 | /// A mutex is a type of semaphore that grants access to only one thread at a 35 | /// time. If a mutex is in use and another thread tries to acquire it, that 36 | /// thread blocks until the mutex is released by its original holder. If 37 | /// multiple threads compete for the same mutex, only one at a time is allowed 38 | /// access to it. 39 | /// 40 | /// You instantiate a re-entrant variant by passing `true` to `init(recursive:)`. 41 | /// A recursive lock may be acquired multiple times by the same thread without 42 | /// causing a deadlock, a situation where a thread is permanently blocked waiting 43 | /// for itself to relinquish a lock. While the locking thread has one or more 44 | /// locks, all other threads are prevented from accessing the code protected by 45 | /// the lock. 46 | /// 47 | /// Both types of locks check for usage errors. See `PTHREAD_MUTEX_ERRORCHECK` 48 | /// and `PTHREAD_MUTEX_RECURSIVE` in `man 3 pthread_mutexattr_settype`. 49 | /// 50 | /// Backed by `pthread_mutex`. 51 | public final class PosixLock: LockingProtocol { 52 | @usableFromInline var _mutex = pthread_mutex_t() 53 | 54 | @inlinable 55 | public init(recursive: Bool = false) { 56 | var attr = recursive 57 | ? recursiveMutexAttributes 58 | : normalMutexAttributes 59 | let rc = pthread_mutex_init(&_mutex, &attr) 60 | precondition(rc == 0) 61 | } 62 | 63 | @inlinable 64 | deinit { 65 | let rc = pthread_mutex_destroy(&_mutex) 66 | precondition(rc == 0) 67 | } 68 | 69 | @inlinable 70 | public func tryAcquire() -> Bool { 71 | return pthread_mutex_trylock(&_mutex) == 0 72 | } 73 | 74 | @inlinable 75 | public func acquire() { 76 | let rc = pthread_mutex_lock(&_mutex) 77 | precondition(rc == 0) 78 | } 79 | 80 | @inlinable 81 | public func release() { 82 | let rc = pthread_mutex_unlock(&_mutex) 83 | precondition(rc == 0) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Sources/FuturesSync/Locking/SpinLock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpinLock.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | // Prefer UnfairLock over this guy on Darwin; 9 | // it's equally performant but safer. 10 | public final class SpinLock: LockingProtocol { 11 | @usableFromInline var _flag: AtomicBool.RawValue = false 12 | 13 | @inlinable 14 | public init() { 15 | AtomicBool.initialize(&_flag, to: false) 16 | } 17 | 18 | @inlinable 19 | public func tryAcquire() -> Bool { 20 | return !AtomicBool.exchange(&_flag, true, order: .acquire) 21 | } 22 | 23 | @inlinable 24 | public func acquire() { 25 | var backoff = Backoff() 26 | while AtomicBool.compareExchangeWeak(&_flag, false, true, order: .acquire) { 27 | backoff.snooze() 28 | } 29 | } 30 | 31 | @inlinable 32 | public func release() { 33 | AtomicBool.store(&_flag, false, order: .release) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/FuturesSync/Locking/UnfairLock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UnfairLock.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | #if !canImport(os) 9 | /// :nodoc: 10 | public typealias UnfairLock = SpinLock 11 | #else 12 | 13 | import os 14 | 15 | /// Low-level lock that allows waiters to block efficiently on contention. 16 | /// 17 | /// There is no attempt at fairness or lock ordering, e.g. an unlocker can 18 | /// potentially immediately reacquire the lock before a woken up waiter gets 19 | /// an opportunity to attempt to acquire the lock. This may be advantageous 20 | /// for performance reasons, but also makes starvation of waiters a possibility. 21 | /// 22 | /// Backed by `os_unfair_lock`. 23 | public final class UnfairLock: LockingProtocol { 24 | @usableFromInline var _lock = os_unfair_lock_s() 25 | 26 | @inlinable 27 | public init() {} 28 | 29 | @inlinable 30 | public func tryAcquire() -> Bool { 31 | return os_unfair_lock_trylock(&_lock) 32 | } 33 | 34 | @inlinable 35 | public func acquire() { 36 | os_unfair_lock_lock(&_lock) 37 | } 38 | 39 | @inlinable 40 | public func release() { 41 | os_unfair_lock_unlock(&_lock) 42 | } 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /Sources/FuturesSync/Mutex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mutex.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | public final class Mutex { 9 | @usableFromInline let _lock: Lock 10 | @usableFromInline var _value: Value 11 | 12 | @inlinable 13 | public init(_ value: Value, lock: Lock) { 14 | _value = value 15 | _lock = lock 16 | } 17 | 18 | @inlinable 19 | public var value: Value { 20 | _read { 21 | _lock.acquire() 22 | defer { _lock.release() } 23 | yield _value 24 | } 25 | _modify { 26 | _lock.acquire() 27 | defer { _lock.release() } 28 | yield &_value 29 | } 30 | } 31 | 32 | @inlinable 33 | @inline(__always) 34 | public func withMutableValue(_ fn: (inout Value) throws -> R) rethrows -> R { 35 | return try _lock.sync { 36 | try fn(&_value) 37 | } 38 | } 39 | 40 | @inlinable 41 | public func load() -> Value { 42 | return withMutableValue { $0 } 43 | } 44 | 45 | @inlinable 46 | public func store(_ desired: Value) { 47 | withMutableValue { $0 = desired } 48 | } 49 | 50 | @inlinable 51 | @discardableResult 52 | public func exchange(_ desired: Value) -> Value { 53 | return withMutableValue { 54 | let current = $0 55 | $0 = desired 56 | return current 57 | } 58 | } 59 | 60 | @inlinable 61 | public func move() -> Wrapped? where Value == Wrapped? { 62 | return withMutableValue { 63 | let current = $0 64 | $0 = nil 65 | return current 66 | } 67 | } 68 | } 69 | 70 | extension Mutex where Value: Equatable { 71 | @inlinable 72 | @discardableResult 73 | public func compareExchange(_ expected: Value, _ desired: Value) -> Value? { 74 | return withMutableValue { 75 | let current = $0 76 | if current == expected { 77 | $0 = desired 78 | return nil 79 | } 80 | return current 81 | } 82 | } 83 | } 84 | 85 | extension Mutex where Lock == UnfairLock { 86 | @inlinable 87 | public convenience init(_ value: Value) { 88 | self.init(value, lock: .init()) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/FuturesSync/Private.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Private.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | @inlinable 9 | @inline(__always) 10 | func isPowerOf2(_ n: Int) -> Bool { 11 | return UInt32(n).nonzeroBitCount == 1 12 | } 13 | 14 | extension Optional { 15 | @inlinable 16 | @_transparent 17 | @discardableResult 18 | mutating func move() -> Wrapped? { 19 | var value: Wrapped? 20 | Swift.swap(&self, &value) 21 | return value 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/FuturesTestSupport/Future.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Future.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import Dispatch 9 | import Foundation 10 | import Futures 11 | import FuturesSync 12 | 13 | public func pending() -> AnyFuture { 14 | return .init { _ in 15 | .pending 16 | } 17 | } 18 | 19 | public func `lazy`(_ fn: @escaping () -> T) -> AnyFuture { 20 | return .init { _ in 21 | .ready(fn()) 22 | } 23 | } 24 | 25 | public func delayed(by: TimeInterval, _ fn: @escaping () -> T) -> AnyFuture { 26 | let flag = AtomicBool(false) 27 | return .init { context in 28 | if !flag.load(order: .acquire) { 29 | let waker = context.waker 30 | DispatchQueue.global().asyncAfter(deadline: .now() + by) { 31 | flag.store(true, order: .release) 32 | waker.signal() 33 | } 34 | if !flag.load(order: .acquire) { 35 | return .pending 36 | } 37 | } 38 | return .ready(fn()) 39 | } 40 | } 41 | 42 | public struct TestFuture: FutureProtocol { 43 | private var _output: Output? 44 | 45 | public init(output: Output) { 46 | _output = output 47 | } 48 | 49 | public mutating func poll(_ context: inout Context) -> Poll { 50 | if let output = _output { 51 | _output = nil 52 | return .ready(output) 53 | } 54 | return context.yield() 55 | } 56 | } 57 | 58 | public func makeFuture(_ output: T) -> TestFuture { 59 | return .init(output: output) 60 | } 61 | -------------------------------------------------------------------------------- /Sources/FuturesTestSupport/Module.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Module.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import Foundation 9 | 10 | public let DONE: Void = () 11 | 12 | public let CPU_COUNT = ProcessInfo.processInfo.processorCount 13 | 14 | private var rng = SystemRandomNumberGenerator() 15 | 16 | public func randomInteger(ofType type: T.Type = T.self) -> T { 17 | var t = type.init() 18 | for _ in 0...((t.bitWidth - 1) / 32) { 19 | t = t << 32 &+ type.init(truncatingIfNeeded: rng.next()) 20 | } 21 | return (t | 1) & (type.max >> 1) 22 | } 23 | 24 | public struct AnyError: Error { 25 | public let error: Error 26 | 27 | public init(_ error: Error) { 28 | if let anyError = error as? AnyError { 29 | self = anyError 30 | } else { 31 | self.error = error 32 | } 33 | } 34 | } 35 | 36 | public final class Ref { 37 | public var value: T 38 | 39 | public init(_ initialValue: T) { 40 | value = initialValue 41 | } 42 | } 43 | 44 | extension Swift.Result { 45 | public var value: Success? { 46 | switch self { 47 | case .success(let value): 48 | return value 49 | case .failure: 50 | return nil 51 | } 52 | } 53 | 54 | public var error: Failure? { 55 | switch self { 56 | case .success: 57 | return nil 58 | case .failure(let error): 59 | return error 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/FuturesTestSupport/Stream.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Stream.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import Futures 9 | 10 | public struct TestStream: StreamProtocol { 11 | public typealias Output = S.Element 12 | 13 | private var _iter: EnumeratedSequence.Iterator 14 | private let _yieldOnIndex: Int 15 | private var _lastElement: Output? 16 | 17 | public init(elements: S, yieldOnIndex: Int?) { 18 | _yieldOnIndex = yieldOnIndex ?? Int.random(in: 0.. Poll { 23 | if let output = _lastElement { 24 | _lastElement = nil 25 | return .ready(output) 26 | } 27 | if let (index, output) = _iter.next() { 28 | if index == _yieldOnIndex { 29 | _lastElement = output 30 | return context.yield() 31 | } 32 | return .ready(output) 33 | } 34 | return .ready(nil) 35 | } 36 | } 37 | 38 | public func makeStream(_ elements: S, yieldOnIndex: Int? = nil) -> TestStream { 39 | return .init(elements: elements, yieldOnIndex: yieldOnIndex) 40 | } 41 | -------------------------------------------------------------------------------- /Sources/FuturesTestSupport/TestCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestCase.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import Futures 9 | import XCTest 10 | 11 | extension XCTestCase { 12 | public func expect( 13 | function: StaticString = #function, 14 | count: Int = 1, 15 | description: String? = nil, 16 | timeout: TimeInterval = 1, 17 | enforceOrder enforceOrderOfFulfillment: Bool = true, 18 | execute: ([XCTestExpectation]) throws -> R 19 | ) rethrows -> R { 20 | var exp = [XCTestExpectation]() 21 | for i in 0.. Poll) { 32 | let executor = ThreadExecutor.current 33 | try! executor.submit(AnyFuture(fn)) // swiftlint:disable:this force_try 34 | XCTAssert(executor.run()) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/FuturesTestSupport/TestError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestError.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | public enum TestError: Error, Equatable { 9 | case dummy 10 | case custom(Int) 11 | 12 | public static func == (lhs: TestError, rhs: TestError) -> Bool { 13 | switch (lhs, rhs) { 14 | case (.dummy, .dummy): 15 | return true 16 | case (.custom(let a), .custom(let b)): 17 | return a == b 18 | default: 19 | return false 20 | } 21 | } 22 | 23 | public static func == (lhs: TestError, rhs: Error) -> Bool { 24 | guard let rhs = rhs as? TestError else { 25 | return false 26 | } 27 | return lhs == rhs 28 | } 29 | 30 | public static func == (lhs: Error, rhs: TestError) -> Bool { 31 | guard let lhs = lhs as? TestError else { 32 | return false 33 | } 34 | return lhs == rhs 35 | } 36 | 37 | public static func == (lhs: Error?, rhs: TestError) -> Bool { 38 | guard let lhs = lhs as? TestError else { 39 | return false 40 | } 41 | return lhs == rhs 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/FuturesSyncTests/LockingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LockingTests.swift 3 | // Futures 4 | // 5 | // Copyright © 2019 Akis Kesoglou. Licensed under the MIT license. 6 | // 7 | 8 | import FuturesSync 9 | import FuturesTestSupport 10 | import XCTest 11 | 12 | public final class LockingTests: XCTestCase { 13 | private func lockTest(_ config: (Int, Int), _ lock: LockingProtocol) { 14 | let (partitions, iterations) = (UInt64(config.0), UInt64(config.1)) 15 | let q = DispatchQueue(label: "futures.test-locking", attributes: .concurrent) 16 | let g = DispatchGroup() 17 | var i: UInt64 = 0 18 | for p in 1...partitions { 19 | q.async(group: g, flags: .detached) { 20 | for _ in 0.. Bool { 12 | return n == 2 || n > 2 && (2...(n - 1)).allSatisfy { 13 | !n.isMultiple(of: $0) 14 | } 15 | } 16 | 17 | func isPronic(_ n: Int) -> Bool { 18 | let f = floor(Double(n).squareRoot()) 19 | let c = ceil(Double(n).squareRoot()) 20 | return n == Int(f) * Int(c) 21 | } 22 | 23 | final class ReadmeTests: XCTestCase { 24 | func test42Stream() { 25 | let integers = Stream.sequence(0...) 26 | let primes = integers.filter(isPrime) 27 | 28 | var answer = primes.buffer(4) 29 | .map { $0[0] * $0[1] * $0[3] } 30 | .first(where: isPronic) 31 | 32 | XCTAssertEqual(answer.wait(), 42) 33 | } 34 | 35 | func test42Channel() { 36 | let deepThought = ( 37 | cpu0: QueueExecutor(label: "CPU 0"), 38 | cpu1: QueueExecutor(label: "CPU 1"), 39 | cpu2: QueueExecutor(label: "CPU 2") 40 | ) 41 | 42 | let pipe1 = Channel.makeUnbuffered(itemType: Int.self) 43 | let pipe2 = Channel.makeUnbuffered(itemType: Int.self) 44 | 45 | let integers = Stream.sequence(0...) 46 | deepThought.cpu1.submit(integers.forward(to: pipe1)) 47 | 48 | let primes = pipe1.makeStream().filter(isPrime) 49 | deepThought.cpu2.submit(primes.forward(to: pipe2)) 50 | 51 | var answer = deepThought.cpu0.spawn( 52 | pipe2.makeStream() 53 | .buffer(4) 54 | .map { $0[0] * $0[1] * $0[3] } 55 | .first(where: isPronic) 56 | ) 57 | 58 | XCTAssertEqual(answer.wait(), .success(42)) 59 | } 60 | } 61 | --------------------------------------------------------------------------------