@@ -48,13 +48,12 @@ multi-threaded program below.
48
48
49
49
import Foundation
50
50
51
- let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
52
- let queue = dispatch_get_global_queue (priority, 0 )
51
+ let queue = DispatchQueue.global (qos : .default )
53
52
54
53
class Bird {}
55
54
var single = Bird ()
56
55
57
- dispatch_async ( queue) {
56
+ queue. async {
58
57
while true { single = Bird () }
59
58
}
60
59
while true { single = Bird () }
@@ -171,13 +170,10 @@ with value semantics as Copyable.
171
170
172
171
Value-semantic types are not the only category of types that can be copied.
173
172
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::
178
174
179
175
// Optionals are copyable if the payload type is copyable.
180
- extension Optional : CopyableType where T : CopyableType {}
176
+ extension Optional : Copyable where T : Copyable {}
181
177
182
178
2. Reentrant code
183
179
~~~~~~~~~~~~~~~~~
@@ -280,13 +276,13 @@ Implementing safe Go-lang style concurrency
280
276
In this section, we describe how the proposed thread-safety layer can be used for
281
277
implementing go-lang style concurrency. Go supports concurrency using
282
278
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
284
280
and gateway annotations.
285
281
286
282
Let's start by implementing Streams, which are analogous to go channels. A
287
283
stream is simply a blocking queue with restrictions on the types that can be
288
284
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).
290
286
Streams are the only legitimate channel of communication between threads.
291
287
292
288
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.
304
300
Together tasks and streams create a thread-safe concurrency construct. Let's
305
301
delve into this claim. Tasks are created using gateways that ensure that all
306
302
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
308
304
implemented in a way that will forbid sharing of memory. The gateway also
309
305
ensures that the closure that will be executed by the task is verified, which
310
306
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
313
309
thread. Tasks can communicate using streams that ensure that information that
314
310
passes between threads, just like the task's closure arguments, does not leak
315
311
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
317
313
tasks without violating thread safety.
318
314
319
315
Stream and Tasks provide safety and allow users to develop server-like tasks
@@ -356,15 +352,19 @@ declaration of the streams in the closure.
356
352
357
353
.. code-block :: swift
358
354
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
+
364
364
// CHECK: 0, 1, 2,
365
365
for ss in [" " ," " ," " ] {
366
366
comm.push (ss)
367
- print (" \( comm.pop () ) , " , appendNewline : false )
367
+ print (" \( comm.pop () ) , " , terminator : " " )
368
368
}
369
369
370
370
Stream utilities
@@ -403,16 +403,16 @@ Example of a concurrent program using Futures in Swift.
403
403
404
404
.. code-block :: swift
405
405
406
- func merge_sort <T : Comparable >(array : ArraySlice <T>) -> [T] {
406
+ func mergeSort <T : Comparable >(array : ArraySlice <T>) -> [T] {
407
407
408
- if array.count <= 16 { return Array (array).sort () }
408
+ if array.count <= 16 { return Array (array).sorted () }
409
409
410
410
let mid = array.count / 2
411
411
let left = array[0 ..< mid]
412
412
let right = array[mid..< array.count ]
413
413
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 )
416
416
417
417
return merge (lf.await (), lr.await ())
418
418
}
@@ -429,16 +429,17 @@ Here is another example of async calls using trailing closures and enums.
429
429
.. code-block :: swift
430
430
431
431
enum Shape {
432
- case Circle , Oval , Square , Triangle
432
+ case circle , oval , square , triangle
433
433
}
434
434
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
+ }
442
443
443
444
// CHECK: Shape: Oval
444
445
print (" Shape: \( res.await () ) " )
@@ -483,18 +484,16 @@ free to make unsafe calls capture locals and access globals.
483
484
@IBAction func onClick (_ sender : AnyObject ) {
484
485
485
486
progress.startAnimating ()
486
- Label ! .text = " "
487
+ label ! .text = " "
487
488
488
- asyncWith (1_000_000 ) {
489
- (num : Int ) -> Int in
489
+ asyncWith (1_000_000 ) { (num : Int ) -> Int in
490
490
var sum = 0
491
491
for i in 1 ..< num {
492
492
if isPrime (i) { sum += 1 }
493
493
}
494
494
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 "
498
497
self .progress .stopAnimating ()
499
498
}
500
499
@@ -523,15 +522,15 @@ thread-safe (explained in the previous section). For example:
523
522
524
523
@_semantics (" swift.concurrent.async" )
525
524
// 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> {
527
526
return unsafeAsync (args, callback : callback)
528
527
}
529
528
530
529
Example of shared data structures
531
530
---------------------------------
532
531
533
532
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
535
534
access to the method ``isPrime ``. To implement a critical section the user
536
535
inherit the class ``Sync `` that contains a lock and a method that implements a
537
536
critical section. The user also had to annotate the shared method as safe
@@ -542,11 +541,11 @@ are not synchronized on the same lock.
542
541
543
542
.. code-block :: swift
544
543
545
- final class PrimesCache : Sync , CopyableType {
546
- var cache : [Int : Bool ] = [: ]
544
+ final class PrimesCache : Sync , Copyable {
545
+ var cache: [Int : Bool ] = [: ]
547
546
548
547
@_semantics (" swift.concurrent.safe" )
549
- func isPrime (_ num : Int ) -> Bool {
548
+ func isPrime (_ num : Int ) -> Bool {
550
549
return self .critical {
551
550
if let r = self .cache[num] { return r }
552
551
let b = calcIsPrime (num)
@@ -556,18 +555,18 @@ are not synchronized on the same lock.
556
555
}
557
556
}
558
557
559
- func countPrimes (_ P : PrimesCache) -> Int {
558
+ func countPrimes (_ p : PrimesCache) -> Int {
560
559
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 }
562
561
return sum
563
562
}
564
563
565
564
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)
568
567
569
568
// CHECK: [1229, 1229]
570
- print ([R1 .await (), R2 .await ()])
569
+ print ([r1 .await (), r2 .await ()])
571
570
572
571
573
572
Example of parallel matrix multiply using Async
@@ -580,45 +579,45 @@ code runs significantly faster.
580
579
581
580
.. code-block :: swift
582
581
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!" )
585
584
586
585
// 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)
600
599
601
600
// 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)
610
609
611
610
// 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 ()
616
615
617
616
// 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)
622
621
return product
623
622
}
624
623
@@ -635,14 +634,14 @@ not backed by a live thread or by a stack.
635
634
636
635
In Swift actors could be implemented using classes that inherit from the generic
637
636
``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
639
638
safety of the model. The actor class exposes two methods: ``send `` and
640
639
``accept ``. Messages are sent to actors using the ``send `` method and they never
641
640
block the sender. Actors process the message using the ``accept `` method.
642
641
643
642
At this point it should be obvious to the reader of the document why
644
643
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
646
645
length in the previous sections).
647
646
648
647
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.
660
659
661
660
var numbers = ContiguousArray < Int > ()
662
661
663
- override func accept (_ x : Int ) { numbers.append (x) }
662
+ override func accept (_ x : Int ) { numbers.append (x) }
664
663
}
665
664
666
665
// Filter numbers that are divisible by an argument.
667
666
class Sieve : Actor<Int > {
668
- var div : Int
669
- var next : Actor<Int >
667
+ var div: Int
668
+ var next: Actor<Int >
670
669
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
673
673
}
674
674
675
- override func accept (_ x : Int ) {
675
+ override func accept (_ x : Int ) {
676
676
if x != div && x % div == 0 { return }
677
677
next.send (x)
678
678
}
679
679
}
680
680
681
681
var col = Collector ()
682
- var head : Actor<Int > = col
682
+ var head: Actor<Int > = col
683
683
684
684
// Construct the Sieve
685
685
for i in 2 ..< limit { head = Sieve (div : i, next : head) }
686
686
687
687
// 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) }
689
689
690
690
// CHECK: [1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
691
691
print (col.numbers .sort ())
0 commit comments