Skip to content

Commit

Permalink
Merge pull request #20 from ra1028/1.3.0
Browse files Browse the repository at this point in the history
1.3.0
  • Loading branch information
ra1028 authored Feb 28, 2018
2 parents a2bbf17 + 111dce5 commit 731e582
Show file tree
Hide file tree
Showing 17 changed files with 368 additions and 216 deletions.
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

0 comments on commit 731e582

Please sign in to comment.