Assertion failure in NSUndoManager on macosx

I have run into an issue on macosx with a project that launches a separate thread to open a window (TGMainFrame) and run the GUI. The system works fine on Linux, but on OS X, the TApplication uses the Cocoa system. Cocoa now has a requirement that the main event loop be run from the first thread. It would be nice if there was a way to specify to not use Cocoa, but just X-11 for this. I realize I could recompile ROOT with cocoa support disabled, but that seems like overkill. Moving this to the “first” thread is not much of an option since it would require a lot of refactoring of the code and OS X is really just a convenient development platform. Linux is what it needs to run on. Maybe there is already another way to do this, but I did not see it.

Below is some code foe a simple program that demonstrates this. The full error message follows that.

----- SConstruct -----------------------------------------------

import subprocess

env = Environment()

subprocess.call(['rootcling', '-f', 'MyMainFrame_Dict.cc', 'MyMainFrame.h'])
env.AppendUnique(CPPFLAGS=subprocess.check_output('root-config --cflags', shell=True).split())
env.AppendUnique(LINKFLAGS=subprocess.check_output('root-config --glibs', shell=True).split())

env.Program(source=['MyMainFrame_Dict.cc', 'test.cc'], target='test')

----- MyMainFrame.h -----------------------------------------------

#ifndef __MYMAINFRAME_H_
#define __MYMAINFRAME_H_

#include <iostream>
using namespace std;

#include <TApplication.h>
#include <TGFrame.h>
#include <TGButton.h>
class MyMainFrame:public TGMainFrame{
	public:
		MyMainFrame(const TGWindow *p, UInt_t w, UInt_t h, bool build_gui):TGMainFrame(p,w,h, kMainFrame | kVerticalFrame){
		
			TGTextButton *bQuit = new TGTextButton(this, "Quit  ");
			this->AddFrame(bQuit, new TGLayoutHints(kLHintsRight,2,2,2,2));
			bQuit->Connect("Clicked()", "TApplication", gApplication, "Terminate()");
			MapSubwindows();
			MapWindow();
		}
	ClassDef(MyMainFrame, 1)
};

#endif // __MYMAINFRAME_H_

----- test.cc -----------------------------------------------

#include <iostream>
#include <thread>
using namespace std;

#include "MyMainFrame.h"

void MakeGui(void)
{
	int narg = 0;
	TApplication app("Test Gui", &narg, NULL);
	
	new MyMainFrame(gClient->GetRoot(), 400, 200, true);

	app.Run();
}

int main(int narg, char *argv[])
{
	//MakeGui();                                // This works fine
	thread mythr(MakeGui);  mythr.join();   // This crashes

	return 0;
}

----- ERROR MESSAGE-----------------------------------------------

