I am finding it difficult to write something with more or less common UI at least for Mac. My application has to have tray icon and be able to show system notifications
The issue is the goroutines themselves. Any call to UI frameworks on Mac requires that the call is made from main thread, or at least in a thread-safe manner.
The issue arise when I am already running UI (well, for GUI application that is a must, no?) and try to show notification. The reason for this seems to be that systray package Init function has to be locked to main thread using runtime.LockOsThread
and never releases it. Then if I try to show notification which also requires runtime.LockOsThread
it causes following error:
2016-01-11 22:56:27.973 main[30162:4094392] *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1256.1/Misc.subproj/NSUndoManager.m:359
2016-01-11 22:56:27.974 main[30162:4094392] +[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.
2016-01-11 22:56:27.977 main[30162:4094392] (
0 CoreFoundation 0x00007fff8d42bae2 __exceptionPreprocess + 178
1 libobjc.A.dylib 0x00007fff8bb03f7e objc_exception_throw + 48
2 CoreFoundation 0x00007fff8d42b8ba +[NSException raise:format:arguments:] + 106
3 Foundation 0x00007fff8cb4c88c -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 198
4 Foundation 0x00007fff8cad24c1 +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 170
5 AppKit 0x00007fff8514206a -[NSApplication run] + 844
6 main 0x0000000004166200 nativeLoop + 128
7 main 0x0000000004165bca _cgo_8c6479959095_Cfunc_nativeLoop + 26
8 main 0x000000000405a590 runtime.asmcgocall + 112
)
2016-01-11 22:56:27.977 main[30162:4094392] *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1256.1/Misc.subproj/NSUndoManager.m:359
2016-01-11 22:56:27.978 main[30162:4094392] An uncaught exception was raised
2016-01-11 22:56:27.978 main[30162:4094392] +[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.
Is there a workaround that? All I could think of so far is to put UI and Notifications into separate binaries and make them to communicate with main over some sort of IPC. But I may be missing something.
It looks like the two libraries that you are using both correctly use runtime.LockOSThread
to make main-thread-only API calls; unfortunately, to use more than one such library, you'll have to do something fancier than the example code that either provides. You'll need to write your own main thread / main.Main
-invoked message loop that handles calls to multiple MTO APIs.
runtime.LockOSThread
is part of the solution to operating with APIs such as this; the golang wiki has a page about how to use it to interact with "call from main thread only" APIs.
An extremely short description of how your program should change:
You'll want to use runtime.LockOSThread
in main.init
to make sure that the main thread is running main.Main
; main.Main
should be refactored into two parts:
main.Main
;Since there is not enough traction on this question I've decided to post my own solution I found while trying to workaround this issue. This won't be marked as answer yet since someone else may provide better solution.
I have moved one of the UI processes (namely part that uses systray) into another binary and call it using cmd := exec.Command(...)
and cmd.Start()
then I pipe stdin
and stdout
and communicate with this child process through those.
The example code can be found on Github. Warning there is an error in this gist where after child exits main process will start burning through CPU cycles. Feel free to fix it yourself.
The reason why I did not want to go through with RPC is because this will become slightly too complex to what I want to achieve and does not provide easy way to do two way communication.