stdlib/String: fix a race in _StringBuffer.grow()

rdar://17855614


Swift SVN r20960
This commit is contained in:
Dmitri Hrybenko
2014-08-02 22:50:45 +00:00
parent 63304b57b4
commit 6d4f8adbf5
9 changed files with 582 additions and 7 deletions

View File

@@ -0,0 +1,114 @@
// RUN: %target-build-swift -parse-stdlib -Xfrontend -disable-access-control -module-name a %s -o %t.out
// RUN: %target-run %t.out | FileCheck %s
import Swift
import StdlibUnittest
import Dispatch
var StringTestCase = TestCase("String")
extension String {
var bufferID: UWord {
return unsafeBitCast(_core._owner, UWord.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 Master
case Slave
}
var barrierVar: UnsafeMutablePointer<_stdlib_pthread_barrier_t> = nil
var sharedString: String = ""
var slaveString: 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 == .Master {
// Get a fresh buffer.
sharedString = ""
sharedString.extend("abc")
expectLE(7, sharedString.capacityInBytes)
// FIXME: check capacity
}
barrier()
// Get a private string.
var privateString = sharedString
barrier()
// Append to the private string.
if tid == .Master {
privateString.extend("def")
} else {
privateString.extend("ghi")
}
barrier()
// Verify that contents look good.
if tid == .Master {
expectEqual("abcdef", privateString)
} else {
expectEqual("abcghi", privateString)
}
expectEqual("abc", sharedString)
// Verify that only one thread took ownership of the buffer.
if tid == .Slave {
slaveString = privateString
}
barrier()
if tid == .Master {
expectTrue(
(privateString.bufferID == sharedString.bufferID) !=
(slaveString.bufferID == sharedString.bufferID))
}
}
}
StringTestCase.test("SliceConcurrentAppend") {
barrierVar = UnsafeMutablePointer.alloc(1)
barrierVar.initialize(_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, .Master)
let (createRet2, tid2) = _stdlib_pthread_create_block(
nil, sliceConcurrentAppendThread, .Slave)
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.destroy()
barrierVar.dealloc(1)
}
StringTestCase.run()
// CHECK: {{^}}String: All tests passed