Skip to content

Commit

Permalink
- fix memory leak issue
Browse files Browse the repository at this point in the history
- update tests
  • Loading branch information
Alex Belozierov committed Jan 14, 2020
1 parent 03c03da commit 5b340c5
Show file tree
Hide file tree
Showing 9 changed files with 498 additions and 299 deletions.
27 changes: 16 additions & 11 deletions Sources/SwiftCoroutine/Coroutine/CoroutineContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class CoroutineContext {
let haveGuardPage: Bool
private let stack: UnsafeMutableRawBufferPointer
private let returnPoint, resumePoint: UnsafeMutablePointer<Int32>
private var block: Block?

init(stackSize: Int, guardPage: Bool = true) {
haveGuardPage = guardPage
Expand All @@ -31,23 +32,27 @@ class CoroutineContext {
resumePoint = .allocate(capacity: .environmentSize)
}

// MARK: - Operations
// MARK: - Start

@inlinable func start(block: @escaping Block) -> Bool {
var blockRef: Block! = block
return withUnsafePointer(to: { [unowned(unsafe) self] in
blockRef()
blockRef = nil
longjmp(self.returnPoint, .finished)
}, start)
self.block = block
return __start(returnPoint, stackStart,
Unmanaged.passUnretained(self).toOpaque()) {
longjmp(Unmanaged<CoroutineContext>
.fromOpaque($0!)
.takeUnretainedValue()
.performBlock(), .finished)
} == .finished
}

private func start(with block: UnsafePointer<Block>) -> Bool {
__start(returnPoint, stackStart, block) {
$0?.assumingMemoryBound(to: Block.self).pointee()
} == .finished
private func performBlock() -> UnsafeMutablePointer<Int32> {
block?()
block = nil
return returnPoint
}

// MARK: - Operations

@inlinable func resume() -> Bool {
__save(returnPoint, resumePoint, -1) == .finished
}
Expand Down
32 changes: 32 additions & 0 deletions SwiftCoroutine.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
1A43084F23C111C8001A89EA /* CoCancellable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A43084D23C111C8001A89EA /* CoCancellable.swift */; };
1A43085123C35DDA001A89EA /* CoHandleFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A43085023C35DDA001A89EA /* CoHandleFuture.swift */; };
1A43085223C35DDA001A89EA /* CoHandleFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A43085023C35DDA001A89EA /* CoHandleFuture.swift */; };
1A57BE5723CC73B400639FDD /* CoFututreWaitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A57BE5623CC73B400639FDD /* CoFututreWaitTests.swift */; };
1A57BE5823CC73B400639FDD /* CoFututreWaitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A57BE5623CC73B400639FDD /* CoFututreWaitTests.swift */; };
1A57BE5A23CC7B1100639FDD /* CoFutureAwaitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A57BE5923CC7B1100639FDD /* CoFutureAwaitTests.swift */; };
1A57BE5B23CC7B1100639FDD /* CoFutureAwaitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A57BE5923CC7B1100639FDD /* CoFutureAwaitTests.swift */; };
1A57BE5D23CC82F000639FDD /* CoFututeCompositeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A57BE5C23CC82F000639FDD /* CoFututeCompositeTests.swift */; };
1A57BE5E23CC82F000639FDD /* CoFututeCompositeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A57BE5C23CC82F000639FDD /* CoFututeCompositeTests.swift */; };
1A57BE6523CC867600639FDD /* CoroutineContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A57BE6423CC867600639FDD /* CoroutineContextTests.swift */; };
1A57BE6623CC867600639FDD /* CoroutineContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A57BE6423CC867600639FDD /* CoroutineContextTests.swift */; };
1A61C02E23BB985B007F488C /* CoTransformFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A61C02D23BB985B007F488C /* CoTransformFuture.swift */; };
1A61C02F23BB985B007F488C /* CoTransformFuture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A61C02D23BB985B007F488C /* CoTransformFuture.swift */; };
1A61C03323BB9926007F488C /* CoSubject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A61C03223BB9926007F488C /* CoSubject.swift */; };
Expand Down Expand Up @@ -135,6 +143,10 @@
1A43084723C0955A001A89EA /* Coroutine+Delay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Coroutine+Delay.swift"; sourceTree = "<group>"; };
1A43084D23C111C8001A89EA /* CoCancellable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoCancellable.swift; sourceTree = "<group>"; };
1A43085023C35DDA001A89EA /* CoHandleFuture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoHandleFuture.swift; sourceTree = "<group>"; };
1A57BE5623CC73B400639FDD /* CoFututreWaitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoFututreWaitTests.swift; sourceTree = "<group>"; };
1A57BE5923CC7B1100639FDD /* CoFutureAwaitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoFutureAwaitTests.swift; sourceTree = "<group>"; };
1A57BE5C23CC82F000639FDD /* CoFututeCompositeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoFututeCompositeTests.swift; sourceTree = "<group>"; };
1A57BE6423CC867600639FDD /* CoroutineContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoroutineContextTests.swift; sourceTree = "<group>"; };
1A61C02D23BB985B007F488C /* CoTransformFuture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoTransformFuture.swift; sourceTree = "<group>"; };
1A61C03223BB9926007F488C /* CoSubject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoSubject.swift; sourceTree = "<group>"; };
1A65BABA239CE05C004C1716 /* CoroutineContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoroutineContext.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -236,6 +248,14 @@
path = Helpers;
sourceTree = "<group>";
};
1A57BE6323CC865800639FDD /* Coroutine */ = {
isa = PBXGroup;
children = (
1A57BE6423CC867600639FDD /* CoroutineContextTests.swift */,
);
path = Coroutine;
sourceTree = "<group>";
};
1A621AE923BF5F730043077D /* Protocols */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -336,6 +356,9 @@
1AFFF6F623C909C40079FF73 /* CoTransformFutureTests.swift */,
1AFFF6F923C9E74C0079FF73 /* CoHandleFutureTests.swift */,
1AEC381F23CA01320000101A /* CoFutureOperatorsTests.swift */,
1A57BE5623CC73B400639FDD /* CoFututreWaitTests.swift */,
1A57BE5923CC7B1100639FDD /* CoFutureAwaitTests.swift */,
1A57BE5C23CC82F000639FDD /* CoFututeCompositeTests.swift */,
);
path = CoFutureTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -367,6 +390,7 @@
F810EF0B1F8F302700025AD1 /* SwiftCoroutineTests */ = {
isa = PBXGroup;
children = (
1A57BE6323CC865800639FDD /* Coroutine */,
1AFFF6EF23C900120079FF73 /* CoFutureTests */,
1A934FB6239029A300155B71 /* Extensions */,
F810EF0E1F8F302700025AD1 /* Info.plist */,
Expand Down Expand Up @@ -655,16 +679,20 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1A57BE5D23CC82F000639FDD /* CoFututeCompositeTests.swift in Sources */,
1AFFF6FA23C9E74D0079FF73 /* CoHandleFutureTests.swift in Sources */,
1A57BE5723CC73B400639FDD /* CoFututreWaitTests.swift in Sources */,
1A934FC4239031C000155B71 /* XCTAssertContains.swift in Sources */,
1AFFF6F123C9002C0079FF73 /* CoFutureTests.swift in Sources */,
1A739A7623ACE66B00165280 /* CoFutureTests2.swift in Sources */,
1A57BE6523CC867600639FDD /* CoroutineContextTests.swift in Sources */,
1AFFF6F723C909C40079FF73 /* CoTransformFutureTests.swift in Sources */,
1AEC382023CA01320000101A /* CoFutureOperatorsTests.swift in Sources */,
1AEC382323CA02C90000101A /* XCTOrderedExpectation.swift in Sources */,
1AFFF6F423C907280079FF73 /* CoPromiseTests.swift in Sources */,
F8CD25F92019A19A00952299 /* SwiftCoroutineTests.swift in Sources */,
1A934FBB23902A5300155B71 /* URL+Extensions.swift in Sources */,
1A57BE5A23CC7B1100639FDD /* CoFutureAwaitTests.swift in Sources */,
1A934FB4239028E900155B71 /* CombineTests.swift in Sources */,
1A934FB8239029B700155B71 /* URLSession+Extensions.swift in Sources */,
);
Expand Down Expand Up @@ -717,16 +745,20 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1A57BE5E23CC82F000639FDD /* CoFututeCompositeTests.swift in Sources */,
1AFFF6FB23C9E74D0079FF73 /* CoHandleFutureTests.swift in Sources */,
1A57BE5823CC73B400639FDD /* CoFututreWaitTests.swift in Sources */,
1A934FC5239031C000155B71 /* XCTAssertContains.swift in Sources */,
1AFFF6F223C9002C0079FF73 /* CoFutureTests.swift in Sources */,
1A739A7723ACE66C00165280 /* CoFutureTests2.swift in Sources */,
1A57BE6623CC867600639FDD /* CoroutineContextTests.swift in Sources */,
1AFFF6F823C909C40079FF73 /* CoTransformFutureTests.swift in Sources */,
1AEC382123CA01320000101A /* CoFutureOperatorsTests.swift in Sources */,
1AEC382423CA02C90000101A /* XCTOrderedExpectation.swift in Sources */,
1AFFF6F523C907280079FF73 /* CoPromiseTests.swift in Sources */,
1A934FBC23902A5300155B71 /* URL+Extensions.swift in Sources */,
1A94E5C223A3E6A300812AFD /* SwiftCoroutineTests.swift in Sources */,
1A57BE5B23CC7B1100639FDD /* CoFutureAwaitTests.swift in Sources */,
1A94E5C323A3E6A600812AFD /* CombineTests.swift in Sources */,
1A934FB9239029B700155B71 /* URLSession+Extensions.swift in Sources */,
);
Expand Down
68 changes: 68 additions & 0 deletions Tests/SwiftCoroutineTests/CoFutureTests/CoFutureAwaitTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// CoFutureAwaitTests.swift
// SwiftCoroutine
//
// Created by Alex Belozierov on 13.01.2020.
// Copyright © 2020 Alex Belozierov. All rights reserved.
//

