Skip to content

Commit 6ae1a34

Browse files
authored
Implement concurrent enumeration for NSArray, NSDictionary and NSIndexSet (swiftlang#1066)
1 parent d8a4c18 commit 6ae1a34

File tree

3 files changed

+78
-38
lines changed

3 files changed

+78
-38
lines changed

Diff for: Foundation/NSArray.swift

-3
Original file line numberDiff line numberDiff line change
@@ -446,9 +446,6 @@ open class NSArray : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCo
446446
self.enumerateObjects(at: IndexSet(integersIn: 0..<count), options: opts, using: block)
447447
}
448448
open func enumerateObjects(at s: IndexSet, options opts: NSEnumerationOptions = [], using block: (Any, Int, UnsafeMutablePointer<ObjCBool>) -> Swift.Void) {
449-
guard !opts.contains(.concurrent) else {
450-
NSUnimplemented()
451-
}
452449
s._bridgeToObjectiveC().enumerate(options: opts) { (idx, stop) in
453450
block(self.object(at: idx), idx, stop)
454451
}

Diff for: Foundation/NSDictionary.swift

+28-10
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
import CoreFoundation
12-
12+
import Dispatch
1313

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

457-
open func enumerateKeysAndObjects(options opts: NSEnumerationOptions = [], using block: (Any, Any, UnsafeMutablePointer<ObjCBool>) -> Swift.Void) {
457+
open func enumerateKeysAndObjects(options opts: NSEnumerationOptions = [], using block: (Any, Any, UnsafeMutablePointer<ObjCBool>) -> Void) {
458458
let count = self.count
459459
var keys = [Any]()
460460
var objects = [Any]()
461+
var sharedStop = ObjCBool(false)
462+
let lock = NSLock()
463+
461464
getObjects(&objects, andKeys: &keys, count: count)
462-
var stop = ObjCBool(false)
463-
for idx in 0..<count {
464-
withUnsafeMutablePointer(to: &stop, { stop in
465-
block(keys[idx], objects[idx], stop)
466-
})
467-
468-
if stop {
469-
break
465+
let iteration: (Int) -> Void = withoutActuallyEscaping(block) { (closure: @escaping (Any, Any, UnsafeMutablePointer<ObjCBool>) -> Void) -> (Int) -> Void in
466+
return { (idx) in
467+
lock.lock()
468+
var stop = sharedStop
469+
lock.unlock()
470+
if stop { return }
471+
472+
closure(keys[idx], objects[idx], &stop)
473+
474+
if stop {
475+
lock.lock()
476+
sharedStop = stop
477+
lock.unlock()
478+
return
479+
}
480+
}
481+
}
482+
483+
if opts.contains(.concurrent) {
484+
DispatchQueue.concurrentPerform(iterations: count, execute: iteration)
485+
} else {
486+
for idx in 0..<count {
487+
iteration(idx)
470488
}
471489
}
472490
}

Diff for: Foundation/NSIndexSet.swift

+50-25
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
88
//
99

10+
import Dispatch
1011

1112
/* 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.
1213

@@ -388,36 +389,60 @@ open class NSIndexSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
388389
let reverse = opts.contains(.reverse)
389390
let passRanges = paramType == NSRange.self
390391
let findIndex = returnType == Bool.self
391-
var stop = false
392+
var sharedStop = false
393+
let lock = NSLock()
392394
let ranges = _ranges[startRangeIndex...endRangeIndex]
393-
let rangeSequence = (reverse ? AnySequence(ranges.reversed()) : AnySequence(ranges))
394-
outer: for curRange in rangeSequence {
395-
let intersection = NSIntersectionRange(curRange, range)
396-
if passRanges {
397-
if intersection.length > 0 {
398-
let _ = block(intersection as! P, &stop)
399-
}
400-
if stop {
401-
break outer
402-
}
403-
} else if intersection.length > 0 {
404-
let maxIndex = NSMaxRange(intersection) - 1
405-
let indexes = reverse ? stride(from: maxIndex, through: intersection.location, by: -1) : stride(from: intersection.location, through: maxIndex, by: 1)
406-
for idx in indexes {
407-
if findIndex {
408-
let found : Bool = block(idx as! P, &stop) as! Bool
409-
if found {
410-
result = idx
411-
stop = true
412-
}
413-
} else {
414-
let _ = block(idx as! P, &stop)
395+
let rangeSequence = (reverse ? AnyCollection(ranges.reversed()) : AnyCollection(ranges))
396+
let iteration = withoutActuallyEscaping(block) { (closure: @escaping (P, UnsafeMutablePointer<ObjCBool>) -> R) -> (Int) -> Void in
397+
return { (rangeIdx) in
398+
lock.lock()
399+
var stop = sharedStop
400+
lock.unlock()
401+
if stop { return }
402+
403+
let idx = rangeSequence.index(rangeSequence.startIndex, offsetBy: IntMax(rangeIdx))
404+
let curRange = rangeSequence[idx]
405+
let intersection = NSIntersectionRange(curRange, range)
406+
if passRanges {
407+
if intersection.length > 0 {
408+
let _ = closure(intersection as! P, &stop)
415409
}
416410
if stop {
417-
break outer
411+
lock.lock()
412+
sharedStop = stop
413+
lock.unlock()
414+
return
415+
}
416+
} else if intersection.length > 0 {
417+
let maxIndex = NSMaxRange(intersection) - 1
418+
let indexes = reverse ? stride(from: maxIndex, through: intersection.location, by: -1) : stride(from: intersection.location, through: maxIndex, by: 1)
419+
for idx in indexes {
420+
if findIndex {
421+
let found : Bool = closure(idx as! P, &stop) as! Bool
422+
if found {
423+
result = idx
424+
stop = true
425+
}
426+
} else {
427+
let _ = closure(idx as! P, &stop)
428+
}
429+
if stop {
430+
lock.lock()
431+
sharedStop = stop
432+
lock.unlock()
433+
return
434+
}
418435
}
419436
}
420-
} // else, continue
437+
}
438+
}
439+
440+
if opts.contains(.concurrent) {
441+
DispatchQueue.concurrentPerform(iterations: Int(rangeSequence.count), execute: iteration)
442+
} else {
443+
for idx in 0..<Int(rangeSequence.count) {
444+
iteration(idx)
445+
}
421446
}
422447

423448
return result

0 commit comments

Comments
 (0)