Skip to content

Commit 671ae94

Browse files
authored
Merge pull request #24514 from broadwaylamb/docs-concurrency
[Docs] Modernize Concurrency.rst
2 parents dcf27a5 + 8454cbd commit 671ae94

File tree

1 file changed

+87
-87
lines changed

1 file changed

+87
-87
lines changed

docs/proposals/Concurrency.rst

Lines changed: 87 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,12 @@ multi-threaded program below.
4848
4949
import Foundation
5050
51-
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
52-
let queue = dispatch_get_global_queue(priority, 0)
51+
let queue = DispatchQueue.global(qos: .default)
5352
5453
class Bird {}
5554
var single = Bird()
5655
57-
dispatch_async(queue) {
56+
queue.async {
5857
while true { single = Bird() }
5958
}
6059
while true { single = Bird() }
@@ -171,13 +170,10 @@ with value semantics as Copyable.
171170

172171
Value-semantic types are not the only category of types that can be copied.
173172
Library designers can implement thread-safe or lockless data structures and
174-
manually mark them as Copyable.
175-
176-
Notice that due to rdar://17144340 we still can't mark Arrays and Optionals as
177-
copyable::
173+
manually mark them as Copyable::
178174

179175
// Optionals are copyable if the payload type is copyable.
180-
extension Optional : CopyableType where T : CopyableType {}
176+
extension Optional : Copyable where T : Copyable {}
181177

182178
2. Reentrant code
183179
~~~~~~~~~~~~~~~~~
@@ -280,13 +276,13 @@ Implementing safe Go-lang style concurrency
280276
In this section, we describe how the proposed thread-safety layer can be used for
281277
implementing go-lang style concurrency. Go supports concurrency using
282278
coroutines and channels. We are going to demonstrate how to
283-
implement go-style concurrency using verified code, CopyableType protocol
279+
implement go-style concurrency using verified code, Copyable protocol
284280
and gateway annotations.
285281

286282
Let's start by implementing Streams, which are analogous to go channels. A
287283
stream is simply a blocking queue with restrictions on the types that can be
288284
passed. Streams are generic data structures where the queue element type is
289-
``CopyableType`` (and conforms to the relevant protocol, discussed above).
285+
``Copyable`` (and conforms to the relevant protocol, discussed above).
290286
Streams are the only legitimate channel of communication between threads.
291287

292288
Streams can be shared by multiple tasks. These tasks can read from and write into the stream
@@ -304,7 +300,7 @@ created using gateways (see above) that ensure thread safety.
304300
Together tasks and streams create a thread-safe concurrency construct. Let's
305301
delve into this claim. Tasks are created using gateways that ensure that all
306302
arguments being passed into the closure that will be executed are
307-
CopyableType. In other words, all of the arguments are either deep-copied or
303+
Copyable. In other words, all of the arguments are either deep-copied or
308304
implemented in a way that will forbid sharing of memory. The gateway also
309305
ensures that the closure that will be executed by the task is verified, which
310306
means that it will not access global variables or unsafe code, and it will not capture
@@ -313,7 +309,7 @@ ensures a hermetic separation between the newly created thread and the parent
313309
thread. Tasks can communicate using streams that ensure that information that
314310
passes between threads, just like the task's closure arguments, does not leak
315311
references and keeps the hermetic separation between the tasks. Notice that
316-
Streams themselves are CopyableTypes because they can be copied freely between
312+
Streams themselves are Copyable because they can be copied freely between
317313
tasks without violating thread safety.
318314

319315
Stream and Tasks provide safety and allow users to develop server-like tasks
@@ -356,15 +352,19 @@ declaration of the streams in the closure.
356352

357353
.. code-block:: swift
358354
359-
let comm : _Endpoint<String, Int> = createTask({var counter = 0
360-
while true {
361-
$0.pop()
362-
$0.push(counter++)
363-
}})
355+
let comm : _Endpoint<String, Int> = createTask {
356+
var counter = 0
357+
while true {
358+
$0.pop()
359+
$0.push(counter)
360+
counter += 1
361+
}
362+
}
363+
364364
// CHECK: 0, 1, 2,
365365
for ss in ["","",""] {
366366
comm.push(ss)
367-
print("\(comm.pop()), ", appendNewline: false)
367+
print("\(comm.pop()), ", terminator: "")
368368
}
369369
370370
Stream utilities
@@ -403,16 +403,16 @@ Example of a concurrent program using Futures in Swift.
403403

404404
.. code-block:: swift
405405
406-
func merge_sort<T : Comparable>(array: ArraySlice<T>) -> [T] {
406+
func mergeSort<T : Comparable>(array: ArraySlice<T>) -> [T] {
407407
408-
if array.count <= 16 { return Array(array).sort() }
408+
if array.count <= 16 { return Array(array).sorted() }
409409
410410
let mid = array.count / 2
411411
let left = array[0..<mid]
412412
let right = array[mid..<array.count]
413413
414-
let lf = async(left, callback: merge_sort)
415-
let lr = async(right, callback: merge_sort)
414+
let lf = async(left, callback: mergeSort)
415+
let lr = async(right, callback: mergeSort)
416416
417417
return merge(lf.await(), lr.await())
418418
}
@@ -429,16 +429,17 @@ Here is another example of async calls using trailing closures and enums.
429429
.. code-block:: swift
430430
431431
enum Shape {
432-
case Circle, Oval, Square, Triangle
432+
case circle, oval, square, triangle
433433
}
434434
435-
let res = async(Shape.Oval) {(c : Shape) -> (String) in
436-
switch c {
437-
case .Circle: return "Circle"
438-
case .Oval: return "Oval"
439-
case .Square: return "Square"
440-
case .Triangle: return "Triangle"
441-
}}
435+
let res = async(Shape.oval) { (c: Shape) -> String in
436+
switch c {
437+
case .circle: return "Circle"
438+
case .oval: return "Oval"
439+
case .square: return "Square"
440+
case .triangle: return "Triangle"
441+
}
442+
}
442443
443444
//CHECK: Shape: Oval
444445
print("Shape: \(res.await())")
@@ -483,18 +484,16 @@ free to make unsafe calls capture locals and access globals.
483484
@IBAction func onClick(_ sender: AnyObject) {
484485
485486
progress.startAnimating()
486-
Label!.text = ""
487+
label!.text = ""
487488
488-
asyncWith (1_000_000) {
489-
(num: Int) -> Int in
489+
asyncWith (1_000_000) { (num: Int) -> Int in
490490
var sum = 0
491491
for i in 1..<num {
492492
if isPrime(i) { sum += 1 }
493493
}
494494
return sum
495-
}.setOnComplete {
496-
(x : Int) in
497-
self.Label!.text = "Found \(x) primes.\n"
495+
}.setOnComplete { (x: Int) in
496+
self.label!.text = "Found \(x) primes.\n"
498497
self.progress.stopAnimating()
499498
}
500499
@@ -523,15 +522,15 @@ thread-safe (explained in the previous section). For example:
523522
524523
@_semantics("swift.concurrent.async")
525524
// This annotation tells the compiler to verify the closure and the passed arguments at the call site.
526-
public func async<RetTy, ArgsTy>(args : ArgsTy, callback : (ArgsTy) -> RetTy) -> Future<RetTy> {
525+
public func async<RetTy, ArgsTy>(args: ArgsTy, callback: @escaping (ArgsTy) -> RetTy) -> Future<RetTy> {
527526
return unsafeAsync(args, callback: callback)
528527
}
529528
530529
Example of shared data structures
531530
---------------------------------
532531

533532
In the example below the class PrimesCache is explicitly marked by the user as a
534-
CopyableType. The user implemented a thread-safe class that allows concurrent
533+
Copyable. The user implemented a thread-safe class that allows concurrent
535534
access to the method ``isPrime``. To implement a critical section the user
536535
inherit the class ``Sync`` that contains a lock and a method that implements a
537536
critical section. The user also had to annotate the shared method as safe
@@ -542,11 +541,11 @@ are not synchronized on the same lock.
542541

543542
.. code-block:: swift
544543
545-
final class PrimesCache : Sync, CopyableType {
546-
var cache : [Int : Bool] = [:]
544+
final class PrimesCache : Sync, Copyable {
545+
var cache: [Int : Bool] = [:]
547546
548547
@_semantics("swift.concurrent.safe")
549-
func isPrime(_ num : Int) -> Bool {
548+
func isPrime(_ num: Int) -> Bool {
550549
return self.critical {
551550
if let r = self.cache[num] { return r }
552551
let b = calcIsPrime(num)
@@ -556,18 +555,18 @@ are not synchronized on the same lock.
556555
}
557556
}
558557
559-
func countPrimes(_ P : PrimesCache) -> Int {
558+
func countPrimes(_ p: PrimesCache) -> Int {
560559
var sum = 0
561-
for i in 2..<10_000 { if P.isPrime(i) { sum += 1} }
560+
for i in 2..<10_000 where p.isPrime(i) { sum += 1 }
562561
return sum
563562
}
564563
565564
let shared = PrimesCache()
566-
let R1 = async(shared, callback: countPrimes)
567-
let R2 = async(shared, callback: countPrimes)
565+
let r1 = async(shared, callback: countPrimes)
566+
let r2 = async(shared, callback: countPrimes)
568567
569568
// CHECK: [1229, 1229]
570-
print([R1.await(), R2.await()])
569+
print([r1.await(), r2.await()])
571570
572571
573572
Example of parallel matrix multiply using Async
@@ -580,45 +579,45 @@ code runs significantly faster.
580579

581580
.. code-block:: swift
582581
583-
func ParallelMatMul(_ A : Matrix,_ B : Matrix) -> Matrix {
584-
assert(A.size == B.size, "size mismatch!")
582+
func ParallelMatMul(_ a: Matrix, _ b: Matrix) -> Matrix {
583+
assert(a.size == b.size, "size mismatch!")
585584
586585
// Handle small matrices using the serial algorithm.
587-
if A.size < 65 { return SerialMatMul(A, B) }
588-
589-
var product = Matrix(A.size)
590-
// Extract 4 quarters from matrices A and B.
591-
let half = A.size/2
592-
let A11 = A.slice(half, 0, 0)
593-
let A12 = A.slice(half, 0, half)
594-
let A21 = A.slice(half, half, 0)
595-
let A22 = A.slice(half, half, half)
596-
let B11 = B.slice(half, 0, 0)
597-
let B12 = B.slice(half, 0, half)
598-
let B21 = B.slice(half, half, 0)
599-
let B22 = B.slice(half, half, half)
586+
if a.size < 65 { return SerialMatMul(a, b) }
587+
588+
var product = Matrix(a.size)
589+
// Extract 4 quarters from matrices a and b.
590+
let half = a.size/2
591+
let a11 = a.slice(half, 0, 0)
592+
let a12 = a.slice(half, 0, half)
593+
let a21 = a.slice(half, half, 0)
594+
let a22 = a.slice(half, half, half)
595+
let b11 = b.slice(half, 0, 0)
596+
let b12 = b.slice(half, 0, half)
597+
let b21 = b.slice(half, half, 0)
598+
let b22 = b.slice(half, half, half)
600599
601600
// Multiply each of the sub blocks.
602-
let C11_1 = async((A11, B11), callback: ParallelMatMul)
603-
let C11_2 = async((A12, B21), callback: ParallelMatMul)
604-
let C12_1 = async((A11, B12), callback: ParallelMatMul)
605-
let C12_2 = async((A12, B22), callback: ParallelMatMul)
606-
let C21_1 = async((A21, B11), callback: ParallelMatMul)
607-
let C21_2 = async((A22, B21), callback: ParallelMatMul)
608-
let C22_1 = async((A21, B12), callback: ParallelMatMul)
609-
let C22_2 = async((A22, B22), callback: ParallelMatMul)
601+
let c11_1 = async((a11, b11), callback: ParallelMatMul)
602+
let c11_2 = async((a12, b21), callback: ParallelMatMul)
603+
let c12_1 = async((a11, b12), callback: ParallelMatMul)
604+
let c12_2 = async((a12, b22), callback: ParallelMatMul)
605+
let c21_1 = async((a21, b11), callback: ParallelMatMul)
606+
let c21_2 = async((a22, b21), callback: ParallelMatMul)
607+
let c22_1 = async((a21, b12), callback: ParallelMatMul)
608+
let c22_2 = async((a22, b22), callback: ParallelMatMul)
610609
611610
// Add the matching blocks.
612-
let C11 = C11_1.await() + C11_2.await()
613-
let C12 = C12_1.await() + C12_2.await()
614-
let C21 = C21_1.await() + C21_2.await()
615-
let C22 = C22_1.await() + C22_2.await()
611+
let c11 = c11_1.await() + c11_2.await()
612+
let c12 = c12_1.await() + c12_2.await()
613+
let c21 = c21_1.await() + c21_2.await()
614+
let c22 = c22_1.await() + c22_2.await()
616615
617616
// Save the matrix slices into the correct locations.
618-
product.update(C11, 0, 0)
619-
product.update(C12, 0, half)
620-
product.update(C21, half, 0)
621-
product.update(C22, half, half)
617+
product.update(c11, 0, 0)
618+
product.update(c12, 0, half)
619+
product.update(c21, half, 0)
620+
product.update(c22, half, half)
622621
return product
623622
}
624623
@@ -635,14 +634,14 @@ not backed by a live thread or by a stack.
635634

636635
In Swift actors could be implemented using classes that inherit from the generic
637636
``Actor`` class. The generic parameter determines the type of messages that the
638-
actor can accept. The message type needs to be of ``CopyableType`` to ensure the
637+
actor can accept. The message type needs to be of ``Copyable`` to ensure the
639638
safety of the model. The actor class exposes two methods: ``send`` and
640639
``accept``. Messages are sent to actors using the ``send`` method and they never
641640
block the sender. Actors process the message using the ``accept`` method.
642641

643642
At this point it should be obvious to the reader of the document why
644643
marking the ``accept`` method as thread safe and allowing the parameter type to
645-
be ``CopyableType`` will ensure the safety of the system (this is discussed at
644+
be ``Copyable`` will ensure the safety of the system (this is discussed at
646645
length in the previous sections).
647646

648647
The ``accept`` method is executed by a user-space scheduler and not by live
@@ -660,32 +659,33 @@ Finally, a collector actor saves all of the messages into an array.
660659
661660
var numbers = ContiguousArray<Int>()
662661
663-
override func accept(_ x : Int) { numbers.append(x) }
662+
override func accept(_ x: Int) { numbers.append(x) }
664663
}
665664
666665
// Filter numbers that are divisible by an argument.
667666
class Sieve : Actor<Int> {
668-
var div : Int
669-
var next : Actor<Int>
667+
var div: Int
668+
var next: Actor<Int>
670669
671-
init(div d : Int, next n : Actor<Int>) {
672-
div = d ; next = n
670+
init(div d: Int, next n: Actor<Int>) {
671+
div = d
672+
next = n
673673
}
674674
675-
override func accept(_ x : Int) {
675+
override func accept(_ x: Int) {
676676
if x != div && x % div == 0 { return }
677677
next.send(x)
678678
}
679679
}
680680
681681
var col = Collector()
682-
var head : Actor<Int> = col
682+
var head: Actor<Int> = col
683683
684684
// Construct the Sieve
685685
for i in 2..<limit { head = Sieve(div: i, next: head) }
686686
687687
// Send all of the integers
688-
for i in 2..<(limit*limit) { head.send(i) }
688+
for i in 2..<(limit * limit) { head.send(i) }
689689
690690
// CHECK: [1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
691691
print(col.numbers.sort())

0 commit comments

Comments
 (0)