swiftlang / swift-corelibs-xctest

The XCTest Project, A Swift core library for providing unit test support
swift.org
Apache License 2.0
1.15k stars 267 forks source link

deadlock in XCTest (Swift 5.10 & Swift 6.0.x) #504

Open weissi opened 1 week ago

weissi commented 1 week ago

In both Swift 5.10 & Swift 6.0 on Linux, there's a trivial deadlock in XCTest itself.

Repro

import XCTest

final class MyTests: XCTestCase {

    func test_foo1() throws {}
    func test_foo2() throws {}
    func test_foo3() throws {}
    func test_foo4() throws {}
    func test_foo5() throws {}
    func test_foo6() throws {}
    func test_foo7() throws {}
    func test_foo8() throws {}
    func test_foo9() throws {}
    func test_foo10() throws {}
    func test_foo11() throws {}
    func test_foo12() throws {}
    func test_foo13() throws {}

    override func setUp() {}
    override func tearDown() {}
}

Expected

Runs the tests forever

Actual

root@a27e47c849f9:/tmp/foo# while swift test; do date; done
Building for debugging...
[1/1] Write swift-version-24593BA9C3E375BF.txt
Build complete! (0.09s)
Test Suite 'All tests' started at 2024-11-12 11:13:51.104
Test Suite 'debug.xctest' started at 2024-11-12 11:13:51.105
Test Suite 'MyTests' started at 2024-11-12 11:13:51.105
Test Case 'MyTests.test_foo1' started at 2024-11-12 11:13:51.105
Test Case 'MyTests.test_foo1' passed (0.0 seconds)
Test Case 'MyTests.test_foo10' started at 2024-11-12 11:13:51.105
[... hangs forever ...]

Versions

root@a27e47c849f9:/tmp/foo# swift -version
Swift version 6.0.1 (swift-6.0.1-RELEASE)
Target: aarch64-unknown-linux-gnu
root@a27e47c849f9:/tmp/foo# cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=24.04
DISTRIB_CODENAME=noble
DISTRIB_DESCRIPTION="Ubuntu 24.04.1 LTS"

Extra info

(lldb) bt all
* thread #1, name = 'fooPackageTests', stop reason = signal SIGSTOP
  * frame #0: 0x0000fffff63210c8 libc.so.6`ppoll + 184
    frame #1: 0x0000fffff67c41a0 libFoundation.so`__CFRunLoopServiceFileDescriptors + 104
    frame #2: 0x0000fffff67bff84 libFoundation.so`__CFRunLoopRun + 1016
    frame #3: 0x0000fffff67bf90c libFoundation.so`CFRunLoopRunSpecific + 476
    frame #4: 0x0000fffff6747fc4 libFoundation.so`Foundation.RunLoop.run(mode: Foundation.RunLoop.Mode, before: FoundationEssentials.Date) -> Swift.Bool + 212
    frame #5: 0x0000fffff6bbbb14 libXCTest.so`XCTest.XCTWaiter.wait(for: Swift.Array<XCTest.XCTestExpectation>, timeout: Swift.Double, enforceOrder: Swift.Bool, file: Swift.StaticString, line: Swift.Int) -> XCTest.XCTWaiter.Result + 444
    frame #6: 0x0000fffff6ba9118 libXCTest.so`XCTest.awaitUsingExpectation(() async throws -> ()) throws -> () + 588
    frame #7: 0x0000fffff6bab5b8 libXCTest.so`XCTest.XCTestCase.performTearDownSequence() -> () + 140
    frame #8: 0x0000fffff6baacbc libXCTest.so`XCTest.XCTestCase.invokeTest() -> () + 608
    frame #9: 0x0000fffff6baa8c0 libXCTest.so`XCTest.XCTestCase.perform(XCTest.XCTestRun) -> () + 128
    frame #10: 0x0000fffff6bad3e8 libXCTest.so`XCTest.XCTest.run() -> () + 164
    frame #11: 0x0000fffff6baba08 libXCTest.so`XCTest.XCTestSuite.perform(XCTest.XCTestRun) -> () + 164
    frame #12: 0x0000fffff6bad3e8 libXCTest.so`XCTest.XCTest.run() -> () + 164
    frame #13: 0x0000fffff6baba08 libXCTest.so`XCTest.XCTestSuite.perform(XCTest.XCTestRun) -> () + 164
    frame #14: 0x0000fffff6bad3e8 libXCTest.so`XCTest.XCTest.run() -> () + 164
    frame #15: 0x0000fffff6baba08 libXCTest.so`XCTest.XCTestSuite.perform(XCTest.XCTestRun) -> () + 164
    frame #16: 0x0000fffff6bad3e8 libXCTest.so`XCTest.XCTest.run() -> () + 164
    frame #17: 0x0000fffff6ba8b2c libXCTest.so`XCTest.XCTMain(_: Swift.Array<(testCaseClass: XCTest.XCTestCase.Type, allTests: Swift.Array<(Swift.String, (XCTest.XCTestCase) throws -> ())>)>, arguments: Swift.Array<Swift.String>, observers: Swift.Optional<Swift.Array<XCTest.XCTestObservation>>) -> Swift.Int32 + 312
    frame #18: 0x0000fffff6ba8bf4 libXCTest.so`XCTest.XCTMain(Swift.Array<(testCaseClass: XCTest.XCTestCase.Type, allTests: Swift.Array<(Swift.String, (XCTest.XCTestCase) throws -> ())>)>) -> Swift.Never + 40
    frame #19: 0x0000aaaaaaad82b8 fooPackageTests.xctest`static Runner.main() at runner.swift:543:14
    frame #20: 0x0000aaaaaaad82d8 fooPackageTests.xctest`static Runner.$main() at <compiler-generated>:0
    frame #21: 0x0000aaaaaaad82f0 fooPackageTests.xctest`main at runner.swift:509:8
    frame #22: 0x0000fffff62684c4 libc.so.6`___lldb_unnamed_symbol3097 + 116
    frame #23: 0x0000fffff6268598 libc.so.6`__libc_start_main + 152
    frame #24: 0x0000aaaaaaaa94b0 fooPackageTests.xctest`_start + 48
  thread #2, name = 'fooPackageTests', stop reason = signal SIGSTOP
    frame #0: 0x0000fffff632bd74 libc.so.6`epoll_pwait + 148
    frame #1: 0x0000fffff750cec4 libdispatch.so`_dispatch_event_loop_drain + 64
    frame #2: 0x0000fffff7501e7c libdispatch.so`_dispatch_mgr_invoke + 124
    frame #3: 0x0000fffff7501df0 libdispatch.so`_dispatch_mgr_thread + 132
    frame #4: 0x0000fffff750562c libdispatch.so`_dispatch_worker_thread + 432
    frame #5: 0x0000fffff62c597c libc.so.6`___lldb_unnamed_symbol3477 + 892
    frame #6: 0x0000fffff632ba4c libc.so.6`___lldb_unnamed_symbol3850 + 12
# lsof -Pnp $(pgrep fooP)
COMMAND    PID USER   FD      TYPE DEVICE SIZE/OFF    NODE NAME
fooPackag 6188 root  cwd       DIR   0,62     4096 8786597 /tmp/foo
fooPackag 6188 root  rtd       DIR   0,62     4096 8683276 /
fooPackag 6188 root  txt       REG   0,62  1161600 8787513 /tmp/foo/.build/aarch64-unknown-linux-gnu/debug/fooPackageTests.xctest
fooPackag 6188 root  mem       REG   0,62  2633224 8006842 /usr/lib/aarch64-linux-gnu/libstdc++.so.6.0.33
fooPackag 6188 root  mem       REG   0,62 39718528 8013362 /usr/lib/swift/linux/lib_FoundationICU.so
fooPackag 6188 root  mem       REG   0,62  1722920 7999573 /usr/lib/aarch64-linux-gnu/libc.so.6
fooPackag 6188 root  mem       REG   0,62  9075480 8013355 /usr/lib/swift/linux/libFoundation.so
fooPackag 6188 root  mem       REG   0,62   136608 8013390 /usr/lib/swift/linux/libswiftSynchronization.so
fooPackag 6188 root  mem       REG   0,62   133696 8006708 /usr/lib/aarch64-linux-gnu/libgcc_s.so.1
fooPackag 6188 root  mem       REG   0,62   591800 7999616 /usr/lib/aarch64-linux-gnu/libm.so.6
fooPackag 6188 root  mem       REG   0,62   572824 8013361 /usr/lib/swift/linux/libXCTest.so
fooPackag 6188 root  mem       REG   0,62  1690784 8013395 /usr/lib/swift/linux/libswift_RegexParser.so
fooPackag 6188 root  mem       REG   0,62  1357456 8013396 /usr/lib/swift/linux/libswift_StringProcessing.so
fooPackag 6188 root  mem       REG   0,62  9653576 8013380 /usr/lib/swift/linux/libswiftCore.so
fooPackag 6188 root  mem       REG   0,62   360096 8013383 /usr/lib/swift/linux/libswiftDispatch.so
fooPackag 6188 root  mem       REG   0,62   466024 8013368 /usr/lib/swift/linux/libdispatch.so
fooPackag 6188 root  mem       REG   0,62  1025864 8013393 /usr/lib/swift/linux/libswift_Concurrency.so
fooPackag 6188 root  mem       REG   0,62  9180824 8013356 /usr/lib/swift/linux/libFoundationEssentials.so
fooPackag 6188 root  mem       REG   0,62    71504 8013354 /usr/lib/swift/linux/libBlocksRuntime.so
fooPackag 6188 root  mem       REG   0,62   108624 8013385 /usr/lib/swift/linux/libswiftGlibc.so
fooPackag 6188 root  mem       REG   0,62  3999904 8013357 /usr/lib/swift/linux/libFoundationInternationalization.so
fooPackag 6188 root  mem       REG   0,62   439384 8013389 /usr/lib/swift/linux/libswiftSwiftOnoneSupport.so
fooPackag 6188 root  mem       REG   0,62  2325976 8013360 /usr/lib/swift/linux/libTesting.so
fooPackag 6188 root  mem       REG   0,62   203968 7999553 /usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1
fooPackag 6188 root    0r     FIFO   0,14      0t0 1599074 pipe
fooPackag 6188 root    1w     FIFO   0,14      0t0 1599075 pipe
fooPackag 6188 root    2w     FIFO   0,14      0t0 1599076 pipe
fooPackag 6188 root    3u  a_inode   0,15        0      32 [eventfd:4]
fooPackag 6188 root    4u  a_inode   0,15        0      32 [eventpoll:3,5,18]
fooPackag 6188 root    5u  a_inode   0,15        0      32 [timerfd]
fooPackag 6188 root    6u  a_inode   0,15        0      32 [eventpoll:7,16]
fooPackag 6188 root    7u  a_inode   0,15        0      32 [eventfd:5]
fooPackag 6188 root    8r     FIFO   0,14      0t0 1593077 pipe
fooPackag 6188 root    9w     FIFO   0,14      0t0 1593077 pipe
fooPackag 6188 root   10r     FIFO   0,14      0t0 1593078 pipe
fooPackag 6188 root   11w     FIFO   0,14      0t0 1593078 pipe
fooPackag 6188 root   12r     FIFO   0,14      0t0 1593079 pipe
fooPackag 6188 root   13w     FIFO   0,14      0t0 1593079 pipe
fooPackag 6188 root   14r     FIFO   0,14      0t0 1593080 pipe
fooPackag 6188 root   15w     FIFO   0,14      0t0 1593080 pipe
fooPackag 6188 root   16u  a_inode   0,15        0      32 [timerfd]
fooPackag 6188 root   18u  a_inode   0,15        0      32 [eventfd:6]
# strace -p $(pgrep fooPac)
strace: Process 6188 attached
ppoll([{fd=4, events=POLLIN}], 1, NULL, NULL, 8

The main thread is blocked ppoll waiting.

weissi commented 1 week ago

Apple folks, rdar://139710145

weissi commented 1 week ago

One line repro

docker run -it --rm swift:6.0-noble bash -c 'mkdir /tmp/foo && cd /tmp/foo && swift package init && echo -e "import XCTest; final class MyTests: XCTestCase { func test_foo1() throws {}; func test_foo2() throws {}; func test_foo3() {}; func test_foo4() {}; func test_foo5() {}; func test_foo6() {}; func test_foo7() {}; func test_foo8() {} }" > Tests/fooTests/fooTests.swift && while swift test; do date; done'

output

$ docker run -it --rm swift:6.0-noble bash -c 'mkdir /tmp/foo && cd /tmp/foo && swift package init && echo -e "import XCTest; final class MyTests: XCTestCase { func test_foo1() throws {}; func test_foo2() throws {}; func test_foo3() {}; func test_foo4() {}; func test_foo5() {}; func test_foo6() {}; func test_foo7() {}; func test_foo8() {} }" > Tests/fooTests/fooTests.swift && while swift test; do date; done'
Creating library package: foo
Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/foo/foo.swift
Creating Tests/
Creating Tests/fooTests/
Creating Tests/fooTests/fooTests.swift
Building for debugging...
[26/26] Linking fooPackageTests.xctest
Build complete! (4.49s)
Test Suite 'All tests' started at 2024-11-12 11:30:24.051
Test Suite 'debug.xctest' started at 2024-11-12 11:30:24.052
Test Suite 'MyTests' started at 2024-11-12 11:30:24.052
Test Case 'MyTests.test_foo1' started at 2024-11-12 11:30:24.052
Test Case 'MyTests.test_foo1' passed (0.0 seconds)
Test Case 'MyTests.test_foo2' started at 2024-11-12 11:30:24.052

[... hang forever ...]
weissi commented 1 week ago

I'm reproing this in Docker for Mac, native architecture, both Intel and Apple Silicon.

Higher chance of repro:

docker run -it --rm swift:6.0-noble bash -c 'mkdir /tmp/foo && cd /tmp/foo && swift package init && echo -e "import XCTest; final class MyTests: XCTestCase { func test_foo1() throws {}; func test_foo2() throws {} }" > Tests/fooTests/fooTests.swift && swift test && while .build/debug/fooPackageTests.xctest; do date; done'

This reproduces from Swift 5.6 on, Swift 5.5 and older are okay.

weissi commented 1 week ago

kernel versions:

Linux 3cd2665b1108 6.10.4-linuxkit #1 SMP PREEMPT_DYNAMIC Wed Oct  2 16:39:54 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

and

Linux e7c7d1145331 6.10.4-linuxkit #1 SMP Mon Aug 12 08:47:01 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux