-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathStringSlicesConcurrentAppend.swift
118 lines (93 loc) · 2.86 KB
/
StringSlicesConcurrentAppend.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// RUN: %target-run-simple-swift
// REQUIRES: executable_test
import StdlibUnittest
import SwiftPrivatePthreadExtras
#if os(OSX) || os(iOS)
import Darwin
#elseif os(Linux)
import Glibc
#endif
var StringTestSuite = TestSuite("String")
extension String {
var bufferID: UInt {
return unsafeBitCast(_core._owner, to: UInt.self)
}
var capacityInBytes: Int {
return _core.nativeBuffer!.capacity
}
}
// Swift.String has an optimization that allows us to append to a shared string
// buffer. Make sure that it works correctly when two threads try to append to
// different non-shared strings that point to the same shared buffer.
enum ThreadID {
case Primary
case Secondary
}
var barrierVar: UnsafeMutablePointer<_stdlib_pthread_barrier_t>? = nil
var sharedString: String = ""
var secondaryString: String = ""
func barrier() {
var ret = _stdlib_pthread_barrier_wait(barrierVar!)
expectTrue(ret == 0 || ret == _stdlib_PTHREAD_BARRIER_SERIAL_THREAD)
}
func sliceConcurrentAppendThread(_ tid: ThreadID) {
for i in 0..<100 {
barrier()
if tid == .Primary {
// Get a fresh buffer.
sharedString = ""
sharedString.append("abc")
sharedString.reserveCapacity(16)
expectLE(16, sharedString.capacityInBytes)
}
barrier()
// Get a private string.
var privateString = sharedString
barrier()
// Append to the private string.
if tid == .Primary {
privateString.append("def")
} else {
privateString.append("ghi")
}
barrier()
// Verify that contents look good.
if tid == .Primary {
expectEqual("abcdef", privateString)
} else {
expectEqual("abcghi", privateString)
}
expectEqual("abc", sharedString)
// Verify that only one thread took ownership of the buffer.
if tid == .Secondary {
secondaryString = privateString
}
barrier()
if tid == .Primary {
expectTrue(
(privateString.bufferID == sharedString.bufferID) !=
(secondaryString.bufferID == sharedString.bufferID))
}
}
}
StringTestSuite.test("SliceConcurrentAppend") {
barrierVar = UnsafeMutablePointer(allocatingCapacity: 1)
barrierVar!.initialize(with: _stdlib_pthread_barrier_t())
var ret = _stdlib_pthread_barrier_init(barrierVar!, nil, 2)
expectEqual(0, ret)
let (createRet1, tid1) = _stdlib_pthread_create_block(
nil, sliceConcurrentAppendThread, .Primary)
let (createRet2, tid2) = _stdlib_pthread_create_block(
nil, sliceConcurrentAppendThread, .Secondary)
expectEqual(0, createRet1)
expectEqual(0, createRet2)
let (joinRet1, _) = _stdlib_pthread_join(tid1!, Void.self)
let (joinRet2, _) = _stdlib_pthread_join(tid2!, Void.self)
expectEqual(0, joinRet1)
expectEqual(0, joinRet2)
ret = _stdlib_pthread_barrier_destroy(barrierVar!)
expectEqual(0, ret)
barrierVar!.deinitialize()
barrierVar!.deallocateCapacity(1)
}
runAllTests()