You can make a difference in the Apple Support Community!

When you sign up with your Apple Account, you can provide valuable feedback to other community members by upvoting helpful replies and User Tips.

Developer Forums relocated!

Need help with Apple Developer tools and technologies? Want to share information with other developers and Apple engineers? Visit Developer Forums at Apple.

Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

Event Loop problem

Lets say I have a multi-platform application object. It has a method called Run(). Run does NOT return until the application exists. I also have the carbon framework which has the CarbonApp with it's Run which also does not return. I have figured out that if I run the CarbonApp in a thread it does not get any messages (Task monitor shows it as red, not responding). I am now running my app as the thread, but I do not know the full ramifications of this, but it presents a problem.

When my object's Run() returns, the thread exits, but the main app still runs. How do I tell the main thread which is in its Run() to quit? I tried QuitApplicationEventLoop(); from the thread but it doesn't do anything. I just need a way to have the main app exit when my thread exists. Can someone explain to me how to do this?

There is another option, run my own event loop instead of using the default one. I have had limited success with this, because at sometime the event loop starts to spin. I copied some example code to use as my event loop, but as I said, it goes nuts sometimes. The "count" was added by me, but the rest is directly copied from some apple document somewhere.
void EventPump()
{
EventRecord theEvent;

int Count = 0;

// Cycle through all events
while (1 == GetNextEvent(everyEvent, &theEvent))
{
Count++;

// Sometimes we get in an infinit message loop. This will limit message proccessing
// to 100 messages max per call.
if ( 100 == Count )
{
break;
}

AEProcessAppleEvent(&theEvent);
}
}

Most of the time, for hours, sometimes for days, that works fine, but then Ill suddenly see my process at 99% cpu usage and stuck in this loop, and will eventually crash. The crash log is how I know where the problem is. Maybe there is a simple fix or someone can give me another simple event loop that will not lock up.

I need that or a way from my threaded app to cause the main event loop to exit when the thread exits. Any help is greatly appreciated.

EMac, Mac Mini, Mac OS X (10.4.6)

Posted on Jan 26, 2009 8:04 AM

Reply
19 replies

Jan 26, 2009 10:41 AM in response to etresoft

Can you give me a new one that doesn't block? Basically once a second pump all messages if there are any called from a alarm timer. Multi-threading without the multithreading.

OR

How can I set it up so when my thread terminates it causes the main thread to terminate? I am looking for examples, but they are all blocking code and even if they were not blocking I would need to exit the message pump to return to whatever was going on when the interrupt came through.

Jan 26, 2009 10:46 AM in response to TheSilverHammer

Let me clarify. Currently I am not using that run loop. Take your garden variety c++ carbon app and let it run its loop the way it normally does. In a thread, I start my app. At some point my app needs to quit, which is decided by the threaded app. It needs to cause the main app run loop to exit, thus stopping the entire application.

Jan 26, 2009 11:13 AM in response to TheSilverHammer

TheSilverHammer wrote:
Can you give me a new one that doesn't block? Basically once a second pump all messages if there are any called from a alarm timer. Multi-threading without the multithreading.


I'm afraid not. I know next to nothing about Carbon event handling. I even gave up on MacOS event handling before OS X came out - it was way too complicated even then. If you don't want to use Cocoa, then study up on Carbon Event handling. Basically, you don't write event loops anymore. There is even a section in the above document that explains the differences between Carbon Event Handling and the old WaitNextEvent. WaitNextEvent is similar to GetNextEvent. It allowed for multiprocessing using the Multifinder back in 1988.

How can I set it up so when my thread terminates it causes the main thread to terminate? I am looking for examples, but they are all blocking code and even if they were not blocking I would need to exit the message pump to return to whatever was going on when the interrupt came through.


I have no examples to give you that don't use Cocoa. You must use a modern event handling system and then this problem will likely just go away. Pay close attention to terminology. I really don't think you really mean "interrupt".

Let me clarify. Currently I am not using that run loop. Take your garden variety c++ carbon app and let it run its loop the way it normally does. In a thread, I start my app.


That is what I'm talking about with terminology. You don't start an app in a thread. Well, you can - but I'm pretty sure you aren't doing inter-process communication with your thread.

