在OS X上运行-两个库调用系统函数

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:

  1. starts a goroutine or goroutines that run what previously was in main.Main;
  2. enters a message loop receiving messages to take certain main-thread actions on one or more channels

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.