Skip to content

Weird issue #220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pjebs opened this issue Jan 24, 2017 · 16 comments
Closed

Weird issue #220

pjebs opened this issue Jan 24, 2017 · 16 comments

Comments

@pjebs
Copy link

pjebs commented Jan 24, 2017

I ran go get github.com/andlabs/ui/ and then ran go build . for the code found here: https://github.com/andlabs/ui/wiki/Update-your-UI-in-other-goroutine

The first time I ran it, it worked beautifully.

I clicked the max os x red close button and the window closed. I could see in the terminal that the process shut down correctly.

When I ran the program again (same way as before), the program doesn't run anymore. It displays this error in terminal:

 *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /SourceCache/Foundation/Foundation-1154/Misc.subproj/NSUndoManager.m:340
2017-01-24 18:08:36.541 Test[9371:126254] +[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.
2017-01-24 18:08:36.544 Test[9371:126254] (
	0   CoreFoundation                      0x00007fff974af03c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff9a1af76e objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff974aee1a +[NSException raise:format:arguments:] + 106
	3   Foundation                          0x00007fff935b999b -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
	4   Foundation                          0x00007fff9353b64f +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 156
	5   AppKit                              0x00007fff901f9e2e _DPSNextEvent + 2389
	6   AppKit                              0x00007fff901f8e58 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 346
	7   AppKit                              0x00007fff901eeaf3 -[NSApplication run] + 594
	8   Test                                0x000000000406cadd uiMain + 29
	9   Test                                0x000000000404e6c0 runtime.asmcgocall + 112
)
2017-01-24 18:08:36.545 Test[9371:126254] *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /SourceCache/Foundation/Foundation-1154/Misc.subproj/NSUndoManager.m:340
2017-01-24 18:08:36.547 Test[9371:126254] An uncaught exception was raised
2017-01-24 18:08:36.547 Test[9371:126254] +[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.
2017-01-24 18:08:36.548 Test[9371:126254] (
	0   CoreFoundation                      0x00007fff974af03c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff9a1af76e objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff974aee1a +[NSException raise:format:arguments:] + 106
	3   Foundation                          0x00007fff935b999b -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
	4   Foundation                          0x00007fff9353b64f +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 156
	5   AppKit                              0x00007fff901eec41 -[NSApplication run] + 928
	6   Test                                0x000000000406cadd uiMain + 29
	7   Test                                0x000000000404e6c0 runtime.asmcgocall + 112
)
2017-01-24 18:08:36.549 Test[9371:126254] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff974af03c __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x00007fff9a1af76e objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff974aee1a +[NSException raise:format:arguments:] + 106
	3   Foundation                          0x00007fff935b999b -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
	4   Foundation                          0x00007fff9353b64f +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 156
	5   AppKit                              0x00007fff901eec41 -[NSApplication run] + 928
	6   Test                                0x000000000406cadd uiMain + 29
	7   Test                                0x000000000404e6c0 runtime.asmcgocall + 112
)
libc++abi.dylib: terminating with uncaught exception of type NSException
SIGABRT: abort
PC=0x7fff93bcd286 m=3
signal arrived during cgo execution

goroutine 4 [syscall, locked to thread]:
runtime.cgocall(0x405b120, 0xc420023740, 0x0)
	/usr/local/go/src/runtime/cgocall.go:131 +0x110 fp=0xc420023710 sp=0xc4200236d0
github.com/andlabs/ui._Cfunc_uiMain()
	??:0 +0x41 fp=0xc420023740 sp=0xc420023710
github.com/andlabs/ui.start(0xc420054060, 0x40a1a40)
	/Developer/Projects/GOPATH/src/github.com/andlabs/ui/main.go:56 +0x19b fp=0xc4200237b0 sp=0xc420023740
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:2086 +0x1 fp=0xc4200237b8 sp=0xc4200237b0
created by github.com/andlabs/ui.Main
	/Developer/Projects/GOPATH/src/github.com/andlabs/ui/main.go:36 +0x6b

goroutine 1 [chan receive]:
github.com/andlabs/ui.Main(0x40a1a40, 0x23, 0x8)
	/Developer/Projects/GOPATH/src/github.com/andlabs/ui/main.go:37 +0xa1
main.main()
	/Users/PJ20/Desktop/Tools/Test/main.go:20 +0x2d

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:2086 +0x1

rax    0x0
rbx    0x6
rcx    0xb0102548
rdx    0x0
rdi    0xb03
rsi    0x6
rbp    0xb0102570
rsp    0xb0102548
r8     0x0
r9     0x7fff8fc13d60
r10    0x8000000
r11    0x206
r12    0xb01026d0
r13    0x4461000
r14    0xb0104000
r15    0xb01025b0
rip    0x7fff93bcd286
rflags 0x206
cs     0x7
fs     0x0
gs     0x0
logout

@pjebs
Copy link
Author

pjebs commented Jan 24, 2017

Maybe this article is helpful: http://nshipster.com/nsundomanager/

@andlabs
Copy link
Owner

andlabs commented Jan 24, 2017

The second time, my code to coerce OS X into thinking any thread other than the very first one created by the OS is main isn't working for some reason. This code is needed because OS X is dumb and cares about which thread was the first one created by the OS for no justifiable reason. I don't know why it isn't working the second time. I do know if you want a proper fix without the need for undocumented functions and hacks it will need to be taken up with the Go team.

Also I thought I locked the wiki to prevent outside edits.

@pjebs
Copy link
Author

pjebs commented Jan 24, 2017

So this is a Go issue? What is the go specific issue so I can file a bug report

@andlabs
Copy link
Owner

andlabs commented Jan 24, 2017

I don't know what the existing issue is an issue with. What I meant is that I do know that the correct, non-undocumented-call-workaround fix has to be done on the Go side: provide a way to lock a single goroutine and/or its cgo calls to m0 (which is the runtime's name for the first OS thread). Otherwise, workarounds like the one I have now will be necessary.