At some point my app needs to quit, which is decided by the threaded app. It needs to cause the main app run loop to exit, thus stopping the entire application.


Normally you would do this with a notification. I have no clue how to do notifications in Carbon. Call something like "CFNotificationCenterPostNotification" I suppose.

Jan 26, 2009 11:22 AM in response to etresoft

I have been reading all the carbon stuff on threading and all. There is an architectural problem here.

I have two functions which block. Both functions must run. Both functions should be able to cause the application to exit.

Function 1: The carbon event loop, their default one, the one you can't see.

Function 2: My application.

The problem is that my application is NOT event driven, however carbon apps are. There is an event I do want to catch, the AEQuitApplication event. That will cause function one to send a message to function 2 (method call) to terminate. That works. However, function 2 has no way that I know of to tell function 1 to quit. I am stuck there.

This is one approach. The other is to not use threading, but that means I need to write my own event pump function that will return when there are no events instead of blocking. I have not been able to figure that one out either.

Jan 26, 2009 11:50 AM in response to TheSilverHammer

TheSilverHammer wrote:
This is one approach. The other is to not use threading, but that means I need to write my own event pump function that will return when there are no events instead of blocking. I have not been able to figure that one out either.


There are still more options. Can you write this application as a C++ tool that runs only from the command line? It sounds like you are trying to shoehorn something in where it doesn't fit. If it isn't an event-driven application, don't try to make it one. Your main program can spawn your non-event app using fork/exec. Then, if main gets a quit event it can signal the child process and do waitpid until it is done.

That is just one example. You could also use any number of semaphores or Multiprocessing Events communicate.

Jan 26, 2009 12:00 PM in response to etresoft

Actually it is running from the command line, but launched automatically. However, its "lifetime" is that of a user session, so when a user logs out, I like to get the AEQuit message to exit. Now if I were to ignore that issue and just have some "main" with to carbon object, it shows up in Activity Monitor as not responding. It is running, but since it is not pumping messages, it gets that status. My current track is to create a timer via the official methods. This is working, but now that I can check for a flag being raised by the other function I am not sure what to do to quit. This function:

CFRunLoopStop(CFRunLoopGetCurrent());

In the timer call, doesn't seem to do anything. I keep getting the timer call and it keeps issuing that message and the app keeps running. I suppose I could do an exit() but that is rather harsh and does not allow for any resource cleanup.

Jan 27, 2009 9:15 AM in response to TheSilverHammer

Write a background only Cocoa app that can handle the quit event. When the Cocoa app starts, it fork/exec's your command line tool. When the Cocoa app gets the quit event it sends a SIGTERM signal to your command line app and then does waitpid on the child.

I don't know the details of your command-line app. It is always best to be a good citizen of whatever world you live in. If you are a pure UNIX tool, handle signals. If you are a Mac app, handle Apple Events using run loops. You can do this with threads too, but your thread must have some kind of loop (or frequent test) that checks an "is running" flag that some other thread or app can change asynchronously.

Jan 27, 2009 10:15 AM in response to etresoft

It started as a windows application. I am porting it to Mac OSX. It is actually a pair of applications. A client and server where the server has high privilege context, like a service on the windows side.

The client has it's main which does not return which causes the problem of using the default message handler. it does not return either. I can't run it on a timer, because the main will not return and I do not think good things would happen when a timer event never exits.

Currently I am running my client as a thread with 1 meg of stack space. I hope this does not break any of the API calls, being on a thread opposed to the main one. When the client exits, I needed to cause the whole application to exit in a graceful manner. I think I have this solved, although in a few days things might start blowing up again. For the client:
OSStatus MyThreadFunction(void* Para)
{
static ClientCore Core;
CoreRef = &Core;
Core.DoRun();

Running = false;

return 0;
}
static void MainEventLoop()
{
RgnHandle cursorRgn;
Boolean gotEvent;
EventRecord event;

cursorRgn = NULL;
while(Running)
{
// Get event and wait up to 1 second. The value is in 'ticks' and 60 ticks = 1 second.
gotEvent = WaitNextEvent(everyEvent, &event, 60, cursorRgn);

if (gotEvent)
{
if ( event.what == kHighLevelEvent )
{
AEProcessAppleEvent(&event);
}
}
}
}
So this pump will wait up to 1 second before going through the while condition. So at worst case, if the threaded Core.DoRun(); returns, The app will exit in 1 second later.