>./test
2017-03-18 11:27:52.665 test[48239:21248491] *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1259/Misc.subproj/NSUndoManager.m:359
2017-03-18 11:27:52.666 test[48239:21248491] +[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.
2017-03-18 11:27:52.667 test[48239:21248491] (
	0   CoreFoundation                      0x00007fff9745a4f2 __exceptionPreprocess + 178
	1   libobjc.A.dylib                     0x00007fff864c673c objc_exception_throw + 48
	2   CoreFoundation                      0x00007fff9745f1ca +[NSException raise:format:arguments:] + 106
	3   Foundation                          0x00007fff8e05d856 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 198
	4   Foundation                          0x00007fff8dfe2af1 +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 170
	5   AppKit                              0x00007fff8d20ce22 -[NSApplication run] + 844
	6   libCore.so                          0x00000001033d04fe _ZN13TMacOSXSystem15InitializeCocoaEv + 174
	7   libGCocoa.so                        0x000000011446c485 _ZN7TGCocoaC2EPKcS1_ + 373
	8   libGCocoa.so                        0x0000000114478a12 _ZN8TGQuartzC2EPKcS1_ + 18
	9   ???                                 0x0000000103e7a081 0x0 + 4360478849
	10  libCling.so                         0x0000000104812424 _ZNK14TClingCallFunc4execEPvS0_ + 6068
	11  libCling.so                         0x00000001048130f4 _ZNK14TClingCallFunc23exec_with_valref_returnEPvPN5cling5ValueE + 260
	12  libCling.so                         0x00000001048145b7 _ZN14TClingCallFunc5ExecTIlEET_Pv + 87
	13  libCore.so                          0x00000001033afb04 _ZN11TMethodCall7ExecuteEPvRl + 84
	14  libCore.so                          0x00000001032e326f _ZN14TPluginHandler14ExecPluginImplIJPKcS2_EEElDpRKT_ + 207
	15  libCore.so                          0x00000001032df61b _ZN12TApplication16LoadGraphicsLibsEv + 619
	16  libCore.so                          0x00000001032ddfbb _ZN12TApplication18InitializeGraphicsEv + 59
	17  libCore.so                          0x00000001032ddd8e _ZN12TApplicationC2EPKcPiPPcPvi + 942
	18  test                                0x0000000102edd56e _Z7MakeGuiv + 62
	19  test                                0x0000000102ede0f6 _ZNSt3__114__thread_proxyINS_5tupleIJPFvvEEEEEEPvS5_ + 390
	20  libsystem_pthread.dylib             0x00007fff877dd99d _pthread_body + 131
	21  libsystem_pthread.dylib             0x00007fff877dd91a _pthread_body + 0
	22  libsystem_pthread.dylib             0x00007fff877db351 thread_start + 13
)
2017-03-18 11:27:52.667 test[48239:21248491] *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1259/Misc.subproj/NSUndoManager.m:359
2017-03-18 11:27:52.668 test[48239:21248491] An uncaught exception was raised
2017-03-18 11:27:52.669 test[48239:21248491] +[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.
2017-03-18 11:27:52.669 test[48239:21248491] (
	0   CoreFoundation                      0x00007fff9745a4f2 __exceptionPreprocess + 178
	1   libobjc.A.dylib                     0x00007fff864c673c objc_exception_throw + 48
	2   CoreFoundation                      0x00007fff9745f1ca +[NSException raise:format:arguments:] + 106
	3   Foundation                          0x00007fff8e05d856 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 198
	4   Foundation                          0x00007fff8dfe2af1 +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 170
	5   AppKit                              0x00007fff8d20cebe -[NSApplication run] + 1000
	6   libCore.so                          0x00000001033d04fe _ZN13TMacOSXSystem15InitializeCocoaEv + 174
	7   libGCocoa.so                        0x000000011446c485 _ZN7TGCocoaC2EPKcS1_ + 373
	8   libGCocoa.so                        0x0000000114478a12 _ZN8TGQuartzC2EPKcS1_ + 18
	9   ???                                 0x0000000103e7a081 0x0 + 4360478849
	10  libCling.so                         0x0000000104812424 _ZNK14TClingCallFunc4execEPvS0_ + 6068
	11  libCling.so                         0x00000001048130f4 _ZNK14TClingCallFunc23exec_with_valref_returnEPvPN5cling5ValueE + 260
	12  libCling.so                         0x00000001048145b7 _ZN14TClingCallFunc5ExecTIlEET_Pv + 87
	13  libCore.so                          0x00000001033afb04 _ZN11TMethodCall7ExecuteEPvRl + 84
	14  libCore.so                          0x00000001032e326f _ZN14TPluginHandler14ExecPluginImplIJPKcS2_EEElDpRKT_ + 207
	15  libCore.so                          0x00000001032df61b _ZN12TApplication16LoadGraphicsLibsEv + 619
	16  libCore.so                          0x00000001032ddfbb _ZN12TApplication18InitializeGraphicsEv + 59
	17  libCore.so                          0x00000001032ddd8e _ZN12TApplicationC2EPKcPiPPcPvi + 942
	18  test                                0x0000000102edd56e _Z7MakeGuiv + 62
	19  test                                0x0000000102ede0f6 _ZNSt3__114__thread_proxyINS_5tupleIJPFvvEEEEEEPvS5_ + 390
	20  libsystem_pthread.dylib             0x00007fff877dd99d _pthread_body + 131
	21  libsystem_pthread.dylib             0x00007fff877dd91a _pthread_body + 0
	22  libsystem_pthread.dylib             0x00007fff877db351 thread_start + 13
)
2017-03-18 11:27:52.669 test[48239:21248491] *** 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                      0x00007fff9745a4f2 __exceptionPreprocess + 178
	1   libobjc.A.dylib                     0x00007fff864c673c objc_exception_throw + 48
	2   CoreFoundation                      0x00007fff9745f1ca +[NSException raise:format:arguments:] + 106
	3   Foundation                          0x00007fff8e05d856 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 198
	4   Foundation                          0x00007fff8dfe2af1 +[NSUndoManager(NSPrivate) _endTopLevelGroupings] + 170
	5   AppKit                              0x00007fff8d20cebe -[NSApplication run] + 1000
	6   libCore.so                          0x00000001033d04fe _ZN13TMacOSXSystem15InitializeCocoaEv + 174
	7   libGCocoa.so                        0x000000011446c485 _ZN7TGCocoaC2EPKcS1_ + 373
	8   libGCocoa.so                        0x0000000114478a12 _ZN8TGQuartzC2EPKcS1_ + 18
	9   ???                                 0x0000000103e7a081 0x0 + 4360478849
	10  libCling.so                         0x0000000104812424 _ZNK14TClingCallFunc4execEPvS0_ + 6068
	11  libCling.so                         0x00000001048130f4 _ZNK14TClingCallFunc23exec_with_valref_returnEPvPN5cling5ValueE + 260
	12  libCling.so                         0x00000001048145b7 _ZN14TClingCallFunc5ExecTIlEET_Pv + 87
	13  libCore.so                          0x00000001033afb04 _ZN11TMethodCall7ExecuteEPvRl + 84
	14  libCore.so                          0x00000001032e326f _ZN14TPluginHandler14ExecPluginImplIJPKcS2_EEElDpRKT_ + 207
	15  libCore.so                          0x00000001032df61b _ZN12TApplication16LoadGraphicsLibsEv + 619
	16  libCore.so                          0x00000001032ddfbb _ZN12TApplication18InitializeGraphicsEv + 59
	17  libCore.so                          0x00000001032ddd8e _ZN12TApplicationC2EPKcPiPPcPvi + 942
	18  test                                0x0000000102edd56e _Z7MakeGuiv + 62
	19  test                                0x0000000102ede0f6 _ZNSt3__114__thread_proxyINS_5tupleIJPFvvEEEEEEPvS5_ + 390
	20  libsystem_pthread.dylib             0x00007fff877dd99d _pthread_body + 131
	21  libsystem_pthread.dylib             0x00007fff877dd91a _pthread_body + 0
	22  libsystem_pthread.dylib             0x00007fff877db351 thread_start + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort

Yes my suggestion would be to install ROOT with X11 backend. I will ask the author of the Cocoa backend to comment your post. May be he has a better solution.

Yes, Cocoa/NSApplication works in the main GUI thread and if you need a UI on mac you have to create it/use it in the main thread (the assert you have clearly explains this). There is no way (and, honestly, no reason) to build a ‘hybrid’ version of ROOT (“use X11 in a background thread, but Cocoa in the main thread”), so you either re-structure the code or build X11 version.

Fair enough. But just to be clear, I don’t need a hybrid program. The main program is a command line only program with no GUI at all. The GUI only comes into existence if this particular plugin happens to be attached. What I was hoping for was some way of specifying that the entire application use the X11 interface rather than Cocoa. The only way I know to do this now is to recompile ROOT so that all applications use only X11 and that is not what I want.

The code you’ve attached is creating a GUI, so I see a contradiction in the statement “The main program is a command line only”. Anyway, you can run ROOT in a batch mode, TMacOSXSystem will fall-back TUnixSystem::DispatchOneEvent then.

Sorry if I’m being confusing. Let me try once more to describe the situation:

  1. I have a program that processes (physics) events obtained from an experiment. It has nothing to do with ROOT and processes the data happily.

  2. I wish to do some diagnostics on the program so I have a plugin that can be used to control the physics event processing of the main program when attached. Much like a debugger. This can pause event processing, step through events one at a time, and examine the reconstructed data objects of the event.

  3. This “diagnostic” plugin has a GUI interface that allows users to more easily step through and examine events. The GUI happens to be written in ROOT.

  4. Due to the nature of the reconstruction framework of the program in 1., the plugin code is not run in the main thread, but in a separate processing thread. Thus, I can’t access the main thread from the plugin. At the same time, the main thread does not implement TApplication or any other class that would handle things like mouse clicks.

I’ve dug a little deeper in the ROOT code. I think what I would like to do is something like add this line just before I create TApplication in my plugin (if I sense it is Mac OS X):

gEnv->SetValue(“Gui.Backend”, “x11”);

When I try that though, it complains that GX11 can’t be found. I’ve tried explicitly adding --enable-x11 and --enable-cocoa when building ROOT, but this library still isn’t there. If I explicitly disable cocoa, then it works. If I then re-enable cocoa, but leave the libGX11 library installed, it finds the library, but gives me this error:

Error in TClass::LoadClassInfo: no interpreter information for class TGX11 is available even though it has a TClass initialization routine.
Error in TPluginHandler::SetupCallEnv: method TGX11 not found in class TGX11

So I guess what I would like is a ROOT build that can support both X11 and Cocoa so I can choose to run a process in either “Cocoa mode” or “X11 mode”.