Lock: guarantee crash instead of undefined behaviour (#1131)

Motivation:

NIO's Lock currently just deadlocks if a thread which already holds a
lock tries to reacquire it. This however isn't even really defined
behaviour.

Modifications:

Switch us to a guaranteed crash from a unguarnateed hang.

Result:

Easier to debug deadlocks with NIO's lock.
This commit is contained in:
Johannes Weiss 2019-09-09 09:50:04 +01:00 committed by Cory Benfield
parent b6067bd531
commit cee3db7072
3 changed files with 103 additions and 10 deletions

View File

@ -0,0 +1,47 @@
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the SwiftNIO open source project
##
## Copyright (c) 2019 Apple Inc. and the SwiftNIO project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.txt for the list of SwiftNIO project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##
set -eu
function make_package() {
cat > "$tmpdir/syscallwrapper/Package.swift" <<"EOF"
// swift-tools-version:5.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "syscallwrapper",
dependencies: [],
targets: [
.target(
name: "syscallwrapper",
dependencies: ["CNIOLinux", "CNIODarwin"]),
.target(
name: "CNIOLinux",
dependencies: []),
.target(
name: "CNIODarwin",
dependencies: []),
]
)
EOF
cp "$here/../../Tests/NIOTests/SystemCallWrapperHelpers.swift" \
"$here/../../Sources/NIO/System.swift" \
"$here/../../Sources/NIO/IO.swift" \
"$tmpdir/syscallwrapper/Sources/syscallwrapper"
ln -s "$here/../../Sources/CNIOLinux" "$tmpdir/syscallwrapper/Sources"
ln -s "$here/../../Sources/CNIODarwin" "$tmpdir/syscallwrapper/Sources"
}

View File

@ -0,0 +1,42 @@
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the SwiftNIO open source project
##
## Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.txt for the list of SwiftNIO project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##
set -eu
source defines.sh
swift_binary=swiftc
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
if [[ ! -z "${SWIFT_EXEC-}" ]]; then
swift_binary="$(dirname "$SWIFT_EXEC")/swiftc"
elif [[ "$(uname -s)" == "Linux" ]]; then
swift_binary=$(which swiftc)
fi
cp "$here/../../Sources/NIOConcurrencyHelpers/lock.swift" "$tmp"
cat > "$tmp/main.swift" <<"EOF"
let l = Lock()
l.lock()
l.lock()
EOF
"$swift_binary" -o "$tmp/test" "$tmp/main.swift" "$tmp/lock.swift"
if "$tmp/test"; then
fail "should have crashed"
else
exit_code=$?
assert_equal $(( 128 + 4 )) $exit_code # 4 == SIGILL
fi

View File

@ -28,13 +28,17 @@ public final class Lock {
/// Create a new lock.
public init() {
let err = pthread_mutex_init(self.mutex, nil)
precondition(err == 0)
var attr = pthread_mutexattr_t()
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK));
let err = pthread_mutex_init(self.mutex, &attr)
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
}
deinit {
let err = pthread_mutex_destroy(self.mutex)
precondition(err == 0)
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
mutex.deallocate()
}
@ -44,7 +48,7 @@ public final class Lock {
/// `unlock`, to simplify lock handling.
public func lock() {
let err = pthread_mutex_lock(self.mutex)
precondition(err == 0)
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
}
/// Release the lock.
@ -53,7 +57,7 @@ public final class Lock {
/// `lock`, to simplify lock handling.
public func unlock() {
let err = pthread_mutex_unlock(self.mutex)
precondition(err == 0)
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
}
}
@ -98,12 +102,12 @@ public final class ConditionLock<T: Equatable> {
self._value = value
self.mutex = Lock()
let err = pthread_cond_init(self.cond, nil)
precondition(err == 0)
precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)")
}
deinit {
let err = pthread_cond_destroy(self.cond)
precondition(err == 0)
precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)")
self.cond.deallocate()
}
@ -141,7 +145,7 @@ public final class ConditionLock<T: Equatable> {
break
}
let err = pthread_cond_wait(self.cond, self.mutex.mutex)
precondition(err == 0, "pthread_cond_wait error \(err)")
precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)")
}
}
@ -192,7 +196,7 @@ public final class ConditionLock<T: Equatable> {
public func unlock(withValue newValue: T) {
self._value = newValue
self.unlock()
let r = pthread_cond_broadcast(self.cond)
precondition(r == 0)
let err = pthread_cond_broadcast(self.cond)
precondition(err == 0, "\(#function) failed in pthread_cond with error \(err)")
}
}