The server on the other hand does have a Run() method which returns rather quickly and is meant to be called every second. It has a similar event pump, and runs on the same thread.
static void MainEventLoop()
{
RgnHandle cursorRgn;
Boolean gotEvent;
EventRecord event;

cursorRgn = NULL;
while(true)
{
// Get event and wait up to 1 second. The value is in 'ticks' and 60 ticks = 1 second.
gotEvent = WaitNextEvent(everyEvent, &event, 60, cursorRgn);

if (gotEvent)
{
if ( event.what == kHighLevelEvent )
{
AEProcessAppleEvent(&event);
}
}
else
{
// Exit the MainEventLoop
break;
}
}
}
Server Loop:
while( true )
{
SCResult = PKC.Run();
if ( ( PKRESULTNORMAL == SCResult ) && ( false == Interupt) )
{

// The system console gripes about windows server stuff when there is
// no server. This may be the cause.
if ( true == MiscInfo::qqBool( MiscInfo::QISINTERACTIVE, false) )
{
MainEventLoop();
}
else
{
// Sincee were no using our event pump, we will use this to wait 1 second.
SLEEP(1000);
}
}
else
{
break;
}
} // End While

It also hooks into a bunch of the signals and is launched by launchd. I use the MainEventLoop function as my 1 second timer unless there is no user and the I use SLEEP(1000) which is a macroed version of a tcl_sleep function that uses MS instead of seconds.

Now a lot of people who have seen my various posts about this and that and keep wondering why don't I do it "The Apple Way". There are several reasons. 1. This app runs on multiple platforms: Windows, OSX, and Linux. This puts architectural restrictions on how I do things. I just can't re-write that client call that does not return and turn it into one that does. Id have to make big changes on a lot of platforms to do that.

This application, between the client and server has well over 200 source files. All written by me. Out of those 200 source files, I would guess that 190 of them are identical on all platforms. That means if I fix a bug or add a feature, every platform gets it fixed or added.

My code, unlike a lot of multi-platform code is not full of a bunch of #ifdef OSX or #ifdef WIN32, etc.. Of those kinds of #if defs, there is a grand total of 6 out of 200 source files on all platforms.

So I may seem stubborn or rigid, but I have my reasons. Certain things have to work a certain way otherwise it causes huge problems.

The problem here, which may be solved is I had two functions which did not return that HAD to be called. My worry on the client is that being in a thread some API functions might get wonky. Not mine, but apples. Take this for example:

Name = SCDynamicStoreCopyConsoleUser(NULL, NULL, NULL);

