EXC_I386_GPFLT in usesNativeSwiftReferenceCounting_unowned

21 views
Skip to first unread message

Jean Suisse

unread,
Aug 19, 2015, 9:02:25 AM8/19/15
to swift-l...@googlegroups.com
Dear All,

I wrote a small benchmark Swift Application to test line drawing using CG API.

The app contains a custom NSView, which is responsible of holding the data & displaying them for the purpose of the test.
At launch, the application dispatches two closures to a concurrent queue. One requests a curve from the view [.xyCurve()] generates data points randomly, adds them to the view’s internal data source [appendPoint]. The other just very roughly measures the number of points per second graphed by the system in the test conditions.

Internally, the view uses NSLocks to:

– Ensure no points are added or removed while the view is refreshing (the view manages its data source and only keeps the N=1000 last data points)

– Ignore any [reloadData()] requests as long as the last request hasn’t been performed. As a result, the view does not refresh every time a point is added.

When the App is launched, I randomly get a EXC_I386_GPFLT error. Sometimes after a few seconds, sometimes after more than a minute.

Stack trace is always:

0: libswiftCore.dylib`usesNativeSwiftReferenceCounting_unowned(void const*):
1: libswiftCore.dylib`swift_unknownRetainUnowned:
2: MyApp.AppDelegate(applicationDidFinishLaunching(MyApp.AppDelegate)->(Objective.C.NSNotification)->()).(closure#1)
3:libdispatch.dylib`_dispatch_call_block_and_release:

Any suggestion on how to debug this?

Best Regards,
Jean






@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

@IBOutlet weak var window: NSWindow!
@IBOutlet weak var smallGraphicView: JSSmallGraphicView!

var i: UInt64 = 0

func applicationDidFinishLaunching(aNotification: NSNotification)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))
{
[unowned self] in
usleep(2000000)
var curve = self.smallGraphicView.xyCurve()

for (self.i = 1; self.i < UInt64.max-1; self.i++)
{
self.smallGraphicView.appendPoint(JSDataPoint(CGFloat(self.i) * 1E-15, CGFloat(random()) * -1E-15), toCurve: curve)
self.smallGraphicView.reloadData()
}
}

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))
{
[unowned self] in

while(true)
{
let startI = self.i
usleep(1000000)
let endI = self.i

print("\(endI-startI) pps\n")
}
}

}

func applicationWillTerminate(aNotification: NSNotification)
{
// Insert code here to tear down your application
}
}

Justin Kolb

unread,
Aug 19, 2015, 8:03:20 PM8/19/15
to Swift Language
From my experience this is due to usage of unowned self. You have to be 200% sure your block will outlive self or you will get this crash. It is usually better to use weak self in you blocks and then do "if let strongSelf = self {" and do your work inside this if statement using strongSelf instead of self. This guarantees that if you are inside the "if" the self will be kept alive by the strong reference. This tip applies to Objective-C also.

Jean Suisse

unread,
Aug 19, 2015, 10:16:10 PM8/19/15
to Justin Kolb, swift-l...@googlegroups.com
I did add a deinit method to the AppDelegate, the JSSmallGraphicView and to JS2DPoint. These are the three classes involved in the loop.
As expected, none of the deinit method is called before the crash BUT removing [unowned self] in at the beginning of the closures unexpectedly solves the issue…

The test project still crashes under Xcode 7.0 beta 4. So if there’s a bug, it’s still there. But I need to be sure before reporting it.




On 20 août 2015, at 03:25, Justin Kolb <frantica...@gmail.com> wrote:

The error you provided definitely points to Swift internal functions that are called when unowned references are used. There have been reported cases of unowned being buggy in older versions of Swift. I usually debug these kind of issues by putting a breakpoint in the deinit method of the object I think is getting dealloced. But if this is a compiler bug there might not be much you can do. I would still strongly suggest against using unowned in most code, especially if multiple threads are involved.

On Aug 19, 2015, at 7:10 PM, Jean Suisse <jean....@gmail.com> wrote:

In my case, self refers to the AppDelegate instance. It should live as long as the app is running, so I shouldn’t get this crash.
How can I investigate this?



On 20 août 2015, at 02:03, Justin Kolb <frantica...@gmail.com> wrote:

From my experience this is due to usage of unowned self. You have to be 200% sure your block will outlive self or you will get this crash. It is usually better to use weak self in you blocks and then do "if let strongSelf = self {" and do your work inside this if statement using strongSelf instead of self. This guarantees that if you are inside the "if" the self will be kept alive by the strong reference. This tip applies to Objective-C also.

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to swift-languag...@googlegroups.com.
To post to this group, send email to swift-l...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/45f6ecdc-8d6f-4158-a913-fa306075ed8d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Ken Ferry

unread,
Aug 20, 2015, 11:09:18 AM8/20/15
to Jean Suisse, Justin Kolb, swift-l...@googlegroups.com
Would it be possible to show the full project? This sounds more like an issue with unsafe threading in the test app than a Swift issue. 

For example, if setNeedsDisplay is not legal on a background thread. 

-ken



Jean Suisse

unread,
Aug 21, 2015, 4:06:15 PM8/21/15
to Ken Ferry, Justin Kolb, swift-l...@googlegroups.com
Dear Ken,

I boiled everything down to small working example. You can try to run the program several times. Usually, the issue appears after less than 30 seconds. It is random, thus most probably a race condition. I can’t figure it out though.

The app contains a custom view (JSSmallGraphicView) that owns a JS2DCurve containing data points (XY pairs). When the “test” button is clicked, the app runs two closures in global queues from the app delegate :

One requests the view to add points to the curve, then to redraw endlessly. The other one just computes how many redraw are made per second.

Redraws are requested by calling JSSmallGraphicView.reloadData(), which only invokes setNeedsDisplayInRect (on the main thread) if a redraw has not already been requested.
The view uses lock to protect accessing the curve to add points or to draw on screen and to protect the redrawRequested flag.

The app is just to benchmark a few parameters and should look like this:

SANDBOX_test.zip
PastedGraphic-3.png
PastedGraphic-2.png
Reply all
Reply to author
Forward
0 new messages