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

Artificial intelligence loop - prevent the application from not responding

Hello there! I'm trying for fun and for knowledge to build up a simple "bot" which takes informations from the environment (and thus not only from the application itself).

The first attempt to build it was to create a "mainLoop", containing some conditions with sub-loops, going into loop via
- (BOOL) mainLoop {
loop:;
if(/* conditions to exit */) return NO;
// my conditions with subloops
goto loop;
return YES; // will never be cast
}


The problem is that as soon as I start the program, it will stop responding.
Is my approach wrong? How would you structure it?

Thanks

iMac 3.06Ghz, Mac OS X (10.6.2), MacBook late 2008

Posted on Dec 23, 2009 6:41 AM

Reply
17 replies

Dec 23, 2009 10:14 AM in response to elegos

I'll leave it to someone else to really answer your question, since I don't really know enough to do so, but I did want to suggest using a loop instead of a 'goto' statement. Everything you read will tell you that 'goto' statements aren't something you want to get in the habit of using, and you should avoid them at all costs, which is normally very easy, since there is always another way to achieve what you want to achieve without using a 'goto' statement. Try rewriting your code with a loop instead of with a 'goto' statement -- it won't affect the outcome, but it will make your code much more solid.

Also, if you do have to use a 'goto' statement, then you need to rewrite the second line of your code (the line with the 'goto' label). Right now you have it like this:


loop:;


but it should be like this:


loop:


Leave out the semicolon -- it shouldn't be there. I know the general rule is that all C statements end in semicolons, but 'goto' labels are the exception.

Hope that helps some. Best of luck with your program!

Dec 23, 2009 1:51 PM in response to elegos

I'm not sure if you're understanding the original objection. The idea is that goto statements should be avoided pretty much whenever possible...

A better architecture would be:

- (BOOL) mainLoop {
while( !/* conditions to exit */)
{
// my conditions with subloops
}
return NO;
}


As to your actual question, My guess is that you have some form of tight loop, so you are not giving up the process... try using a debugger, or some NSLog statements to trace where you are actually going when you run...

Dec 23, 2009 1:57 PM in response to elegos

elegos wrote:
Someone told me to use the system call "select"... is there any special function in Objective C? From what I've understand it will wait untill an event occours without stopping the program flow.


"select" is in Objective-C, but what you are asking for is fairly advanced. Perhaps you need to start with something a bit simpler just to get the feel of how it works. What you want to use eventually is a "run loop" but you normally don't even write one. You use the one that NSApplication provides.

Dec 24, 2009 4:43 AM in response to etresoft

Ok, I figured out, surfing the web, the problem is that an infinite-or-like loop will freeze the main thread, which is the one that drives the GUI (which must run smoothly to control all the things). So I have to create other threads, but I think I misunderstood something... this is the code:

Calling of the main loop:
// in "start" method, should detach a new thread, separated from the main one. BotController is the class within the mainLoop method is defined
[NSThread detachNewThreadSelector:@selector(mainLoop) toTarget:[BotController class] withObject:nil];


mainLoop method (same class):
- (void) mainLoop {
while (/* conditions of the bot */) {
if(/* condition one */) {
// should run -(void) secondLoop on the same thread of mainLoop
[self performSelector:@selector(secondLoop) onThread:[NSThread currentThread] withObject:nil waitUntilDone:YES];
}
else {
[self performSelector:@selector(thirdLoop:) onThread:[NSThread currentThread] withObject:nil waitUntilDone:YES];
}
}
}


The bot will express an error on the call of the mainLoop (and I think it may do the same on the others):
* -[NSThread initWithTarget:selector:object:]: target does not implement selector (* +[BotController mainLoop])


So, what's wrong with it? I tried to search the documentation for @selector, but haven't understood it very well I must admit.

Thanks 🙂

Dec 24, 2009 7:57 AM in response to elegos

Other people on this forum may disagree with me, but since you know C, I would start by experimenting with a few multi-threaded applications written in C first. Objective-C is a superset of C, so anything you write in C can easily be moved over to Objective-C later.