That is a totally legal way to call that function. And yes I did CFRelease(Name); so I was not leaking memory. That function could not be called once every second for more then 2 days without crashing. It crashed because it tried to allocate memory internally, didn't get it, and then wrote to it and boom. I put a try/catch around it. No go still blew up. This is what I mean by Apple APIs going whacky. I did solve the problem by doing what apple SHOULD have done, but didn't. Here is the solution that can run forever and never crash.
bool UserInfo::IsCurrentUserInteractive()
{
LOG(5, "UserInfo::IsCurrentUserInteractive Enter");

bool RetVal = true;
CFStringRef Name = NULL;
SCDynamicStoreContext *context = NULL;
SCDynamicStoreRef DStore = NULL;
bool StoreExcept = false;

try
{
DStore = SCDynamicStoreCreate(NULL, CFSTR("scClient"), NULL, context);
}
catch(...)
{
StoreExcept = true;
LOG(1, "UserInfo::IsCurrentUserInteractive SCDynamicStoreCreate threw an exeption");
}

// The local store may be null, but this is a valid input value for SCDynamicStoreCopyConsoleUser.
// of course it may have the same problem.
if ( NULL == DStore )
{
LOG(1, "UserInfo::IsCurrentUserInteractive SCDynamicStoreCreate returned NULL");
}
else
{
// This is just test code. I want to know if this condition is possible.
// I am certain it is not, but sometimes the Mac does weird things.
if ( true == StoreExcept )
{
LOG(1,"UserInfo::IsCurrentUserInteractive exception thrown, but value was returned.");
}
}

// This call has crashed before
try
{
Name = SCDynamicStoreCopyConsoleUser(DStore, NULL, NULL);
}
catch(...)
{
LOG(1, "UserInfo::IsCurrentUserInteractive SCDynamicStoreCopyConsoleUser threw an exception. Exit true");
}

if (NULL == Name)
{
RetVal = false;
}
else
{
// This is only done once, during object construction.
// The user name can not change without this program exiting and restarting.
if ( true == RefreshUserName )
{
RefreshUserName = false;

char *bytes = NULL;

bytes = (char *)CFStringGetCStringPtr(Name, kCFStringEncodingMacRoman);
if ( NULL != bytes )
{
mm_UserName = (const char *)bytes;
}
else
{
// It would be intresting to observe how often this happens.
// I am guessing for usernames, the above code will never return null.
LOG(2, "UserInfo::IsCurrentUserInteractive() CFStringGetCStringPtr returned null.");
char *Buffer = new char[1025];

bzero(Buffer, sizeof(Buffer));
if( true == CFStringGetCString(Name, Buffer, 1024, kCFStringEncodingMacRoman) )
{
mm_UserName = Buffer;
}

delete Buffer;
}
}
CFRelease(Name);
}

if (NULL != DStore)
{
CFRelease(DStore);
}

LOG(5, "UserInfo::IsCurrentUserInteractive Exit");

return RetVal;
}


I hope people can appreciate why I do things a certain way and not always the official Apple way.

Jan 27, 2009 12:36 PM in response to TheSilverHammer

TheSilverHammer wrote:
It started as a windows application. I am porting it to Mac OSX. It is actually a pair of applications. A client and server where the server has high privilege context, like a service on the windows side.


A daemon, in other words.

Now a lot of people who have seen my various posts about this and that and keep wondering why don't I do it "The Apple Way". There are several reasons. 1. This app runs on multiple platforms: Windows, OSX, and Linux. This puts architectural restrictions on how I do things. I just can't re-write that client call that does not return and turn it into one that does. Id have to make big changes on a lot of platforms to do that.


It isn't so much of an "Apple Way" or not. There are standard ways, on any platform, of accomplishing various things. Unless you have a very good reason, you should follow what "most people" are doing. Those methods have been developed over many years by people who really understand them. If you have a problem, there are resources that can be helpful. If you are using the same architecture and terminology as others, you can say things like "My multithreaded daemon isn't responding to signals. What did I do wrong?" and there will be many people who will understand.

This application, between the client and server has well over 200 source files. All written by me. Out of those 200 source files, I would guess that 190 of them are identical on all platforms. That means if I fix a bug or add a feature, every platform gets it fixed or added.

My code, unlike a lot of multi-platform code is not full of a bunch of #ifdef OSX or #ifdef WIN32, etc.. Of those kinds of #if defs, there is a grand total of 6 out of 200 source files on all platforms.


It certainly sounds like you are handling the multiplatform aspects correctly. I also hate those macros.

Certain things have to work a certain way otherwise it causes huge problems.


Exactly.

The problem here, which may be solved is I had two functions which did not return that HAD to be called. My worry on the client is that being in a thread some API functions might get wonky. Not mine, but apples. Take this for example:

Name = SCDynamicStoreCopyConsoleUser(NULL, NULL, NULL);

That is a totally legal way to call that function. And yes I did CFRelease(Name); so I was not leaking memory. That function could not be called once every second for more then 2 days without crashing. It crashed because it tried to allocate memory internally, didn't get it, and then wrote to it and boom. I put a try/catch around it. No go still blew up. This is what I mean by Apple APIs going whacky.


That is a problematic function that really deserves its own thread. To quote Apple Technical Q&A 1133, "routines like SCDynamicStoreCopyConsoleUser exist only for compatibility purposes. It's likely that they will be formally deprecated in a future version of Mac OS X." Basically, it doesn't fully work now and is destined to work even less well in the future. Your best bet is a change in architecture to get the same functionality. But again, this needs it own thread as it has nothing to do with event loops.

