Path: 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: 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 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 | \-----------------------------------------------------------------------/