To start, I would look at these two examples:
http://en.wikipedia.org/wiki/Fork%28operating_system%29#Example_inC
http://en.wikipedia.org/wiki/Pthread#Example

The first one is an example with with multiple processes, the second with multiple threads. Try modifying one of the examples so that there is an infinite loop in one of the threads or processes and see what happens.

Dec 24, 2009 8:43 AM in response to Noah Robbin

Thank you, I've tested those codes and learnt something more about threads. Wanting an Objective C solution (just to learn this superset too, without having to port this code on other platforms), I found my error and resolved.

There was still a series of warnings about memory leaking, so surfing on the web I found what an NSAutoreleasePool is and I used it to do threaded stuff and release it at the end.

Now, threading my process, I came to another problem:
* __NSAutoreleaseNoPool(): Object 0x3174e0 of class MemoryAccess autoreleased with no pool in place - just leaking

^-- this and similar errors, like
* __NSAutoreleaseNoPool(): Object 0x30f600 of class NSCFString autoreleased with no pool in place - just leaking

I haven't written the MemoryAccess class, nor is present within my threaded code. I suspect that threading my process may lead to something unwanted within various threads? I'm not sure about that.


The question is: how to find where this class is missing of NSAutoreleasePools?


This is my actual code:
mainLoop evocation:
NSAutoreleasePool *mainLoopPool = [[NSAutoreleasePool alloc] init];
[NSThread detachNewThreadSelector:@selector(mainLoop) toTarget:self withObject:nil];
[mainLoopPool release];


mainLoop method:
- (void) mainLoop {
int nLoop = LOOP_NULL;
NSAutoreleasePool *subLoopPool;
while (/* loop conditions */) {
subLoopPool = [[NSAutoreleasePool alloc] init];
if(nLoop != LOOP_EVENT1 && /* event1 conditions */) {
NSLog(@"LOOP: Entering event1 Loop");
nLoop = LOOP_EVENT1;
[self performSelector:@selector(event1Loop) onThread:[NSThread currentThread] withObject:nil waitUntilDone:YES];
}
else if(nLoop != LOOP_EVENT2 && /* event2 conditions */) {
NSLog(@"LOOP: Entering event2 Loop");
nLoop = LOOP_EVENT2;
[self performSelector:@selector(event2Loop) onThread:[NSThread currentThread] withObject:nil waitUntilDone:YES];
[subLoopPool release];
}
[subLoopPool release];
}
}


For now event1/2Loop methods are just infinite while cycles with no functions evocated in but the conditional ones.

After a while the program crashes due to
Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x93720ed7 objc_msgSend + 23
1 com.apple.CoreFoundation 0x967f50ed _CFAutoreleasePoolPop + 253
...


There are something like 20 autoreleasepool errors, then the log "LOOP: Entering event1 loop"

Thanks.

Dec 24, 2009 9:46 AM in response to elegos

elegos wrote:
I have never programmed multi-threaded applications and have a little experience in OOP, but I know C., the basics of C++ (and PHP from which the OOP experience).


But you have never programmed for MacOS X before either.

Now please can you help me giving me hints and/or links and/or examples, without asking me rhetoric questions please?


It isn't rhetoric. I don't really know what you are attempting to do, but your approach is never going to work. You absolutely cannot do an even loop like this. MacOS X has a built-in even loop called NSRunLopp. That is what you should be using. Properly using multithreaded operations during a MacOS X run loop is quite difficult.

Your best course of action is to more fully explain exactly what kind of program you are trying to write. Then we can give you pointers on how to start it. So far, you have jumped into the deep end of the pool head first and you don't realize that you can't swim.

Dec 24, 2009 10:21 AM in response to etresoft

OK. I'm trying to have the following things running parallely:

1. a GUI which controls all the classes, variables and so on
2. a program controller which starts / stops some actions depending on the GUI variables. This must run continuously to continue checking the various variables that are changed by the other classes and the GUI. This should not affect the running of the GUI
3. the various actions of the classes, which should run in background, or not depending on their needs

If I don't multithread the application, the controller will allways hang the GUI (or at least this is what I know so far).

Dec 24, 2009 10:54 AM in response to elegos

