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