17

I often see this line in the Stack Trance when trying to track a memory leak in Xcode Instruments:

thunk for @escaping @callee_guaranteed () -> ()

What does it mean? I can't even translate thunk word, not to mention its technical meaning in this context. The full stack trace looks like this:

0 libsystem_malloc.dylib calloc
1 libobjc.A.dylib weak_resize(weak_table_t*, unsigned long)
2 libobjc.A.dylib weak_register_no_lock
3 libobjc.A.dylib objc_storeWeak
4 SpriteKit -[SKNode(setParent) setParent:]
5 SpriteKit -[SKNode insertChild:atIndex:]
6 SpriteKit -[SKNode addChild:]
7 IOSTest PieceNode.setup() /.../PieceNode.swift:66
8 IOSTest LabeledPieceNode.setup() /.../PieceNode.swift:86
9 IOSTest closure #1 in closure #1 in MaskedRectBoardNodeController.maskedRectBoard(_:didFill:with:alongGravity:) /.../MaskedRectBoardNodeController.swift:48
10 IOSTest thunk for @escaping @callee_guaranteed () -> () /.../<compiler-generated>:0
11 libdispatch.dylib _dispatch_call_block_and_release
12 libdispatch.dylib _dispatch_client_callout
13 libdispatch.dylib _dispatch_main_queue_callback_4CF$VARIANT$mp
14 CoreFoundation __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
15 CoreFoundation __CFRunLoopRun
16 CoreFoundation CFRunLoopRunSpecific
17 GraphicsServices GSEventRunModal
18 UIKitCore UIApplicationMain
19 IOSTest main /.../PauseMediator.swift:13
20 libdyld.dylib start
kelin
  • 11,323
  • 6
  • 67
  • 104

1 Answers1

23

A thunk generally is a box around a delayed function call (possibly adding some context, and possibly requiring additional context to complete). In Swift, thunks are generally used to help manage memory or calling conventions around a closure. As a rule, you can ignore the thunk; it's a bit of an implementation detail.

What this is really telling you is that you're leaking an SKNode somewhere, and that SKNode was created in a block dispatched to the main queue (probably using DispatchQueue.main.async). It is highly unlikely that this call stack actually has anything to do with the leak. It's just telling you where the leaked object was created.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • 1
    Thanks! The topic of this particular leak is mysterious and lies outside of this question. – kelin May 02 '19 at 19:36
  • So if we imagine that a closure is a structure, then it will contain a `thunk` and `functionPointer`, right? – kelin May 02 '19 at 19:38
  • 1
    The thunk generally would *be* that structure. A closure is often implemented as a thunk, but it doesn't have to be. For example, if a closure is executed immediately, we may be able to avoid creating a thunk at all. This discussion from C++ has some nice explanations of one way that thunks can be implemented (but you should take this discussion as illustrative from one language, not a definition): https://stackoverflow.com/questions/2641489/what-is-a-thunk – Rob Napier May 02 '19 at 19:41
  • Thunks are very common in lazily-evaluated functional languages. If I have some function that returns a lazy sequence, it might return `[(thunk)]`. If I evaluate the first element, it might be replaced with `[firstElement, (thunk)]`, etc. The point of the thunk is to hold a computation (including all the context required for that computation) that may be required later. – Rob Napier May 02 '19 at 19:43
  • 1
    First time I've heard about a "thunk" in this context, despite my user name. Thanks to @RobNapier for the simple explanation. – Rethunk Jan 20 '21 at 15:20