import XCTest
@testable import SwiftCoroutine

class CoFutureAwaitTests: XCTestCase {

func testOutCoroutineCall() {
let promise = CoPromise<Int>()
do {
_ = try promise.await()
XCTFail()
} catch let error as Coroutine.CoroutineError {
return XCTAssertEqual(error, .mustBeCalledInsideCoroutine)
} catch {
XCTFail()
}
}

func testAwait() {
testAwait(sec: 1) { promise in
XCTAssertEqual(try promise.await(), 1)
}
}

func testAwaitResult() {
testAwait(sec: 1) { promise in
XCTAssertEqual(promise.awaitResult(), 1)
}
}

func testAwaitTimeout() {
testAwait(sec: 1) { promise in
XCTAssertEqual(try promise.await(timeout: .now() + 2), 1)
}
}

func testAwaitTimeout2() {
testAwait(sec: 2) { promise in
do {
_ = try promise.await(timeout: .now() + 1)
} catch let error as CoFutureError {
return XCTAssertEqual(error, .timeout)
}
XCTFail()
}
}

private func testAwait(sec: Int, test: @escaping (CoPromise<Int>) throws -> Void) {
let exp = expectation(description: "testAwaitTimeout")
let promise = CoPromise<Int>()
DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(sec)) {
promise.send(1)
}
coroutine {
try test(promise)
exp.fulfill()
}
wait(for: [exp], timeout: 2)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// CoFututeCompositeTests.swift
// SwiftCoroutine
//
// Created by Alex Belozierov on 13.01.2020.
// Copyright © 2020 Alex Belozierov. All rights reserved.
//

import XCTest
@testable import SwiftCoroutine

class CoFututeCompositeTests: XCTestCase {

func testComposite() {
func test<T>(futures: [CoFuture<T>],
@CoFututeComposite<T> builder: @escaping () -> [CoFuture<T>]) {
XCTAssertEqual(futures, builder())
}
let promises = (0..<3).map { _ in CoPromise<Int>() }
test(futures: promises) {
promises[0]
promises[1]
promises[2]
}
}

}
49 changes: 49 additions & 0 deletions Tests/SwiftCoroutineTests/CoFutureTests/CoFututreWaitTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// CoFututreWaitTests.swift
// SwiftCoroutine
//
// Created by Alex Belozierov on 13.01.2020.
// Copyright © 2020 Alex Belozierov. All rights reserved.
//

import XCTest
@testable import SwiftCoroutine

class CoFututreWaitTests: XCTestCase {

func testWait() {
testWait(sec: 1) { promise in
XCTAssertEqual(try? promise.wait(), 1)
}
}

func testWaitTimeout() {
testWait(sec: 1) { promise in
XCTAssertEqual(try? promise.wait(timeout: .now() + 2), 1)
}
}

func testWaitTimeout2() {
testWait(sec: 2) { promise in
do {
_ = try promise.wait(timeout: .now() + 1)
} catch let error as CoFutureError {
return XCTAssertEqual(error, .timeout)
}
XCTFail()
}
}

private func testWait(sec: Int, test: @escaping (CoPromise<Int>) throws -> Void) {
let promise = CoPromise<Int>()
DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(sec)) {
promise.send(1)
}
do {
try test(promise)
} catch {
XCTFail()
}
}

}
68 changes: 34 additions & 34 deletions Tests/SwiftCoroutineTests/CoFutureTests2.swift
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
////
//// CoFutureTests.swift
//// SwiftCoroutine
////
//// Created by Alex Belozierov on 20.12.2019.
//// Copyright © 2019 Alex Belozierov. All rights reserved.
////
//
// CoFutureTests.swift
// SwiftCoroutine
//import XCTest
//import SwiftCoroutine
//
// Created by Alex Belozierov on 20.12.2019.
// Copyright © 2019 Alex Belozierov. All rights reserved.
//

import XCTest
import SwiftCoroutine

class CoFutureTests2: XCTestCase {

func testTransform() {
let expectation = XCTestExpectation(description: "Test Transform")
expectation.expectedFulfillmentCount = 3
let promise = async { () -> Int in
sleep(1)
return 1
}
let transformed = promise
.transformOutput { $0 * 2 }
.onError { _ in XCTFail() }
.transformOutput { $0 * 3 }
.onSuccess { XCTAssertEqual($0, 6) }
.onSuccess { _ in expectation.fulfill() }
.transformOutput { $0.description }
.onCompletion { expectation.fulfill() }
transformed.onSuccess(on: .global) {
XCTAssertEqual($0, "6")
expectation.fulfill()
}
wait(for: [expectation], timeout: 5)
}

}
//class CoFutureTests2: XCTestCase {
//
// func testTransform() {
// let expectation = XCTestExpectation(description: "Test Transform")
// expectation.expectedFulfillmentCount = 3
// let promise = async { () -> Int in
// sleep(1)
// return 1
// }
// let transformed = promise
// .transformOutput { $0 * 2 }
// .onError { _ in XCTFail() }
// .transformOutput { $0 * 3 }
// .onSuccess { XCTAssertEqual($0, 6) }
// .onSuccess { _ in expectation.fulfill() }
// .transformOutput { $0.description }
// .onCompletion { expectation.fulfill() }
// transformed.onSuccess(on: .global) {
// XCTAssertEqual($0, "6")
// expectation.fulfill()
// }
// wait(for: [expectation], timeout: 5)
// }
//
//}
Loading

0 comments on commit 5b340c5

Please sign in to comment.