I hope people can appreciate why I do things a certain way and not always the official Apple way.


People do things the "non-official" way all the time. It is just that your "MainEventLoop" above really needs to go. It is giving you problems and there are so many better ways to do the same thing.

You are porting a Window service. That should be daemon. Your system does something with the logged in user information. You need a user-space app to talk to you daemon. This user-space app should be simple and 100% Mac. It will not impact any other platform. Maybe write an authorization plug-in or launchd user agent to get the user logged in.

Jan 27, 2009 3:27 PM in response to etresoft

The service is the daemon, but the above stuff (with exception of the service piece) is an agent. It is the one that has to run the message pump AND call a function that doesn't return until its time to quit. The server (daemon) is easier to deal with, its main function returns.

Now looking at the first case, the agent. If I do the classic carbon app.Run(); I get stuck because I can't call Core.Run() which also does NOT return until it is time to quit. So app.Run(); is off the table. All that magic code no one else has to think about, I have to think about. So I need a workaround. The only viable way is to use threads. To further complicate things, I can't run Core.Run() on the thread and let the main thread do its app.Run(); because there is no way for the threaded function to tell app.Run(); to quit.

My only choice is to have my own event pump so I can put in a flag to quit. Then apple documentation is schizophrenic at best. I am digging around and find something and try it and it seems to work, only it doesn't. Case and point, the link you provided on carbon event handling, thats where I got those event pumps. Seems great right? No notes about things being bad or wrong or dangerous. It looks like the official way to do that. So I use it and at some point I want to know more about one of the functions and say find selected text in API. I do, and it does and you know what it has that the web page doesn't have? A BIG WARNING that carbon event handling is a very old system and you should use some newer system. So the online help doesn't say jack about that, but the XCode local help does.

How on god's green earth are you to find the "Right" way to do something?

Cocoa: What the **** is that? Will it work with C++? What about objective C? I think of languages like Java, C#, C++, LUA, etc... They do not mix. You do not start writing C++ code in the middle of a java program. My code base is C++ and whatever I use MUST BE 100% COMPATIBLE.

I wish I had a direct line to Steve Jobs or whomever is running Apple now, I would certainly let them know how monumentally ** their entire API is. There are 10 ways to do the same thing with the Apple API, only 2 of them actually work.

And the documentation, oh my god. Man pages copied from BSD that totally DO NOT apply to Darwin. Apple API examples which are death traps. That initial event loop I started this thread with was CUT AND PASTE from their docs which bore NO WARNING about it being old and you shouldn't use it. Then the second example, web page A-OK, local docs "DANGER WILL ROBINSON".

I could go nuts and find all this cocoa stuff only be told, well that cocoa thing is know to be unstable here is a link to some obscure technical note about it. Is it in the docs? No, its in a forum post.

If Apple did for programmers what they do for end users, Microsoft would be ancient history. I find the whole CFRef thing insane. So these are references to objects that you call functions on. That is not object oriented! How is this better then just plain old functional C with no objects? Its not, its worse. An object can tell you what methods it has, a CFStringRef can't. You have dig up what you can do to it what functions you use to play with it. Oh, don't forget the create rule or get rule. You got to CFRelease on the right things, or you leak memory, but do it on the wrong thing and the entire system can crash! Hey real object just go out of scope and clean themselves up, but not our special APPLE objects.

Its funny, in C# everything is a reference, yet you can get methods from them, and if they go out of scope, they clean themselves up, but Apple CFRefs are fickle time bombs if not used the right way.

Dear Apple:
You are rich. You can hire a 100 people for a major project, right? Well get a set of qualified people to design the dream API. The API that programmers will LOVE to use. Work it out. Green Field it.

Then get some more people to build this API and test it to make sure it works. Finally get another set of people to create the documentation. Look at MSDN. Its pretty darn good, but it could be better. You make one MUCH better. Then look at your compiler. Demand a compiler that is in every way superior to Dev Studio 2008.

