[comp.sys.amiga.tech] Ultimate Wait-GetMsg strategy

shimoda@infohh.rmi.de (Markus Schmidt) (02/08/89)

Hi!

Every time using a Wait-GetMsg-pair I thought how to do it 
best way. Now I think I got it:

while (1) {
  while (!(imsg= GetMsg(win->UserPort)))
     Wait (1<<win->UserPort->mp_SigBit);
 ... select imsg->Class and break when ended ...

}

I think that should ship around all "oddities" like multiple
Messages arriving or NULL-msges arriving?!

Any comments?

Markus 

dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) (02/10/89)

:Every time using a Wait-GetMsg-pair I thought how to do it 
:best way. Now I think I got it:
:
:while (1) {
:  while (!(imsg= GetMsg(win->UserPort)))
:     Wait (1<<win->UserPort->mp_SigBit);
: ... select imsg->Class and break when ended ...
:
:}
:
:I think that should ship around all "oddities" like multiple
:Messages arriving or NULL-msges arriving?!
:
:Any comments?
:
:Markus 

	Yes, that's probably the worse way to do it.  Well, not the worse,
but it's not the best coding style I've seen (you asked for comments!).

	Use something like this if you are handling only a single source of
	events (i.e. one window's IDCMP):  Note that since all messages are
	processed before the next Wait(), you can safely do the Wait().

	while (notquit) {
	    Wait(1 << win->UserPort->mp_SigBit);
	    while (mess = GetMsg(win->UserPort)) {
		/* process message */
		ReplyMsg(mess);
	    }
	}

	On the otherhand, if you are waiting for multiple events, the logical
	extension works as well.  Again, the assumption is that you handle
	*everything* related to the signal bits in question before going back
	to the Wait().

	while (notquit) {
	    mask = Wait(mask1 | mask2 | mask3);

	    if (mask & mask1) {	/* say this is for your intuition window */
		register struct IntuiMessage *mess;
		while (mess = GetMsg(win->UserPort)) {
		    /* process all messages */
		    ReplyMsg(mess);
		}
	    }
	    if (mask & mask2) {
		/* etc... */
	    }
	    if (mask & mask3) {
		/* etc... */
	    }
	}

	The above works fine in the worst case:  (1) A message comes in while
we are in the GetMsg() loop, causing a (harmless) spurious signal, and (2)
A message comes in just after we exit the GetMsg() loop but before the Wait(),
in which case we still get a signal.   Also, note that the construction of
the code fragment allows you to easily declare very-local variables within
each if() block ... a much more modular coding practice as well as helpful
to the compiler's code generation.

	An example of INCORRECT code:

	while (notquit) {	/* INCORRECT CODE */
	    Wait(1 << win->UserPort->mp_SigBit);
	    msg = GetMsg(win->UserPort);
	    /* process */
	    ReplyMsg(msg);
	}

	Will NOT work because it is possible to get into two crash situations.
(1) Several messages get queued to you quickly (before Wait() can clear the
signal bit), then Wait() clears the signal bit, you process just one of the
messages and go back to the Wait() ... the Wait() locks up the computer until
the next message comes in even though there is already one left on the port.

(2) You get a spurious signal and GetMsg() returns NULL (no messages on the
port).  You must always check that GetMsg() returns a non-null message
pointer.  In the examples of correct code, I have a while (mess = GetMsg...)
and thus the while() takes care of this check.

				-Matt

rokicki@polya.Stanford.EDU (Tomas G. Rokicki) (02/10/89)

>:Now I think I got it:
>:while (1) {
>:  while (!(imsg= GetMsg(win->UserPort)))
>:     Wait (1<<win->UserPort->mp_SigBit);
>:  select imsg->Class and break when ended
>:}

This is *perfectly* fine.  while(1)/break is a general looping
construct.  while(notquit) is fine, too.

>	Yes, that's probably the worse way to do it.  Well, not the worse,
>but it's not the best coding style I've seen (you asked for comments!).

First, it's worst, not worse.  (What I hear about Berkeley producing
only bit pushers may be true.)  Secondly, his code *works*, and the
coding style is one I happen to agree with.  Matter of taste, Matt.

>	while (notquit) {
>	    Wait(1 << win->UserPort->mp_SigBit);
>	    while (mess = GetMsg(win->UserPort)) {
>		/* process message */
>		ReplyMsg(mess);
>	    }
>	}

