1 ////////////////////////////////////////////////////////////////////////////////////
2 //         w32chan.c           Fish's new i/o scheduling logic
3 ////////////////////////////////////////////////////////////////////////////////////
4 // (c) Copyright "Fish" (David B. Trout), 2001-2009. Released under the Q Public License
5 // (http://www.hercules-390.org/herclic.html) as modifications to Hercules.
6 ////////////////////////////////////////////////////////////////////////////////////
7 
8 #include "hstdinc.h"
9 
10 #define _W32CHAN_C_
11 #define _HENGINE_DLL_
12 
13 #include "hercules.h"
14 #include "w32chan.h"
15 
16 #if defined(OPTION_FISHIO)
17 
18 /////////////////////////////////////////////////////////////////////////////
19 // Internal scheduler helper macros...
20 
21 #define LockScheduler()                     (EnterCriticalSection(&IOSchedulerLock))
22 #define LockThreadParms(pThreadParms)       (EnterCriticalSection(&pThreadParms->IORequestListLock))
23 #define UnlockScheduler()                   (LeaveCriticalSection(&IOSchedulerLock))
24 #define UnlockThreadParms(pThreadParms)     (LeaveCriticalSection(&pThreadParms->IORequestListLock))
25 
26 /////////////////////////////////////////////////////////////////////////////
27 // i/o scheduler variables...  (some private, some externally visible)
28 
29 int    ios_arch_mode          = 0;          // current architecture mode
30 int    ios_devthread_timeout  = 30;         // max device thread wait time
31 int*   ios_devthread_prio     = NULL;       // pointer to sysblk.devprio
32 
33 LIST_ENTRY        ThreadListHeadListEntry;  // anchor for DEVTHREADPARMS linked list
34 CRITICAL_SECTION  IOSchedulerLock;          // lock for accessing above list
35                                             // and below variables
36 
37 long ios_devtwait    = 0;   // #of threads currently idle
38 int  ios_devtnbr     = 0;   // #of threads currently active
39 int  ios_devthwm     = 0;   // max #of threads that WERE active
40 int  ios_devtmax     = 0;   // max #of threads there can be
41 int  ios_devtunavail = 0;   // #of times 'idle' thread unavailable
42 
43 /////////////////////////////////////////////////////////////////////////////
44 // initialize i/o scheduler variables and work areas...
45 
InitIOScheduler(int arch_mode,int * devt_prio,int devt_timeout,long devt_max)46 void  InitIOScheduler
47 (
48     int    arch_mode,       // (for calling execute_ccw_chain)
49     int*   devt_prio,       // (ptr to device thread priority)
50     int    devt_timeout,    // (MAX_DEVICE_THREAD_IDLE_SECS)
51     long   devt_max         // (maximum #of device threads allowed)
52 )
53 {
54     ios_arch_mode          = arch_mode;
55     ios_devthread_prio     = devt_prio;
56     ios_devthread_timeout  = devt_timeout;
57     ios_devtmax            = devt_max;
58 
59     InitializeListHead(&ThreadListHeadListEntry);
60     MyInitializeCriticalSection(&IOSchedulerLock);
61 }
62 
63 /////////////////////////////////////////////////////////////////////////////
64 // device_thread parms block...
65 
66 typedef struct _DevThreadParms
67 {
68     DWORD             dwThreadID;                   // device_thread thread id
69     LIST_ENTRY        ThreadListLinkingListEntry;   // (just a link in the chain)
70     HANDLE            hShutdownEvent;               // tells device thread to exit
71     HANDLE            hRequestQueuedEvent;          // tells device thread it has work
72     LIST_ENTRY        IORequestListHeadListEntry;   // anchor for DEVIOREQUEST list
73     CRITICAL_SECTION  IORequestListLock;            // (for accessing above list)
74     BOOL              bThreadIsDead;                // TRUE = thread has exited
75 }
76 DEVTHREADPARMS;
77 
78 /////////////////////////////////////////////////////////////////////////////
79 // i/o request block...
80 
81 typedef struct _DevIORequest
82 {
83     LIST_ENTRY      IORequestListLinkingListEntry;  // (just a link in the chain)
84     void*           pDevBlk;                        // (ptr to device block)
85     int*            pnDevPrio;                      // (ptr to device i/o priority)
86     unsigned short  wDevNum;                        // (device number for debugging)
87 }
88 DEVIOREQUEST;
89 
90 /////////////////////////////////////////////////////////////////////////////
91 // (forward references for some of our functions...)
92 
93 DEVTHREADPARMS*  SelectDeviceThread();
94 DEVTHREADPARMS*  CreateDeviceThread(unsigned short wDevNum);
95 
96 void   AdjustThreadPriority(int* pCurPrio, int* pNewPrio);
97 void*  DeviceThread(void* pThreadParms);
98 void   RemoveDeadThreadsFromList();
99 void   RemoveThisThreadFromOurList(DEVTHREADPARMS* pThreadParms);
100 
101 /////////////////////////////////////////////////////////////////////////////
102 // Schedule a DeviceThread for this i/o request... (called by the 'startio'
103 // function whenever the startio or start subchannel instruction is executed)
104 
ScheduleIORequest(void * pDevBlk,unsigned short wDevNum,int * pnDevPrio)105 int  ScheduleIORequest(void* pDevBlk, unsigned short wDevNum, int* pnDevPrio)
106 {
107     /////////////////////////////////////////////////////////////////
108     // PROGRAMMING NOTE: The various errors that can occur in this
109     // function should probably be reported by returning cc1 with
110     // 'channel control check' set (or something similar), but until
111     // I can work out the details, I'm going to be lazy and just
112     // return cc2 (subchannel busy) for now to allow the system to
113     // retry the i/o request if it so desires. Hopefully the problem
114     // was only intermittent and will work the second time around.
115     /////////////////////////////////////////////////////////////////
116 
117     DEVIOREQUEST*    pIORequest;            // ptr to i/o request
118     DEVTHREADPARMS*  pThreadParms;          // ptr to device_thread parameters
119 
120     // Create an i/o request queue entry for this i/o request
121 
122     pIORequest = (DEVIOREQUEST*) malloc(sizeof(DEVIOREQUEST));
123 
124     if (!pIORequest)
125     {
126         logmsg(_("HHCCP084E malloc(DEVIOREQUEST) failed; device=%4.4X, strerror=\"%s\"\n"),
127             wDevNum,strerror(errno));
128         return 2;
129     }
130 
131     InitializeListLink(&pIORequest->IORequestListLinkingListEntry);
132     pIORequest->pDevBlk = pDevBlk;
133     pIORequest->wDevNum = wDevNum;
134     pIORequest->pnDevPrio = pnDevPrio;
135 
136     // Schedule a device_thread to process this i/o request
137 
138     LockScheduler();        // (lock scheduler vars)
139 
140     if (ios_devtmax < 0)
141     {
142         // Create new "one time only" thread each time...
143 
144         // (Note: the device thread's parms will automatically
145         //  be locked upon return if creation was successful.)
146 
147         RemoveDeadThreadsFromList();    // (prevent runaway list)
148 
149         if (!(pThreadParms = CreateDeviceThread(wDevNum)))
150         {
151             UnlockScheduler();          // (unlock scheduler vars)
152             free(pIORequest);           // (discard i/o request)
153             return 2;                   // (return device busy)
154         }
155     }
156     else
157     {
158         // Select a non-busy device thread to handle this i/o request...
159 
160         // (Note: the device thread's parms will automatically
161         //  be locked upon return if selection was successful.)
162 
163         if (!(pThreadParms = SelectDeviceThread()))
164         {
165             // All threads are currently busy or no threads exist yet.
166 
167             // Since the possibility of a deadlock[1] can easily occur if we schedule
168             // an i/o request to a currently busy device thread[2], we have no choice
169             // but to create another device thread.
170 
171             // [1] Not an actual programmatic deadlock of course, but nonetheless
172             //     a deadlock from the guest operating system's point of view.
173 
174             // [2] A curently busy device thread could easily have its channel program
175             //     suspended and thus would never see the queued request until such time
176             //     its suspended channel program was resumed and allowed to complete, and
177             //     if the request we queued (that would end up waiting to be processed
178             //     by the currently suspended device thread) just so happens to be one
179             //     that the operating system needs to have completed before it can resume
180             //     the currently suspended channel program, then the operating system will
181             //     obviously hang. (It would end up waiting for an i/o to complete that
182             //     would never complete until the currently suspended channel program was
183             //     resumed, which would never be resumed until the queued i/o completes,
184             //     which would never even be processed until the currently suspended
185             //     channel program is resumed ..... etc.)
186 
187             if (ios_devtmax && ios_devtnbr >= ios_devtmax)  // max threads already created?
188             {
189                 logmsg(_("HHCCP085W *WARNING* max device threads exceeded.\n"));
190                 ios_devtunavail++;          // (count occurrences)
191             }
192 
193             // Create a new device thread for this i/o request...
194 
195             // (Note: the device thread's parms will automatically
196             //  be locked upon return if creation was successful.)
197 
198             if (!(pThreadParms = CreateDeviceThread(wDevNum)))
199             {
200                 UnlockScheduler();          // (unlock scheduler vars)
201                 free(pIORequest);           // (discard i/o request)
202                 return 2;                   // (return device busy)
203             }
204         }
205     }
206 
207     // (Note: the thread parms lock should still be held at this point)
208 
209     // Queue the i/o request to the selected device_thread's i/o request queue...
210 
211     InsertListTail(&pThreadParms->IORequestListHeadListEntry,&pIORequest->IORequestListLinkingListEntry);
212 
213     // Tell device_thread it has work (must do this while its request list is still
214     // locked to prevent it from prematurely exiting in case it's just about to die)
215 
216     MySetEvent(pThreadParms->hRequestQueuedEvent);
217 
218     // Now unlock its request queue so it can process the request we just gave it
219 
220     UnlockThreadParms(pThreadParms);        // (let it proceed)
221 
222     // We're done, so unlock the scheduler vars so another cpu thread
223     // in the configuration can schedule and i/o request and then exit
224     // back the the startio function...
225 
226     UnlockScheduler();          // (unlock vars and exit; we're done)
227 
228     return 0;                   // (success)
229 }
230 
231 /////////////////////////////////////////////////////////////////////////////
232 // Select a non-busy DeviceThread...
233 // NOTE! the IOSchedulerLock must be acquired before calling this function!
234 // NOTE! the selected thread's parms will be locked upon return!
235 
SelectDeviceThread()236 DEVTHREADPARMS*  SelectDeviceThread()
237 {
238     DEVTHREADPARMS*  pThreadParms;      // ptr to selected device thread
239     LIST_ENTRY*      pListEntry;        // (work)
240 
241     pListEntry = ThreadListHeadListEntry.Flink;
242 
243     while (pListEntry != &ThreadListHeadListEntry)
244     {
245         pThreadParms = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
246 
247         LockThreadParms(pThreadParms);      // (freeze moving target)
248 
249         if (pThreadParms->bThreadIsDead)
250         {
251             UnlockThreadParms(pThreadParms);
252             RemoveThisThreadFromOurList(pThreadParms);
253             pListEntry = ThreadListHeadListEntry.Flink;
254             continue;
255         }
256 
257         if (!IsEventSet(pThreadParms->hRequestQueuedEvent))
258         {
259             RemoveListEntry(pListEntry);
260             InsertListTail(&ThreadListHeadListEntry,pListEntry);
261             return pThreadParms;
262         }
263 
264         UnlockThreadParms(pThreadParms);    // (can't use this one)
265 
266         pListEntry = pListEntry->Flink;
267     }
268 
269     return NULL;    // (all of them are busy)
270 }
271 
272 /////////////////////////////////////////////////////////////////////////////
273 // create a new device thread and add it to the list...
274 // NOTE! the IOSchedulerLock must be acquired before calling this function!
275 // NOTE! the created thread's parms will be locked upon return!
276 
CreateDeviceThread(unsigned short wDevNum)277 DEVTHREADPARMS*  CreateDeviceThread(unsigned short wDevNum)
278 {
279     DEVTHREADPARMS*  pThreadParms;      // ptr to returned device_thread parameters
280     DWORD            dwThreadID;        // (work)
281 
282     pThreadParms = malloc(sizeof(DEVTHREADPARMS));      // (allocate structure)
283 
284     if (!pThreadParms)
285     {
286         logmsg(_("HHCCP086E malloc(DEVTHREADPARMS) failed; device=%4.4X, strerror=\"%s\"\n"),
287             wDevNum,strerror(errno));
288         return NULL;    // (error)
289     }
290 
291     pThreadParms->hShutdownEvent = MyCreateEvent(NULL,TRUE,FALSE,NULL);
292 
293     if (!pThreadParms->hShutdownEvent)
294     {
295         logmsg(_("HHCCP087E CreateEvent(hShutdownEvent) failed; device=%4.4X, strerror=\"%s\"\n"),
296             wDevNum,strerror(errno));
297         free(pThreadParms);
298         return NULL;    // (error)
299     }
300 
301     pThreadParms->hRequestQueuedEvent = MyCreateEvent(NULL,TRUE,FALSE,NULL);
302 
303     if (!pThreadParms->hRequestQueuedEvent)
304     {
305         logmsg(_("HHCCP088E CreateEvent(hRequestQueuedEvent) failed; device=%4.4X, strerror=\"%s\"\n"),
306             wDevNum,strerror(errno));
307         MyCloseHandle(pThreadParms->hShutdownEvent);
308         free(pThreadParms);
309         return NULL;    // (error)
310     }
311 
312     MyInitializeCriticalSection(&pThreadParms->IORequestListLock);
313 
314     InitializeListLink(&pThreadParms->ThreadListLinkingListEntry);
315     InitializeListHead(&pThreadParms->IORequestListHeadListEntry);
316 
317     pThreadParms->bThreadIsDead = FALSE;
318     pThreadParms->dwThreadID = 0;
319 
320     if (fthread_create(&dwThreadID,NULL,DeviceThread,pThreadParms,"DeviceThread") != 0)
321     {
322         logmsg(_("HHCCP089E fthread_create(DeviceThread) failed; device=%4.4X, strerror=\"%s\"\n"),
323             wDevNum,strerror(errno));
324         MyCloseHandle(pThreadParms->hShutdownEvent);
325         MyCloseHandle(pThreadParms->hRequestQueuedEvent);
326         MyDeleteCriticalSection(&pThreadParms->IORequestListLock);
327         free(pThreadParms);
328         return NULL;    // (error)
329     }
330 
331     // Add the newly created device_thread to the end of our list of managed threads.
332 
333     InsertListTail(&ThreadListHeadListEntry,&pThreadParms->ThreadListLinkingListEntry);
334 
335     if (++ios_devtnbr > ios_devthwm) ios_devthwm = ios_devtnbr;
336 
337     LockThreadParms(pThreadParms);  // (lock thread parms before using)
338 
339     return pThreadParms;            // (success)
340 }
341 
342 /////////////////////////////////////////////////////////////////////////////
343 //  helper function to set a thread's priority to its proper value
344 
AdjustThreadPriority(int * pCurPrio,int * pNewPrio)345 void AdjustThreadPriority(int* pCurPrio, int* pNewPrio)
346 {
347     if (*pCurPrio != *pNewPrio)
348     {
349         setpriority(PRIO_PROCESS, 0, *pNewPrio);
350         *pCurPrio = *pNewPrio;
351     }
352 }
353 
354 /////////////////////////////////////////////////////////////////////////////
355 // the device_thread itself...  (processes queued i/o request
356 // by calling "execute_ccw_chain" function in channel.c...)
357 
358 extern void  call_execute_ccw_chain(int arch_mode, void* pDevBlk);
359 
DeviceThread(void * pArg)360 void*  DeviceThread (void* pArg)
361 {
362     DEVTHREADPARMS*  pThreadParms;      // ptr to thread parms
363     LIST_ENTRY*      pListEntry;        // (work)
364     DEVIOREQUEST*    pIORequest;        // ptr to i/o request
365     void*            pDevBlk;           // ptr to device block
366     int*             pnDevPrio;         // ptr to device i/o priority
367     int              nCurPrio;          // current thread priority
368 
369     pThreadParms = (DEVTHREADPARMS*) pArg;
370 
371     pThreadParms->dwThreadID = GetCurrentThreadId();
372 
373     nCurPrio = getpriority(PRIO_PROCESS, 0);
374     AdjustThreadPriority(&nCurPrio,ios_devthread_prio);
375 
376     for (;;)
377     {
378         // Wait for an i/o request to be queued...
379 
380         InterlockedIncrement(&ios_devtwait);
381         MyWaitForSingleObject(pThreadParms->hRequestQueuedEvent,ios_devthread_timeout * 1000);
382         InterlockedDecrement(&ios_devtwait);
383 
384 
385         if (IsEventSet(pThreadParms->hShutdownEvent)) break;
386 
387         // Lock our queue so it doesn't change while we take a look at it...
388 
389         LockThreadParms(pThreadParms);      // (freeze moving target)
390 
391         // Check to see if we have any work...
392 
393         if (IsListEmpty(&pThreadParms->IORequestListHeadListEntry))
394         {
395             // We've waited long enough...
396 
397             pThreadParms->bThreadIsDead = TRUE;     // (keep scheduler informed)
398             UnlockThreadParms(pThreadParms);        // (let go of our parms block)
399             return NULL;                            // (return, NOT break!)
400         }
401 
402         // Remove the i/o request from our queue...
403 
404         // It's important that we remove the request from our queue but
405         // NOT reset our flag (if the list is now empty) until AFTER we've
406         // processed the request (see further below for details as to why).
407 
408         pListEntry = RemoveListHead(&pThreadParms->IORequestListHeadListEntry);
409 
410         UnlockThreadParms(pThreadParms);    // (done with thread parms for now)
411 
412         pIORequest = CONTAINING_RECORD(pListEntry,DEVIOREQUEST,IORequestListLinkingListEntry);
413         pDevBlk   = pIORequest->pDevBlk;    // (need ptr to devblk)
414         pnDevPrio = pIORequest->pnDevPrio;  // (need ptr to devprio)
415         free(pIORequest);                   // (not needed anymore)
416 
417         // Process the i/o request by calling the proper 'execute_ccw_chain'
418         // function (based on architectural mode) in source module channel.c
419 
420         // Set thread priority to requested device level
421         AdjustThreadPriority(&nCurPrio,pnDevPrio);
422 
423         call_execute_ccw_chain(ios_arch_mode, pDevBlk); // (process i/o request)
424 
425         // Reset thread priority, if necessary
426         if (nCurPrio > *ios_devthread_prio)
427             AdjustThreadPriority(&nCurPrio,ios_devthread_prio);
428 
429 
430         ////////////////////////////////////////////////////////////////////////////
431         //
432         //                  * * *   I M P O R T A N T   * * *
433         //
434         // It's important that we reset our flag AFTER we're done with our request
435         // in order to prevent the scheduler from trying to give us more work while
436         // we're still busy processing the current i/o request (in case the channel
437         // program we're processing gets suspended). If the channel program we're
438         // processing gets suspended and the scheduler places another request in our
439         // queue, it won't ever get processed until our suspended channel program is
440         // resumed, and if the request that was placed in our queue was a request
441         // for an i/o to another device that the operating system needs to complete
442         // in order to obtain the information needed to resume our suspended channel
443         // program, a deadlock will occur! (i.e. if the operating system needs to
444         // read data from another device before it can resume our channel program,
445         // then the i/o request to read from that device better not be given to us
446         // because we're suspended and will never get around to executing it until
447         // we're resumed, which will never happen until the i/o request waiting in
448         // our queue is completed, which will never happend until we're resumed,
449         // etc...)
450         //
451         ////////////////////////////////////////////////////////////////////////////
452 
453         // Now that we're done this i/o, reset our flag.
454 
455         LockThreadParms(pThreadParms);      // (freeze moving target)
456 
457         if (IsListEmpty(&pThreadParms->IORequestListHeadListEntry))
458             MyResetEvent(pThreadParms->hRequestQueuedEvent);
459 
460         UnlockThreadParms(pThreadParms);    // (thaw moving target)
461 
462         if (IsEventSet(pThreadParms->hShutdownEvent)) break;
463 
464         // If we're a "one time only" thread, then we're done.
465 
466         if (ios_devtmax < 0)
467         {
468             // Note: no need to lock our thread parms before setting 'dead' flag
469             // since the scheduler should never queue us another request anyway
470             // because we're a "one-time-only" thread.
471 
472             pThreadParms->bThreadIsDead = TRUE;     // (let garbage collector discard us)
473             return NULL;                            // (return, NOT break!)
474         }
475     }
476 
477     // The only time we reach here is if the shutdown event was signalled (i.e. we
478     // were manually "cancelled" or asked to stop processing; i.e. the i/o subsystem
479     // was reset). Discard all i/o requests that may still be remaining in our queue.
480 
481     TRACE("** DeviceThread %8.8X: shutdown detected\n",
482         (unsigned int)pThreadParms->dwThreadID);
483 
484     LockThreadParms(pThreadParms);          // (freeze moving target)
485 
486     // Discard all queued i/o requests...
487 
488     while (!IsListEmpty(&pThreadParms->IORequestListHeadListEntry))
489     {
490         pListEntry = RemoveListHead(&pThreadParms->IORequestListHeadListEntry);
491 
492         pIORequest = CONTAINING_RECORD(pListEntry,DEVIOREQUEST,IORequestListLinkingListEntry);
493 
494         TRACE("** DeviceThread %8.8X: discarding i/o request for device %4.4X\n",
495             (unsigned int)pThreadParms->dwThreadID,pIORequest->wDevNum);
496 
497         free(pIORequest);
498     }
499 
500     pThreadParms->bThreadIsDead = TRUE;     // (tell scheduler we've died)
501 
502     TRACE("** DeviceThread %8.8X: shutdown complete\n",
503         (unsigned int)pThreadParms->dwThreadID);
504 
505     UnlockThreadParms(pThreadParms);        // (thaw moving target)
506 
507     return NULL;
508 }
509 
510 /////////////////////////////////////////////////////////////////////////////
511 // shutdown idle threads (if possible) until number drops below threshold...
512 
TrimDeviceThreads()513 void  TrimDeviceThreads()
514 {
515     DEVTHREADPARMS*  pThreadParms;
516     BOOL             bThreadIsBusy;
517     LIST_ENTRY*      pListEntry;
518 
519     LockScheduler();        // (lock scheduler vars)
520 
521     pListEntry = ThreadListHeadListEntry.Flink;
522 
523     while (pListEntry != &ThreadListHeadListEntry && ios_devtnbr > ios_devtmax)
524     {
525         pThreadParms = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
526 
527         pListEntry = pListEntry->Flink;
528 
529         LockThreadParms(pThreadParms);
530         bThreadIsBusy = IsEventSet(pThreadParms->hRequestQueuedEvent);
531         UnlockThreadParms(pThreadParms);
532 
533         if (bThreadIsBusy) continue;
534 
535         // Thread is currently idle and awaiting work. Tell it to exit.
536 
537         // (Note: we need to signal the hRequestQueuedEvent event too so that
538         //  it can wake up and notice that the hShutdownEvent event is signaled)
539 
540         MySetEvent(pThreadParms->hShutdownEvent);
541         MySetEvent(pThreadParms->hRequestQueuedEvent);  // (wakeup thread)
542 
543         Sleep(10);  // (give it time to die)
544         RemoveDeadThreadsFromList();
545     }
546 
547     UnlockScheduler();      // (unlock shceduler vars)
548 }
549 
550 #if 0   // (the following is not needed yet)
551 /////////////////////////////////////////////////////////////////////////////
552 // ask all device_threads to exit... (i.e. i/o subsystem reset)
553 
554 void  KillAllDeviceThreads()
555 {
556     DEVTHREADPARMS*  pThreadParms;
557     LIST_ENTRY*      pListEntry;
558 
559     TRACE("** ENTRY KillAllDeviceThreads\n");
560 
561     LockScheduler();                // (lock scheduler vars)
562 
563     RemoveDeadThreadsFromList();    // (discard dead threads)
564 
565     while (!IsListEmpty(&ThreadListHeadListEntry))
566     {
567         pListEntry = ThreadListHeadListEntry.Flink;     // (get starting link in chain)
568 
569         while (pListEntry != &ThreadListHeadListEntry)  // (while not end of chain reached...)
570         {
571             pThreadParms = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
572 
573             // (Note: we need to signal the hRequestQueuedEvent event too so that
574             //  it can wake up and notice that the hShutdownEvent event is signaled)
575 
576             TRACE("** KillAllDeviceThreads: setting shutdown event for thread %8.8X\n",
577                 pThreadParms->dwThreadID);
578 
579             LockThreadParms(pThreadParms);                  // (lock thread parms)
580             MySetEvent(pThreadParms->hShutdownEvent);       // (request shutdown)
581             MySetEvent(pThreadParms->hRequestQueuedEvent);  // (wakeup thread)
582             UnlockThreadParms(pThreadParms);                // (unlock thread parms)
583 
584             pListEntry = pListEntry->Flink;     // (go on to next link in chain)
585         }
586 
587         UnlockScheduler();              // (unlock scheduler vars)
588         TRACE("** KillAllDeviceThreads: waiting for thread(s) to shutdown\n");
589         Sleep(10);                      // (give them time to die)
590         LockScheduler();                // (re-lock scheduler vars)
591         RemoveDeadThreadsFromList();    // (discard dead threads)
592     }
593 
594     UnlockScheduler();              // (unlock scheduler vars)
595 
596     TRACE("** EXIT KillAllDeviceThreads\n");
597 }
598 #endif // (the preceding is not needed yet)
599 
600 /////////////////////////////////////////////////////////////////////////////
601 // remove all dead threads from our list of threads...
602 // NOTE! the IOSchedulerLock must be acquired before calling this function!
603 
RemoveDeadThreadsFromList()604 void  RemoveDeadThreadsFromList()
605 {
606     DEVTHREADPARMS*  pThreadParms;
607     BOOL             bThreadIsDead;
608     LIST_ENTRY*      pListEntry = ThreadListHeadListEntry.Flink;
609 
610     while (pListEntry != &ThreadListHeadListEntry)
611     {
612         pThreadParms = CONTAINING_RECORD(pListEntry,DEVTHREADPARMS,ThreadListLinkingListEntry);
613 
614         pListEntry = pListEntry->Flink;
615 
616         LockThreadParms(pThreadParms);
617 
618         bThreadIsDead = pThreadParms->bThreadIsDead;
619 
620         UnlockThreadParms(pThreadParms);
621 
622         if (bThreadIsDead) RemoveThisThreadFromOurList(pThreadParms);
623     }
624 }
625 
626 /////////////////////////////////////////////////////////////////////////////
627 // private function to remove an entry from our list and discard it...
628 // NOTE! the IOSchedulerLock must be acquired before calling this function!
629 
RemoveThisThreadFromOurList(DEVTHREADPARMS * pThreadParms)630 void  RemoveThisThreadFromOurList(DEVTHREADPARMS* pThreadParms)
631 {
632     RemoveListEntry(&pThreadParms->ThreadListLinkingListEntry);
633     MyCloseHandle(pThreadParms->hShutdownEvent);
634     MyCloseHandle(pThreadParms->hRequestQueuedEvent);
635     MyDeleteCriticalSection(&pThreadParms->IORequestListLock);
636     free(pThreadParms);
637     ios_devtnbr--;          // (track number of active device_thread)
638 }
639 
640 /////////////////////////////////////////////////////////////////////////////
641 
642 #endif // !defined(OPTION_FISHIO)
643