Skip to content

Implement concurrent enumeration for NSArray, NSDictionary and NSIndexSet #1066

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

Merged
merged 1 commit into from
Jun 23, 2017
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
3 changes: 0 additions & 3 deletions Foundation/NSArray.swift
Original file line number Diff line number Diff line change
Expand Up @@ -446,9 +446,6 @@ open class NSArray : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCo
self.enumerateObjects(at: IndexSet(integersIn: 0..<count), options: opts, using: block)
}
open func enumerateObjects(at s: IndexSet, options opts: NSEnumerationOptions = [], using block: (Any, Int, UnsafeMutablePointer<ObjCBool>) -> Swift.Void) {
guard !opts.contains(.concurrent) else {
NSUnimplemented()
}
s._bridgeToObjectiveC().enumerate(options: opts) { (idx, stop) in
block(self.object(at: idx), idx, stop)
}
Expand Down
38 changes: 28 additions & 10 deletions Foundation/NSDictionary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@


import CoreFoundation

import Dispatch

open class NSDictionary : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCoding {
private let _cfinfo = _CFInfo(typeID: CFDictionaryGetTypeID())
Expand Down Expand Up @@ -454,19 +454,37 @@ open class NSDictionary : NSObject, NSCopying, NSMutableCopying, NSSecureCoding,
enumerateKeysAndObjects(options: [], using: block)
}

open func enumerateKeysAndObjects(options opts: NSEnumerationOptions = [], using block: (Any, Any, UnsafeMutablePointer<ObjCBool>) -> Swift.Void) {
open func enumerateKeysAndObjects(options opts: NSEnumerationOptions = [], using block: (Any, Any, UnsafeMutablePointer<ObjCBool>) -> Void) {
let count = self.count
var keys = [Any]()
var objects = [Any]()
var sharedStop = ObjCBool(false)
let lock = NSLock()

getObjects(&objects, andKeys: &keys, count: count)
var stop = ObjCBool(false)
for idx in 0..<count {
withUnsafeMutablePointer(to: &stop, { stop in
block(keys[idx], objects[idx], stop)
})

if stop {
break
let iteration: (Int) -> Void = withoutActuallyEscaping(block) { (closure: @escaping (Any, Any, UnsafeMutablePointer<ObjCBool>) -> Void) -> (Int) -> Void in
return { (idx) in
lock.lock()
var stop = sharedStop
lock.unlock()
if stop { return }

closure(keys[idx], objects[idx], &stop)

if stop {
lock.lock()
sharedStop = stop
lock.unlock()
return
}
}
}

if opts.contains(.concurrent) {
DispatchQueue.concurrentPerform(iterations: count, execute: iteration)
} else {
for idx in 0..<count {
iteration(idx)
}
}
}
Expand Down
75 changes: 50 additions & 25 deletions Foundation/NSIndexSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//

import Dispatch

/* Class for managing set of indexes. The set of valid indexes are 0 .. NSNotFound - 1; trying to use indexes outside this range is an error. NSIndexSet uses NSNotFound as a return value in cases where the queried index doesn't exist in the set; for instance, when you ask firstIndex and there are no indexes; or when you ask for indexGreaterThanIndex: on the last index, and so on.

Expand Down Expand Up @@ -388,36 +389,60 @@ open class NSIndexSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
let reverse = opts.contains(.reverse)
let passRanges = paramType == NSRange.self
let findIndex = returnType == Bool.self
var stop = false
var sharedStop = false
let lock = NSLock()
let ranges = _ranges[startRangeIndex...endRangeIndex]
let rangeSequence = (reverse ? AnySequence(ranges.reversed()) : AnySequence(ranges))
outer: for curRange in rangeSequence {
let intersection = NSIntersectionRange(curRange, range)
if passRanges {
if intersection.length > 0 {
let _ = block(intersection as! P, &stop)
}
if stop {
break outer
}
} else if intersection.length > 0 {
let maxIndex = NSMaxRange(intersection) - 1
let indexes = reverse ? stride(from: maxIndex, through: intersection.location, by: -1) : stride(from: intersection.location, through: maxIndex, by: 1)
for idx in indexes {
if findIndex {
let found : Bool = block(idx as! P, &stop) as! Bool
if found {
result = idx
stop = true
}
} else {
let _ = block(idx as! P, &stop)
let rangeSequence = (reverse ? AnyCollection(ranges.reversed()) : AnyCollection(ranges))
let iteration = withoutActuallyEscaping(block) { (closure: @escaping (P, UnsafeMutablePointer<ObjCBool>) -> R) -> (Int) -> Void in
return { (rangeIdx) in
lock.lock()
var stop = sharedStop
lock.unlock()
if stop { return }

let idx = rangeSequence.index(rangeSequence.startIndex, offsetBy: IntMax(rangeIdx))
let curRange = rangeSequence[idx]
let intersection = NSIntersectionRange(curRange, range)
if passRanges {
if intersection.length > 0 {
let _ = closure(intersection as! P, &stop)
}
if stop {
break outer
lock.lock()
sharedStop = stop
lock.unlock()
return
}
} else if intersection.length > 0 {
let maxIndex = NSMaxRange(intersection) - 1
let indexes = reverse ? stride(from: maxIndex, through: intersection.location, by: -1) : stride(from: intersection.location, through: maxIndex, by: 1)
for idx in indexes {
if findIndex {
let found : Bool = closure(idx as! P, &stop) as! Bool
if found {
result = idx
stop = true
}
} else {
let _ = closure(idx as! P, &stop)
}
if stop {
lock.lock()
sharedStop = stop
lock.unlock()
return
}
}
}
} // else, continue
}
}

if opts.contains(.concurrent) {
DispatchQueue.concurrentPerform(iterations: Int(rangeSequence.count), execute: iteration)
} else {
for idx in 0..<Int(rangeSequence.count) {
iteration(idx)
}
}

return result
Expand Down