Equivalent, *except* that you have a wait at the top that's not guarded
by a `while (GetMsg())' loop.  Fine, perhaps, depending on how you enter
the loop and what operations you allow inside the loop (what those
subroutines might do to the signals, you know) but conservative programming
techniques never hurt anyone.

>	while (notquit) {
>	    mask = Wait(mask1 | mask2 | mask3);
>
>	    if (mask & mask1) {	/* say this is for your intuition window */
>		register struct IntuiMessage *mess;
>		while (mess = GetMsg(win->UserPort)) {
>		    /* process all messages */
>		    ReplyMsg(mess);
>		}
>	    }
>	    if (mask & mask2) {
>		/* etc... */
>	    }
>	    if (mask & mask3) {
>		/* etc... */
>	    }
>	}

Not very fair; mask1 can dominate, causing problems later.  Something
like

int msgseen ;
while (notquit) {
   msgseen = 0 ;
   if (msg = GetMsg(port1)) {
      msgseen = 1 ;
   }
   if (msg = GetMsg(port2)) {
      msgseen = 1 ;
   }
   if (msg = GetMsg(port2)) {
      msgseen = 1 ;
   }
   if (! msgseen)
      Wait(mask1 | mask2 | mask3) ;
}

might work a little better.  Remember, GetMsg() is *very* quick if no
messages are on the port.  And your mousemove's won't completely lock
out your serial I/O.  (You can change any one of the above `if (msg...'
to while's, for things that *need* processing with a fair minimal
latency.)

>				-Matt

Sorry for the flaming tone of this article, Matt.  I just hate people
cutting down other people's coding style like that.

peter@sugar.uu.net (Peter da Silva) (02/10/89)

In article <494@infohh.rmi.de>, shimoda@infohh.rmi.de (Markus Schmidt) writes:
> Every time using a Wait-GetMsg-pair I thought how to do it 
> best way. Now I think I got it:

> while (1) {
>   while (!(imsg= GetMsg(win->UserPort)))
>      Wait (1<<win->UserPort->mp_SigBit);
>  ... select imsg->Class and break when ended ...
>
> }

That's really weird. Why don't you just do this:

while(1) {
  Wait(1<<win->UserPort->mp_SigBit);
  while(imsg= GetMsg(win->UserPort) {
    ... select imsg->Class and break when ended ...
  }
}

Which generalises to multiple message ports a lot better.
-- 
Peter "Have you hugged your wolf today" da Silva  `-_-'  Hackercorp.
...texbell!sugar!peter, or peter@sugar.uu.net      'U`

dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) (02/12/89)

:>	while (notquit) {
:>	    Wait(1 << win->UserPort->mp_SigBit);
:>	    while (mess = GetMsg(win->UserPort)) {
:>		/* process message */
:>		ReplyMsg(mess);
:>	    }
:>	}
:
:Equivalent, *except* that you have a wait at the top that's not guarded
:by a `while (GetMsg())' loop.  Fine, perhaps, depending on how you enter
:the loop and what operations you allow inside the loop (what those
:subroutines might do to the signals, you know) but conservative programming
:techniques never hurt anyone.

	P.S.  I suppose in my old age (@ 22) I have turned to making my
code aesthetically pleasing (in a logical sense) and intuitive.  Of course,
what one sees of one's own code is never what one sees of another's code or
another sees of one's own code.

	The initial condition (Wait before check) is valid 
because you have not ready anything from the port yet, and a re-entry 
condition would also be valid because you have read all pending messages
(and the next one that comes in will set the signal).

	I agree with you that, if one were using the IDCMP port for other
purposes one might have to be careful... maybe if you wanted to use it as
a sink for IO requests to (which is not unreasonable).  In that case I
would put the Wait() at the bottom, but it would have to be like this:

	while (notquit) {
	    while (mess = GetMsg(win->UserPort)) {
		/* process message */
		..
		case CLOSEWINDOW:
		    notquit = 0;
		    break;
		...

		ReplyMsg(mess);
	    }
	    if (notquit)
	        Wait(1 << win->UserPort->mp_SigBit);
	}

	Otherwise the user would have to hit the close gadget twice.

: Re my multiple-IDCMP example

:Not very fair; mask1 can dominate, causing problems later.  Something
:like
:
:int msgseen ;
:while (notquit) {
:   msgseen = 0 ;
:   if (msg = GetMsg(port1)) {
:      msgseen = 1 ;
:   }
:   if (msg = GetMsg(port2)) {
:      msgseen = 1 ;
:   }

	True, but only in rare circumstances.  In most cases problems due to
temporary domination will not materialize.

	What you outline as a solution would take quite a bit more overhead,
but a few small changes would accomplish the same thing with the same overhead
as the initial case.  Unfortunetly, the codes looks a bit more messy:

	mask = 0;
	while (notquit) {
	    while (mask) {
		if (mask & mask1) {
		    if (msg = GetMsg(port1)) {
			...
		    } else {
			mask &= ~mask1;
		    }
		}
		if (mask & mask2) {
		    ...
		}
	    }
	    if (notquit)
	        mask = Wait(mask1 | mask2 | mask3);
	}

	The overhead for an AND operation is much less than that for a 
GetMsg() operation, even if there is nothing to get.

:might work a little better.  Remember, GetMsg() is *very* quick if no
:messages are on the port.  And your mousemove's won't completely lock
:out your serial I/O.  (You can change any one of the above `if (msg...'
:to while's, for things that *need* processing with a fair minimal
:latency.)

	As far as MOUSEMOVEs go, I have learned a valuable lesson:

	always get ALL MOUSEMOVE messages and just execute/utilize the
