Article View: comp.os.os2.programmer
Article #1542Re: Horses!
From: David Bolen
Date: Fri, 28 Sep 1990 19:29
Date: Fri, 28 Sep 1990 19:29
114 lines
6771 bytes
6771 bytes
In article <90270.152726TURGUT@TREARN.BITNET> TURGUT@TREARN.BITNET (Turgut Kalfaoglu) writes:
> [...] Here is the curiosity: if I put a
>DosSleep(0L) in the main()'s loop where the beginthread is called five
>times to create the five horses, everything is fine. If I remove that,
>five horses are created, but they each receive the same index number (5),
>and they move together too! (so only one horse appears on display). It
>roughly looks like this:
>
>for (j=0;j<5;j++) {
> if (_beginthread(horse,NULL,4096,&j) == 0) {
> printf("Error");
> exit(1);
> }
> DosSleep(0L);
>}
>I remove the DosSleep and it no longer works!
The last parameter that you are supplying to _beginthread is a pointer,
which is simply copied as a parameter to the starting thread. The pointer
points to the location of the local variable "j" on the main stack. I
haven't seen the full program, but I presume as each thread starts, it
dereferences the pointer to determine which horse it is.
However - imagine that the main thread of the program keeps running for
a little bit after calling _beginthread before the new thread actually
starts up, which is certainly possible if the main thread runs fast
enough not to have run out of its timeslice after calling _beginthread.
Then, the for loop will increment the value of j on the stack, and when
the new thread actually begins executing, since it just takes the actual
value of j on the main stack, will see the incremented value and not the
value that j held when _beginthread was originally called. In your case,
it looks like _beginthread is called all 5 times before any of the threads
begin executing, so they all see the value of j as being the final value
of the loop.
In fact, passing a pointer to the loop counter as a parameter is pretty
dangerous - it's possible that the new thread will access the value of
the counter in the middle of the main thread changing its value, and thus
the new thread will get a value that is entirely wrong. True, when the
increment is done with "++", it's probably a single "inc" instruction and
won't be interrupted, but in general this is not a nice way to do things.
This "seems" to be fixed by adding the DosSleep(0) because you force the
main thread to give up the remainder of its timeslice after creating each
new thread, which gives each new thread a chance to start executing and
grab the value of j before it changes. I say "seems" because other
factors might keep the new thread from executing before main gets control
again, although if they all have the same priority, the round robin
scheduling of OS/2 will probably let each thread run before main gets
another timeslice.
How to fix this? I'd suggest passing the value of j as the parameter,
rather than a pointer to it. Cast j into a (void *), or whatever the last
parameter of _beginthread is, and cast it back to a integer at the start
of the thread. This does assume that sizeof(int) <= sizeof(void *), but
for an example program that's probably not too bad.
A more "proper" alternative is to use a semaphore to let each thread tell
the main thread when it is done starting up, and has the proper value of
j. Have main DosSemSet the semaphore before calling _beginthread, and
then have the thread function DosSemClear it when it gets its numeric
code. Then rather than DosSleep(0) in main, use DosSemWait on the
semaphore.
To finish off a "fair" horserace - each thread should, after using
DosSemClear to tell main is it up and running, do a DosSemWait on another
semaphore. When main finishes creating all 5 threads, it should clear
the second semaphore, thus allowing all 5 threads to begin executing at
the same time (all 5 threads will be waiting on the same semaphore).
>Any ideas? Is it a bug in OS/2 1.1 that got corrected with 1.2 ?
Nope - I think it's just that your process is getting scheduled slightly
differently under 1.2 than under 1.1. It is possible that the scheduler
has changed a little between versions, and your main procedure is just
running longer under 1.2 than it did under 1.1, which lets it modify j
before the new thread begins. Perhaps you are also using different
values of TIMESLICE or MAXWAIT in your CONFIG.SYS which can affect the
maximum time a single thread can have control of the CPU. (I don't know
if the defaults for those settings changed between 1.1 and 1.2)
>Also I noticed that if the main() ends, even with DosExit(0,0),
>all threads are killed, which is contrary to Peter Norton's book..
Well, I've never read Peter Norton's book, but if the primary thread of
an application exits, the application will (or definitely should) exit.
The main thread of an application is a special thread, and not really
equivalent to those that you create - especially in the way that signals
are always sent to the main thread, so one must exist for any running
process. If this disagrees with the book, I think the book is wrong.
To quote from the IBM toolkit documentation, Control Program Programming
Reference, DosExit function:
"Do not terminate thread 1 without terminating the process. Thread 1
is the initial thread of execution, not a thread started by a
DosCreateThread request. When thread 1 ends, any monitors or signal
processing routines set for this process also end. To avoid
unpredictable results, DosExit should be specified with ActionCode=1
to ensure the process ends."
I suppose the application ending falls under "unpredictable results" :-)
-- David
--
/-----------------------------------------------------------------------\
\ David Bolen /
| Laboratory Automation, IBM Thomas J. Watson Research Center |
/ P.O. Box 218, Yorktown Heights, NY 10598 \
| - - - - - - - - - - - - M i t h r a n d i r - - - - - - - - - - - - |
| Internet : db3l@ibm.com | Bitnet : db3l@watson |
| Usenet : uunet!bywater!arnor!larios!db3l | Phone : (914) 945-1940 |
\-----------------------------------------------------------------------/
Technical Details
Message-ID:
<DB3L.90Sep28152933@larios.watson.ibm.com>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!bywater!arnor!larios!db3lReferences:
<90270.152726TURGUT@TREARN.BITNET>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!bywater!arnor!larios!db3l From: db3l@ibm.com (David Bolen) Newsgroups: comp.os.os2.programmer Subject: Re: Horses! Message-ID: <DB3L.90Sep28152933@larios.watson.ibm.com> Date: 28 Sep 90 19:29:33 GMT References: <90270.152726TURGUT@TREARN.BITNET> Sender: news@arnor.uucp (NNTP News Poster) Organization: Laboratory Automation, IBM Thomas J. Watson Research Center Lines: 114 In-Reply-To: TURGUT@TREARN.BITNET's message of 28 Sep 90 03:56:40 GMT