jimm@amiga.UUCP (Jim Mackraz) (12/13/85)
Hello net.micro.amiga, and thank you John Draper for forwarding the
cause of IntuiEducation. Dale Luck posted a comment on John's main
processing loop, but his correction itself was slightly in error.
Furthermore, John commits another common error in his example, duplicated
below.
EXAMPLE 1 ************ John D's example
for (;;)
{
if (message = (struct IntuiMessage *)GetMsg(w->UserPort)) {
MessageClass = message->Class;
code = message->Code;
ReplyMsg(message);
switch (MessageClass) {
case GADGETUP :
case GADGETDOWN : do_gadgets(message, w);
break;
case CLOSEWINDOW : close_things();
exit(0);
break;
case MOUSEBUTTONS: break;
} /* Case */
} /* if */
} /* for */
COMMENTS ********
As Dale pointed out, a polling loop on the message port is not a proper
multitasking way to behave. A more subtle error: note that the message is
replied to before its contents is used in do_gadgets(). A general
principle (metaphor by bobp) applies: A Message is a license to use a
portion of the sender's data space, which no longer applies after the
message is Reply'd. In this case, Intuition will reuse the message, its
contents will change, and do_gadgets() may be confused, or crashed, if this
happens.
** IMPORTANT ****
I personally like the way John caches the code of the message, and replies
as soon as possible. This might prevent Intuition from allocating another
message for this window if it needs to send another, but the savings are
marginal. With the exception of the very dangerous MENUVERIFY, REQVERIFY,
and SIZEVERIFY messages, it doesn't really matter much if you Reply()
before or after you use the data in the message. HOWEVER, be careful about
your caching. In particular, IAddress is not guaranteed to hold a
reasonable value if it is not defined for the particular class of message.
So if you try to stash the Gadget ID
((struct Gadget *)message->IAddress)->GadgetID
at the same place John stashes the class and code, and if the class is not
GADGETUP or GADGETDOWN, you may get an address error (Guru Meditation
00000003.<your task here>). Check the Intuition Manual for the exact times
that IAddress is used and therefore guaranteed vaild.
EXAMPLE 2 ************ Dale's correction to busy loop
for (;;)
{
Wait(1<<w->UserPort->mp_SigBit); /* wake up when message there */
if (message = (......
etc.
}
COMMENTS *******
This demonstrates the spirit of the block-until-message approach, but makes
a subtle error: Messages queue, while Signals do not. Therefore, if two
messages arrive while you are in the body of the loop, when you Wait() you
will not sleep (signal has already been posted with the arrival of the
messages), but will process only one message. Next time you Wait(), you
will sleep although there is a message in the queue.
EXAMPLE 3 ************ What I use.
FOREVER /* define as for(;;) in intuition.h */
{
if ((msg = (struct IntuiMessage *)GetMsg(window->UserPort)) == NULL)
{
Wait(1<<window->UserPort->mp_SigBit);
continue;
}
...... etc. ...
}
COMMENTS ***** You may feel free to use any flow of control you like,
of course. I think I chose this one because it was different than the
first solution I saw.
GENERAL ***** I want to thank John again for his contributions. Unless I
missed something, the remainder of his posting was accurate and clear.
I'm the new guy at Amiga Software, and my responsibilities include
maintenance and enhancement of Intuition. I welcome all questions and
suggestions and comments. We have trouble mailing through ARPAnet
gateways, so include your phone number or USnail address, if you might
want a direct reply.
It is my hope that I can also provide some examples for new Intuition
and Amiga application programmers, but time ..., you know.
{hplabs,decwr}!pyramid!amiga!jimm