last one.  You can get fancy and execute/utilize whenever you come upon
a button change as well.

	It would be nice if one could adjust the maximum mousemove message
rate in 1.4

:>				-Matt
:
:Sorry for the flaming tone of this article, Matt.  I just hate people
:cutting down other people's coding style like that.

	I apologize, but it looked horrible!  Never mind that it worked....

				-Matt

joe@dayton.UUCP (Joseph P. Larson) (02/13/89)

Matt Dillon responded to someone's code fragment listed below.  He didn't like
it...  This posting is my response to Tomas' response to Matt's response.

In some article, rokicki@polya.Stanford.EDU (Tomas G. Rokicki) writes:
>>:Now I think I got it:
>>:while (1) {
>>:  while (!(imsg= GetMsg(win->UserPort)))
>>:     Wait (1<<win->UserPort->mp_SigBit);
>>:  select imsg->Class and break when ended
>>:}
>
>This is *perfectly* fine.  while(1)/break is a general looping
>construct.  while(notquit) is fine, too.
>
>>	Yes, that's probably the worse way to do it.
>
>.... Secondly, his code *works*, and the
>coding style is one I happen to agree with.  Matter of taste, Matt.
>
.....
>Sorry for the flaming tone of this article, Matt.  I just hate people
>cutting down other people's coding style like that.

I think what happened is that Matt's response might have been a little fast.
I don't want to put words in Matt's mouth, but I almost posted the same
message until I looked at the code a little better.  The second time around
I noticed the "!" on the "while" line.  It makes for a very big difference
in how you read the code!  Personally, I don't like using a "!" in this
case for two reasons:

1. It can lead to the problem I just described.  "!" just isn't as noticable
	as one might like to indicate "I'm going to do this if this DOESN'T happen."

2. GetMsg() returns a pointer.  "!" is a logic operator.  Just because NULL
	happens to be zero doesn't mean that "!" is the correct way of checking
	for it.  Good coding practice (as outlined by people with more letters
	after their name than I have) suggest you check pointers against NULL.
	If nothing else, it help assures better portability.  (There are machines
	where "!" won't work.  As of 2 years ago, Sperry's 1100 version of C
	required 1 36-bit word for the address and another byte for an offset
	in the 36-bit word that a pointer points to.  "!" on pointers wouldn't
	work....)

However, on the Amiga, the above code fragment will work (assuming you're
only grabbing messages from one UserPort).  -J
-- 
          When you fall on your head do you land on your feet?
UUCP: rutgers!dayton!joe   (Feed my      Dayton Hudson Department Store Company
ATT : (612) 375-3537       picture       Joe Larson/MIS 1060
(standard disclaimer...)   collection)   700 on the Mall      Mpls, Mn. 55402

dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) (02/14/89)

:I don't want to put words in Matt's mouth, but I almost posted the same
:message until I looked at the code a little better.  The second time around
:I noticed the "!" on the "while" line.  It makes for a very big difference
:in how you read the code!  Personally, I don't like using a "!" in this
:case for two reasons:

	Nah, it wasn't fast.  I never said it didn't work .. in fact, I 
believe I said that sure, it did work.  But I had to twist my mind to
read the code!

				-Matt