@pjebs
Copy link
Author

pjebs commented Jan 24, 2017

I'm primarily an iOS developer (which is similar to macOS). If we need code to run on the main thread, we can check it using: https://developer.apple.com/reference/foundation/thread and https://developer.apple.com/reference/foundation/thread/1408455-ismainthread

Could that be relevant for testing or atleast checking before running NSUndoManager

@pjebs
Copy link
Author

pjebs commented Jan 24, 2017

Also GCD: https://www.raywenderlich.com/60749/grand-central-dispatch-in-depth-part-1 has a function where you can call code guaranteed to be on main thread: dispatch_get_main_queue()

@andlabs
Copy link
Owner

andlabs commented Jan 24, 2017

You're thinking backwards: it's NSUndoManager that's using isMainThread to determine whether it is on the main thread. All of the things you named funnel down to the main CFRunLoop, which can be changed by using an undocumented function exported for the purposes of being usable by kextd; some posts on the Apple mailing lists have Apple engineers talking about it. I'm already using that function in the package (see link_darwin_amd64.go), so that's not why that message is coming back for you.

The problem is that OS X (and by extension iOS, watchOS, and tvOS) treats "the main thread" and "the first thread created by the OS when you fork() or start the program with Launch Services" as the same thing. On the other hand, on other systems, the main thread can be any thread you want, so long as you don't cross threads. I can spawn a thousand OS threads, then call CreateWindow() (Windows) or gtk_init() (GTK+) on the 1000th, and everything will still work.

Aside: there is a pthreads function pthread_main_np() in OS X that tells you if the current thread is that first OS thread. WebKit uses this directly, so in another project I needed to take more drastic measures that involve deep kernel features.

This normally doesn't really matter for most programmers, except that the Go runtime does not guarantee that goroutines map 1:1 to OS threads. Goroutines can move around at will, as the scheduler optimizes things to run as efficiently as possible. It's entirely possible that by the time ui.Main() is called, its goroutine is not running on the first OS thread anymore. And with init() being able to spawn goroutines now, it might be that your init() isn't running on the first OS thread anymore, but I'm not sure if that actually can happen with the current version of the Go runtime, and hence I'm not sure if this is the culprit of your problem. Go does provide a function runtime.LockOSThread(), which ties a goroutine to the OS thread it's on at the time of the call, but this can still be a different thread from the first OS thread.

I did realize something while writing this: if you're starting a goroutine before calling ui.Main(), and that goroutine is doing something else that starts Core Foundation going, that might cause the issue you're describing, for the reasons described above. (One possibility would be using crypto/x509, as the code that fetches system root certificates in a cgo build uses Security.framework.)

And for what it's worth package ui is not the only package subject to this problem — all Go GUI packages that run on OS X are, including shiny.

@pjebs
Copy link
Author

pjebs commented Jan 25, 2017

Thanks for the detailed reply.
However, I have run shiny example programs many times and never seen this issue before.

@andlabs
Copy link
Owner

andlabs commented Jan 26, 2017

And are you running ui samples or your own code?

@pjebs
Copy link
Author

pjebs commented Jan 26, 2017

ui samples

@paulocoutinhox
Copy link

I think the problem is with UI package:

go get github.com/andlabs/ui

ld: warning: object file (/Users/paulo/Developer/workspaces/go/src/github.com/andlabs/ui/libui_darwin_amd64.a(libui-combined.o)) was built for newer OSX version (10.11) than being linked (10.8)

@andlabs
Copy link
Owner

andlabs commented May 24, 2017

That's an unrelated problem with a linking error in libui that will be fixed in the next stable-ish release. If that fix does fix this, then there are things about OS X that I do not understand...

@paulocoutinhox
Copy link

Ok, if you can, post here that you update to it test. Ok?

@childeYin
Copy link

may be you can read this question https://groups.google.com/forum/#!topic/golang-nuts/E_5-kZnXi5o

@andlabs
Copy link
Owner

andlabs commented Sep 12, 2017

Thanks, but I have considered that approach when it was first devised — I'd rather not require installing a separate framework or using $DYLD_LIBRARY_PATH.

I want to propose a runtime.LockMainOSThread() or similar, but I'm not sure what the answer to the question "what if two goroutines call this?" would be.

As for the issue here, once I finally get the next libui out the door (whether it's an Alpha 0.3.1 with only a fix for a Windows bug or the Alpha 0.4) I can do more Heisenbug testing.

@andlabs
Copy link
Owner

andlabs commented Mar 26, 2018

Merged with #280 (which will also explain why the shiny examples worked).

andlabs/libui#308 will fix the built for newer SDK version issue.

@andlabs andlabs closed this as completed Mar 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants