i didn’t think this would compile, but it does:
import Atomics
extension HTTP
{
struct TimeCop:~Copyable, Sendable
{
let epoch:UnsafeAtomic<UInt>
init()
{
self.epoch = .create(0)
}
deinit
{
self.epoch.destroy()
}
}
}
let cop:HTTP.TimeCop = .init()
async
let _:Void =
{
try await cop.start(interval: .milliseconds(1000))
try await connection.channel.close()
}()
capturing this thing in a closure will end badly.
double free or corruption (fasttop)
the problem is that you can’t capture the noncopyable type in an escaping closure, and it needs to take it as a borrowing
parameter:
async
let _:Void =
{
(cop:borrowing HTTP.TimeCop) in
try await cop.start(interval: .milliseconds(1000))
try await connection.channel.close()
} (cop)
but why on earth did the capturing one compile in the first place?
2 Likes
jrose
(Jordan Rose)
October 28, 2023, 1:08am
2
Nice catch! Does it reproduce in a function body? It wouldn’t surprise me if this was a bug in top-level code handling. (My guess is it’s expecting to be able to move the value into the closure, but it fails to.)
it is in a function scope, i undented it to make it more readable for the forums.
it raises the expected error if i'm explicit about the capture with a [cop] in
.
jrose
(Jordan Rose)
October 28, 2023, 1:13am
4
See, that should be fine because captures of locals capture the variable, not the value. The value should still end up being destroyed exactly once.
2 Likes
i got this down to a minimal reproducer which i filed as a bug here:
opened 12:55AM - 30 Oct 23 UTC
bug
triage needed
the following test program crashes with a double free.
```swift
struct TimeC… op:~Copyable, Sendable
{
final
class Class:Sendable
{
init()
{
}
}
let x:Class
init()
{
self.x = .init()
}
func use()
{
}
deinit
{
}
}
@main
enum Main
{
static
func main() async throws
{
do
{
let cop:TimeCop = .init()
async
let _:Void =
{
cop.use()
try await Task.sleep(for: .milliseconds(50))
}()
}
}
}
```
```
$ swiftc -parse-as-library example.swift
$ ./example
```
```
💣 Program crashed: Bad pointer dereference at 0x0000000000000000
Thread 4 crashed:
0 0x0000000000000000
1 0x00007fcf6be4c29b bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1> >::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 122 in libswiftCore.so
2 0x00005598a378b9c9 TimeCop.deinit + 24 in example
3 0x00005598a378bcd5 __swift_async_resume_get_context + 36 in example
4 0x00007fcf6be4b8e0 _swift_release_dealloc + 15 in libswiftCore.so
5 0x00007fcf6be4c29b bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1> >::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 122 in libswiftCore.so
6 0x00005598a378bc2e static Main.main() + 93 in example
7 0x00005598a378c600 static Main.$main() in example
```
```
$ swift --version
Swift version 5.9.1 (swift-5.9.1-RELEASE)
Target: x86_64-unknown-linux-gnu
```
4 Likes