janhen@wn2.sci.kun.nl (Jan Hendrikx) (02/14/89)

In article <6402@dayton.UUCP>, joe@dayton.UUCP (Joseph P. Larson) writes:
> >>:  while (!(imsg= GetMsg(win->UserPort)))
> 
> 2. GetMsg() returns a pointer.  "!" is a logic operator.  Just because NULL
> 	happens to be zero doesn't mean that "!" is the correct way of checking
> 	for it.  Good coding practice (as outlined by people with more letters
> 	after their name than I have) suggest you check pointers against NULL.
> 	If nothing else, it help assures better portability.  (There are machines
> 	where "!" won't work.  As of 2 years ago, Sperry's 1100 version of C
> 	required 1 36-bit word for the address and another byte for an offset
> 	in the 36-bit word that a pointer points to.  "!" on pointers wouldn't
> 	work....)

Sorry, but you are fortunately not right. 

(!expr) means the same as (expr != 0), which is THE test to test for a 
NULL pointer. Even if NULL pointers are internally not represented by
all-bits-zero, the compiler must recognize a comparison between a
pointer and the constant 0, and emit approprate code in case that that
compiler's internal representation of a NULL pointer is not the same
as 0. See relatively recent articles on comp.lang.c.

-Olaf Seibert.

brianr@tekig5.PEN.TEK.COM (Brian Rhodefer) (02/14/89)

I have obviously not yet attained the Zen of the Amiga, because
the construct under discussion, 

>:while (1) {
>:  while (!(imsg= GetMsg(win->UserPort)))
>:     Wait (1<<win->UserPort->mp_SigBit);
>:  select imsg->Class and break when ended
>:}

doesn't seem "*perfectly fine*" to me at all, notwithstanding Tomas R's
endorsement.

Presuming that the fragment was quoted from the original posting without
distortion, it sure looks to me that for the "select imsg->Class and break"
portion of the code, the "imsg" pointer is NULL!

Still unenlightened,

Brian Rhodefer

janhen@wn2.sci.kun.nl (Jan Hendrikx) (02/14/89)

In article <331@wn2.sci.kun.nl>, janhen@wn2.sci.kun.nl I wrote:
> (!expr) means the same as (expr != 0), which is THE test to test for a 

Of course I meant !(expr != 0).

> -Olaf Seibert.

urs@Spengat.oz (Urs Ruegger) (02/18/89)

In article <494@infohh.rmi.de>, shimoda@infohh.rmi.de (Markus Schmidt) writes:
> Hi!
> 
> Every time using a Wait-GetMsg-pair I thought how to do it 
> best way. Now I think I got it:
> 
> while (1) {
>   while (!(imsg= GetMsg(win->UserPort)))
>      Wait (1<<win->UserPort->mp_SigBit);
>  ... select imsg->Class and break when ended ...
> 
> }

Why not use :

