Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.3.0 #20

Merged
merged 10 commits into from
Feb 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CommonInternal/AtomicBool.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Foundation

final class AtomicBool: ExpressibleByBooleanLiteral {
private let rawValue = UnsafeMutablePointer<Int32>.allocate(capacity: 1)

var value: Bool {
return rawValue.pointee == true.int32Value
}

init(booleanLiteral value: Bool) {
rawValue.initialize(to: value.int32Value)
}

deinit {
rawValue.deinitialize()
rawValue.deallocate(capacity: 1)
}

func compareAndSwapBarrier(old: Bool, new: Bool) -> Bool {
return OSAtomicCompareAndSwap32Barrier(old.int32Value, new.int32Value, rawValue)
}
}

private extension Bool {
var int32Value: Int32 {
return self ? 1 : 0
}
}
90 changes: 90 additions & 0 deletions CommonInternal/Lock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import Foundation

/// Coordinates the operation of multiple threads of execution.
struct Lock {
@available(iOS 10.0, *)
@available(macOS 10.12, *)
@available(tvOS 10.0, *)
@available(watchOS 3.0, *)
private final class OSUnfairLock: NSLocking {
private let _lock = os_unfair_lock_t.allocate(capacity: 1)

init() {
_lock.initialize(to: os_unfair_lock())
}

deinit {
_lock.deinitialize()
_lock.deallocate(capacity: 1)
}

func lock() {
os_unfair_lock_lock(_lock)
}

func unlock() {
os_unfair_lock_unlock(_lock)
}
}

private final class PosixThreadMutex: NSLocking {
private let _lock = UnsafeMutablePointer<pthread_mutex_t>.allocate(capacity: 1)

init(recursive: Bool = false) {
_lock.initialize(to: pthread_mutex_t())

if recursive {
let attributes = UnsafeMutablePointer<pthread_mutexattr_t>.allocate(capacity: 1)
attributes.initialize(to: pthread_mutexattr_t())
pthread_mutexattr_init(attributes)
pthread_mutexattr_settype(attributes, Int32(PTHREAD_MUTEX_RECURSIVE))
pthread_mutex_init(_lock, attributes)

pthread_mutexattr_destroy(attributes)
attributes.deinitialize()
attributes.deallocate(capacity: 1)
} else {
pthread_mutex_init(_lock, nil)
}
}

deinit {
pthread_mutex_destroy(_lock)
_lock.deinitialize()
_lock.deallocate(capacity: 1)
}

func lock() {
pthread_mutex_lock(_lock)
}

func unlock() {
pthread_mutex_unlock(_lock)
}
}

private let inner: NSLocking

/// Attempts to acquire a lock, blocking a thread’s execution until the lock can be acquired.
func lock() {
inner.lock()
}

/// Relinquishes a previously acquired lock.
func unlock() {
inner.unlock()
}

/// Create a lock.
///
/// - Parameters:
/// - recursive: A Bool value indicating whether locking is recursive.
/// - usePosixThreadMutexForced: Force to use Posix thread mutex.
init(recursive: Bool, usePosixThreadMutexForced: Bool = false) {
if #available(*, iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0), !usePosixThreadMutexForced, !recursive {
inner = OSUnfairLock()
} else {
inner = PosixThreadMutex(recursive: recursive)
}
}
}
20 changes: 10 additions & 10 deletions VueFlux/Storage.swift → CommonInternal/Storage.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/// Collection of values of type `Element` that to be able to remove value by key.
public struct Storage<Element> {
struct Storage<Element> {
private var elements = ContiguousArray<Element>()
private var keyRawValues = ContiguousArray<Key.RawValue>()
private var nextKey = Key.first

/// Create the new, empty storage.
public init() {}
init() {}

/// Add a new element.
///
Expand All @@ -14,7 +14,7 @@ public struct Storage<Element> {
///
/// - Returns: A key for remove given element.
@discardableResult
public mutating func add(_ element: Element) -> Key {
mutating func add(_ element: Element) -> Key {
let key = nextKey
nextKey = key.next

Expand All @@ -31,7 +31,7 @@ public struct Storage<Element> {
///
/// - Returns: A removed element.
@discardableResult
public mutating func remove(for key: Key) -> Element? {
mutating func remove(for key: Key) -> Element? {
guard let index = indices.first(where: { keyRawValues[$0] == key.rawValue }) else { return nil }

keyRawValues.remove(at: index)
Expand All @@ -40,26 +40,26 @@ public struct Storage<Element> {
}

extension Storage: RandomAccessCollection {
public var startIndex: Int {
var startIndex: Int {
return elements.startIndex
}

public var endIndex: Int {
var endIndex: Int {
return elements.endIndex
}

public subscript(index: Int) -> Element {
subscript(index: Int) -> Element {
return elements[index]
}

public func makeIterator() -> IndexingIterator<ContiguousArray<Element>> {
func makeIterator() -> IndexingIterator<ContiguousArray<Element>> {
return elements.makeIterator()
}
}

public extension Storage {
extension Storage {
/// An unique key for remove element.
public struct Key {
struct Key {
fileprivate typealias RawValue = UInt64

fileprivate let rawValue: RawValue
Expand Down
File renamed without changes.
35 changes: 35 additions & 0 deletions Tests/AtomicBoolTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import XCTest
@testable import VueFlux
@testable import VueFluxReactive

protocol AtomicBoolProtocol: ExpressibleByBooleanLiteral {
var value: Bool { get }

func compareAndSwapBarrier(old: Bool, new: Bool) -> Bool
}

extension VueFlux.AtomicBool: AtomicBoolProtocol {}
extension VueFluxReactive.AtomicBool: AtomicBoolProtocol {}

final class AtomicBoolTests: XCTestCase {
func testAtomicBool() {
func runTest<AtomicBool: AtomicBoolProtocol>(for type: AtomicBool.Type) {
let atomicBool: AtomicBool = true

XCTAssertTrue(atomicBool.value)

let result1 = atomicBool.compareAndSwapBarrier(old: true, new: false)

XCTAssertFalse(atomicBool.value)
XCTAssertTrue(result1)

let result2 = atomicBool.compareAndSwapBarrier(old: true, new: false)

XCTAssertFalse(atomicBool.value)
XCTAssertFalse(result2)
}

runTest(for: VueFlux.AtomicBool.self)
runTest(for: VueFluxReactive.AtomicBool.self)
}
}
21 changes: 0 additions & 21 deletions Tests/AtomicReferenceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,25 +124,4 @@ final class AtomicReferenceTests: XCTestCase {
_ = group.wait(timeout: .now() + 10)
XCTAssertEqual(atomicReference.value, 0)
}

func testPosixThreadMutex() {
let atomicReference = AtomicReference(0, usePosixThreadMutexForced: true, recursive: false)

let queue = DispatchQueue(label: "testPosixThreadMutex", attributes: .concurrent)
let group = DispatchGroup()

for _ in (1...100) {
queue.async(group: group) {
atomicReference.modify { value in
value += 1
value -= 1
value += 2
value -= 2
}
}
}

_ = group.wait(timeout: .now() + 10)
XCTAssertEqual(atomicReference.value, 0)
}
}
2 changes: 2 additions & 0 deletions Tests/ExecutorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ final class ExecutorTests: XCTestCase {

queue.resume()

workItem.cancel()

queue.async(execute: expectation.fulfill)

waitForExpectations(timeout: 1) { _ in
Expand Down
63 changes: 63 additions & 0 deletions Tests/LockTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import XCTest
@testable import VueFlux
@testable import VueFluxReactive

protocol LockProtocol {
func lock()
func unlock()
}

extension VueFlux.Lock: LockProtocol {}
extension VueFluxReactive.Lock: LockProtocol {}

final class LockTests: XCTestCase {
func testLock() {
func runTest(synchronized: @escaping (() -> Void) -> Void) {
let queue = DispatchQueue(label: "testLock", attributes: .concurrent)
let group = DispatchGroup()

var value = 0
for _ in (1...100) {
queue.async(group: group) {
synchronized {
value += 1
value -= 1
value += 2
value -= 2
}
}
}

_ = group.wait(timeout: .now() + 10)
XCTAssertEqual(value, 0)
}

func runNonRecursiveTest<Lock: LockProtocol>(for lock: Lock) {
runTest { criticalSection in
lock.lock()
criticalSection()
lock.unlock()
}
}

func runRecursiveTest<Lock: LockProtocol>(for lock: Lock) {
runTest { criticalSection in
lock.lock()
lock.lock()
criticalSection()
lock.unlock()
lock.unlock()
}
}

runNonRecursiveTest(for: VueFlux.Lock(recursive: false))
runNonRecursiveTest(for: VueFluxReactive.Lock(recursive: false))
runNonRecursiveTest(for: VueFlux.Lock(recursive: false, usePosixThreadMutexForced: true))
runNonRecursiveTest(for: VueFluxReactive.Lock(recursive: false, usePosixThreadMutexForced: true))

runRecursiveTest(for: VueFlux.Lock(recursive: true))
runRecursiveTest(for: VueFluxReactive.Lock(recursive: true))
runRecursiveTest(for: VueFlux.Lock(recursive: true, usePosixThreadMutexForced: true))
runRecursiveTest(for: VueFluxReactive.Lock(recursive: true, usePosixThreadMutexForced: true))
}
}
Loading