Could you be more specific? This description would apply to virtually any program ever written. What does the program do?

The basic flow of a MacOS X Cocoa program (GUI or not) is as follows:
1) Create a run loop
2) Wait for a message
3) Process the message
4) Go to step 2

The "message" in question is exactly that, an Objective-C message. When you create the run loop, you tell if what kinds of messages you want to see. If you do nothing, you get a standard set of messages. You could do extra work such that a network connection or filesystem activity generates a message. One special message you get for free are messages from the "NSTimer" class. Using this class is a good way to simulate parallel processing while keeping your program simple.

In a normal program, there is only one thread. So step 3 above "process the message" has to execute quickly to avoid locking up the system. When that happens, you get a "beachball" and the operating system flags your application as "not responding".

If at all possible, you need to setup your one, main thread run loop to handle all the messages you will get. It can handle a great number of such messages. You just have to think in an event-driven way. Never call any function that could possibly block. There are better ways. If you explicitly tell us what you want this program to do, we can tell you how to setup your run loop to handle the messages you need.

In some situations, multiple threads are required. But they are quite difficult, even for experienced programmers. Considering that you don't have a great deal of MacOS X experience and you haven't clearly explained what your program needs to do, you need to stay far away from any sort of multithreading.

Dec 24, 2009 12:57 PM in response to elegos

I think you need to tackle one thing at a time. Think hard about what you are trying to accomplish.

Are you trying to learn programming on the Mac? If so, start with a simple application, forget about having multiple threads for now.

Are you trying to learn about multi-threaded programming? If so, stick with a language and platform that you know. Parallel operations are hard to get right, especially if you are also changing languages and platforms.

If you are trying to do both of those things, chose one of them to do first.

Dec 24, 2009 1:18 PM in response to etresoft

Ok, I'll try to be more specific.

This AI should handle variables to make decisions.

For example: if nothing is disturbing you, start the routine "walk" untill something (handled) happens. In that case, terminate what you're doing and check what changed. If you match one of the handled cases, execute that action, which may be "reply to a salute" or "avoid that black cat". You can configure this AI via a GUI, which has to be available in all the moments. The routines may cycle for a long time (i.e. the "walk" action may be made by subroutines like "follow the light", which should be handled with something like point-to-point navigation).

This is so the logic flow I thought first:

do a cycle untill I say you "stop" (from the GUI) or when something bad happens
- if (condition "seen friend") execute action "talk to friend"
- else if (condition "I am hungry") execute action "search for food"
- else execute action "walk"
end of cycle

So, the GUI runs on the main thread, which means I have to make the main thread stop-less, while the cycle must run on a separate level, because it will run untill the AI will run. Or this is my point of view, maybe it may be programmed in a different way, like this:

(main thread)
- create an event waiter for "event x", in case stop all running actions and run this action
- create an event waiter for "event y", in case stop all running actions and run this action

But should the action again be run on a different thread, in order not to freeze the GUI? This is my problem.

So... the concept is that actions must be taken one after another, in case interrupted, but the GUI must allways be accessible without any "lag", that is the time taken by the "background?" actions.

Have you understood my idea better now?

Thanks

Dec 24, 2009 5:11 PM in response to elegos

elegos wrote:
So... the concept is that actions must be taken one after another, in case interrupted, but the GUI must allways be accessible without any "lag", that is the time taken by the "background?" actions.

Have you understood my idea better now?


Yes, thank you. You have transformed this thread from "what is this person trying to do?" into "That sounds interesting".

It does look like you will need a separate thread to accomplish this so that your "agent" can run continuously. I think the best course of action is to keep things as simple as possible and to use the most basic built-in tools that Cocoa gives you.

Use the NSThread method "detachNewThreadSelector" to run a method on a separate thread. Have it look at some instance variable (such as an NSMutableDictionary) that contains various values that you want to share between the threads. Have your thread wrap each access to that shared data with "@synchronized" blocks. Do the same in your main thread in your event handlers when you change those values in response to user input. If you need the "agent thread" to access the main thread for any reason (such as to update a variable), it can call "performSelectorOnMainThread" to do that.

Artificial intelligence loop - prevent the application from not responding

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