Now everyone will develop of Apple OS and port to Windows, because by comparison, windows ***** in every respect to OSX from both the user and developer point of view.

Jan 27, 2009 5:26 PM in response to TheSilverHammer

TheSilverHammer wrote:
The service is the daemon, but the above stuff (with exception of the service piece) is an agent. It is the one that has to run the message pump AND call a function that doesn't return until its time to quit. The server (daemon) is easier to deal with, its main function returns.


MacOS X is a complicated system running at multiple levels. You have to code to the appropriate level. Daemons should never assume that there is a user logged in, that there is a monitor plugged in, or that any apple events exist. It is very similar to a Windows service. In fact, a Windows service was designed to work like a daemon.

My only choice is to have my own event pump so I can put in a flag to quit. Then apple documentation is schizophrenic at best.


I won't disagree with that. You do need some background in MacOS to understand that some APIs are to be avoided. Plus, there are political and technical considerations. Politically, Apple can't say Carbon is dead, but it is. That doesn't mean that people shouldn't still use Carbon because not everything is possible in Cocoa. Carbon, however, should be an API of last resort.

How on god's green earth are you to find the "Right" way to do something?


You read documentation and ask questions.

Cocoa: What the **** is that?


It is the primary API for MacOS X.

Will it work with C++?


Yes

What about objective C?


Cocoa is based on Objective-C - more or less.

I think of languages like Java, C#, C++, LUA, etc... They do not mix. You do not start writing C++ code in the middle of a java program.


Those other languages have extensive runtime systems they depend on. Pretty much any compiled languages (C, C++, Objective-C, Pascal, etc.) lives at a lower level and are virtually interchangeable.

My code base is C++ and whatever I use MUST BE 100% COMPATIBLE.


Sorry, it isn't. It is about 98.2% compatible. However, there is a common thread of C underneath all of it.

I wish I had a direct line ... know to be unstable here is a link to some obscure technical note about it. Is it in the docs? No, its in a forum post.


MacOS X, Windows, and UNIX all have about 30 years of history behind them. Things evolve over time. They try to mark old documentation as legacy. Things aren't any different in Windows or UNIX.
UNIX is probably the most stable. Mac is in the middle. Nothing can touch Microsoft with Win32, MFC, COM, ATL, .NET, C#, VB, and more - all currently in use and supported.

I find the whole CFRef thing insane. So these are references to objects that you call functions on. That is not object oriented! How is this better then just plain old functional C with no objects? Its not, its worse. An object can tell you what methods it has, a CFStringRef can't. You have dig up what you can do to it what functions you use to play with it.


It is a C interface to data structures from Cocoa. It was only intended as a transitional layer to ease people into Cocoa.

Oh, don't forget the create rule or get rule. You got to CFRelease on the right things, or you leak memory, but do it on the wrong thing and the entire system can crash! Hey real object just go out of scope and clean themselves up, but not our special APPLE objects.


That is a C limitation rather than a Core Foundation limitation. At one time I (as I'm sure many others have) wrote a C++ wrapper around some core foundation objects so I could use things like C++'s scope to clean things up. In the end, I abandoned it because Objective-C++ was so much easier to use. I still do some things in C++, but they usually get replaced with Objective-C++ eventually.

Its funny, in C# everything is a reference, yet you can get methods from them, and if they go out of scope, they clean themselves up, but Apple CFRefs are fickle time bombs if not used the right way.


Don't use CFRefs then! If your code is 90% cross-platform, then it shouldn't have any CFRefs. Use Cocoa and garbage collection. Ask your Objective-C objects which methods they support.

Dear Apple:
You are rich. You can hire a 100 people for a major project, right? Well get a set of qualified people to design the dream API. The API that programmers will LOVE to use. Work it out. Green Field it.


Already done. It is called Cocoa.

Then look at your compiler. Demand a compiler that is in every way superior to Dev Studio 2008.


That is more difficult. Apple doesn't make compilers. They use GCC because it has such a huge installed base. As compilers go, it isn't all that great. The linker is a joke compared to MS. You can buy Intel's compiler and use that if you want. Microsoft makes a good compiler though. Can't win 'em all.

Event Loop problem

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.