while (WaitPort(win->UserPort) {
	msg = GetMsg(win->UserPort);
	......
	ReplyMsg(msg);
	if (xyz) break;
}

WaitPort() will return as long as there are _any_ messages in your port.
Only if the port is empty will it wait for the ports SigBit.

Of course this will only wait for one single port, but so does your example.

dillon@POSTGRES.BERKELEY.EDU (Matt Dillon) (02/19/89)

:Why not use :
:
:while (WaitPort(win->UserPort) {
:	msg = GetMsg(win->UserPort);
:	......
:	ReplyMsg(msg);
:	if (xyz) break;
:}
:
:WaitPort() will return as long as there are _any_ messages in your port.
:Only if the port is empty will it wait for the ports SigBit.
:
:Of course this will only wait for one single port, but so does your example.

	While this will work, it doesn't make much sense.  WaitPort() 
ALWAYS returns the first valid message on the port.  If the port is empty,
WaitPort() blocks until somebody sends a message to the port and then
returns that.

	Making it the argument of a while() implies that it is possible to
return a non-zero value at some point, which it isn't.  Thus, the code
will confuse the hell out of anybody with less than optimal experience with
the system library.

					-Matt

dan@ivucsb.UUCP (Dan Howell) (03/05/89)

In article <331@wn2.sci.kun.nl> janhen@wn2.sci.kun.nl (Jan Hendrikx) writes:
|(!expr) means the same as (expr != 0), which is THE test to test for a 
|NULL pointer. Even if NULL pointers are internally not represented by
|all-bits-zero, the compiler must recognize a comparison between a
|pointer and the constant 0, and emit approprate code in case that that
|compiler's internal representation of a NULL pointer is not the same
|as 0. See relatively recent articles on comp.lang.c.

You mean to tell me that comp.lang.c is STILL arguing about this?
I was on this group in 1985 and they were arguing about it.  I haven't
got comp.lang.c since September because my current feed doesn't get it.
Some things never change...

 
-- Dan Howell  <ivucsb!dan@anise.acc.com>
--             <...!(pyramid|ucbvax)!ucsbcsl!nessus!ivucsb!dan>
-- * I think this is it!  My address should be relatively permanent now. *

joe@dayton.UUCP (Joseph P. Larson) (03/07/89)

>In article <331@wn2.sci.kun.nl> janhen@wn2.sci.kun.nl (Jan Hendrikx) writes:
>|(!expr) means the same as (expr != 0), which is THE test to test for a 
>|NULL pointer. Even if NULL pointers are internally not represented by
>|all-bits-zero, the compiler must recognize a comparison between a
>|pointer and the constant 0, and emit approprate code in case that that
>|compiler's internal representation of a NULL pointer is not the same
>|as 0.

Irregardless of whether this works or not, it is better style to compare
against NULL, a value that indicates a pointer to nowhere.

What if address zero is a legal address?  NULL might be -1 then.  You don't
know how some odd machine is going to work in the future.

So THE test for comparing a pointer against NULL is to compare it against
NULL, not to depend on what NULL's value might be.  !expr or expr == 0
works, but that is simply because on most machines, NULL is going to be
zero.

-J
-- 
          When you fall on your head do you land on your feet?
UUCP: rutgers!dayton!joe   (Feed my      Dayton Hudson Department Store Company
ATT : (612) 375-3537       picture       Joe Larson/MIS 1060
(standard disclaimer...)   collection)   700 on the Mall      Mpls, Mn. 55402

prm@usl.usl.edu (Patrick R. Michaud) (03/09/89)

In article <6445@dayton.UUCP> joe@dayton.UUCP (Joseph P. Larson) writes:
>>In article <331@wn2.sci.kun.nl> janhen@wn2.sci.kun.nl (Jan Hendrikx) writes:
>>|(!expr) means the same as (expr != 0), which is THE test to test for a 
>>|NULL pointer. Even if NULL pointers are internally not represented by
>>|all-bits-zero, the compiler must recognize a comparison between a
>>|pointer and the constant 0, and emit approprate code in case that that
>>|compiler's internal representation of a NULL pointer is not the same
>>|as 0.

Absolutely.

>Irregardless of whether this works or not, it is better style to compare
>against NULL, a value that indicates a pointer to nowhere.

This is, of course, simply a matter of style.

>What if address zero is a legal address?  

In 'C', zero is _never_ a legal address. Refer to section 5.4 (page 97
in my book) of "The C Programming Language" by Kernighan and Ritchie:

   "C guarantees that no pointer that validly points at data will
   contain zero, so a return value of zero can be used to signify 
   an abnormal event.... We write NULL instead of zero, however, 
   to indicate more clearly that this is a special value for a 
   pointer."

>NULL might be -1 then.  You don't
>know how some odd machine is going to work in the future.
>So THE test for comparing a pointer against NULL is to compare it against
>NULL, not to depend on what NULL's value might be.  !expr or expr == 0
>works, but that is simply because on most machines, NULL is going to be
>zero.

Again, see above. If C is properly implemented, then the value of NULL 
almost has to be zero; or, at the very least, the compiler MUST be able
to properly test pointer comparisons against zero.

Personally, I really do prefer !expr over expr==0; it seems to say
"if expr is not a valid pointer, then..." But again, this is probably 
only a matter of personal taste.

>-- 
>          When you fall on your head do you land on your feet?
>UUCP: rutgers!dayton!joe   (Feed my      Dayton Hudson Department Store Company
>ATT : (612) 375-3537       picture       Joe Larson/MIS 1060
>(standard disclaimer...)   collection)   700 on the Mall      Mpls, Mn. 55402

////////////////////////////////////////////////////////////////////////
// Patrick R. Michaud			UUCP: ...!uunet!dalsqnt!usl!prm
// USL/NASA Project Leader		      ...!cs.utexas.edu!usl!prm
// Univ. of Southwestern Louisiana	INET: prm@usl.usl.edu
//     - "Programmer at Large"
////////////////////////////////////////////////////////////////////////