|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24: /* unixThread.m 1.0 02/07/91 (c) 1991 NeXT
25: *
26: * unixThread.m - unixDisk Device Thread support.
27: *
28: * HISTORY
29: * 07-Feb-91 Doug Mitchell at NeXT
30: * Created.
31: */
32:
33: #import <bsd/sys/types.h>
34: #import <mach/cthreads.h>
35: #import <driverkit/IODevice.h>
36: #import "unixDisk.h"
37: #import "unixThread.h"
38: #import "unixDiskUxpr.h"
39: #import <driverkit/generalFuncs.h>
40: #import <machkit/NXLock.h>
41:
42: static void unixThreadDequeue(IOQueue_t *ioQ,
43: BOOL needs_disk,
44: int threadNum);
45:
46: @implementation unixDisk(Thread)
47:
48: /*
49: * Enqueue an IOBuf on an IOQueue and wake up anyone (i.e., an I/O thread)
50: * who might be waiting for the IOBuf.
51: */
52: - (void) enqueueIoBuf : (IOBuf_t *)buf
53: needs_disk : (BOOL)needs_disk
54: {
55: queue_head_t *q;
56:
57: if(needs_disk)
58: q = &IOQueue.q_disk;
59: else
60: q = &IOQueue.q_nodisk;
61: [IOQueue.qlock lock];
62: queue_enter(q, buf, IOBuf_t *, ioChain);
63: [IOQueue.qlock unlockWith:WORK_AVAILABLE];
64: xpr_ud("enqueueIoBuf: exiting. IOBuf 0x%x needs_disk %s\n",
65: buf, needs_disk ? (int)"TRUE" : (int)"FALSE", 3,4,5);
66: }
67:
68: /*
69: * Wakeup up I/O threads. Used for 'diskBecameReady' notification.
70: */
71: - (void)ioThreadWakeup
72: {
73: [IOQueue.qlock lock];
74: [IOQueue.qlock unlockWith:WORK_AVAILABLE];
75: }
76:
77: /*
78: * Unlock IOQueue.qlock, updating condition variable as appropriate.
79: */
80: - (void)unlockIOQueue
81: {
82: int queue_state;
83: IODiskReadyState lastReady = [self lastReadyState];
84:
85: /*
86: * There's still work to do when:
87: * -- q_nodisk non-empty, or
88: * -- q_disk non-empty and we "really" have a disk.
89: */
90:
91: if((!queue_empty(&IOQueue.q_nodisk)) ||
92: ((!queue_empty(&IOQueue.q_disk)) &&
93: (lastReady != IO_NoDisk) &&
94: (lastReady != IO_Ejecting) &&
95: (!IOQueue.ejectPending)
96: )
97: ) {
98: queue_state = WORK_AVAILABLE;
99: }
100: else
101: queue_state = NO_WORK_AVAILABLE;
102: [IOQueue.qlock unlockWith:queue_state];
103: }
104:
105: @end
106:
107: /*
108: * I/O thread. 'n' copies of this are IOForkThread()'d in the init:sender:
109: * method. This handles IOBufs which have been enqueued by exported methods
110: * (like read: and write:).
111: *
112: * This thread merely loops doing the following:
113: * -- get an IOBuf off of the IOQueue.
114: * -- perform the task specified in IOBuf->command.
115: * -- if async request, ioComplete: the result, else
116: * wakeup the waiting thread.
117: */
118:
119: volatile void unix_thread(IOQueue_t *ioQ)
120: {
121: int threadNum;
122: IODiskReadyState lastReady;
123:
124: /*
125: * First assign ourself a thread number.
126: */
127: [ioQ->qlock lock];
128: threadNum = ioQ->numThreads++;
129: [ioQ->qlock unlock];
130: xpr_uth("unix_thread %d: starting\n", threadNum, 2,3,4,5);
131: while(1) {
132:
133: /*
134: * Wait for something to do. Keep the lock until we
135: * dequeue something.
136: */
137: [ioQ->qlock lockWhen:WORK_AVAILABLE];
138:
139: /*
140: * Service all requests which do not need a disk.
141: */
142: xpr_uth("unix_thread: servicing q_nodisk\n", 1,2,3,4,5);
143: while(!queue_empty(&ioQ->q_nodisk))
144: unixThreadDequeue(ioQ, NO, threadNum);
145:
146: /*
147: * Now service all requests which need a disk, if our disk
148: * is present.
149: */
150: xpr_uth("unix_thread: servicing q_disk\n", 1,2,3,4,5);
151: while((!queue_empty(&ioQ->q_disk)) &&
152: ([ioQ->device lastReadyState] != IO_NoDisk) &&
153: ([ioQ->device lastReadyState] != IO_Ejecting) &&
154: (!ioQ->ejectPending)) {
155: unixThreadDequeue(ioQ, YES, threadNum);
156: }
157:
158: /*
159: * If we have work to do in q_disk but we don't have a disk,
160: * ask volCheck to put up a panel. In either case, when we
161: * unlock ioQ.qlock for the last time, update its
162: * condition variable as appropriate so we and the other
163: * I/O threads working on this IOQueue know whether or not
164: * to sleep.
165: */
166: lastReady = [ioQ->device lastReadyState];
167: if((!queue_empty(&ioQ->q_disk)) &&
168: ((lastReady == IO_NoDisk) || ioQ->ejectPending)) {
169: [ioQ->device unlockIOQueue];
170: xpr_uth("unix_thread: volCheckRequest()\n", 1,2,3,4,5);
171: volCheckRequest(ioQ->device,
172: [ioQ->device diskType]);
173: }
174: else {
175: [ioQ->device unlockIOQueue];
176: }
177: }
178: /* NOT REACHED */
179: }
180:
181: /*
182: * Process a request at the head of one of the queues in *ioQ.
183: * ioQ->qlock must be held on entry; it will still be held on exit (though we
184: * release it when doing a command dispatch).
185: */
186: static void unixThreadDequeue(IOQueue_t *ioQ,
187: BOOL needs_disk,
188: int threadNum)
189: {
190: queue_head_t *q;
191: IOBuf_t *IOBuf;
192:
193: if(needs_disk)
194: q = &ioQ->q_disk;
195: else
196: q = &ioQ->q_nodisk;
197: if(queue_empty(q)) {
198: IOLog("unixThreadDequeue: Empty queue!\n");
199: return;
200: }
201: IOBuf = (IOBuf_t *)queue_first(q);
202: queue_remove(q,
203: IOBuf,
204: IOBuf_t *,
205: ioChain);
206:
207: /*
208: * For proper timing of the call to volCheckEjecting, we have to
209: * determine right now - while the queue is locked - whether this
210: * is an eject command. If so, the call to volCheckEjecting() will
211: * prevent other I/O threads from attempting to perform I/Os from
212: * q_disk.
213: *
214: * The numDiskIos counter allows the thread doing an eject command
215: * to wait for all other pending Disk I/Os to complete before doing
216: * the eject.
217: *
218: * We also have to keep that cruft ejectPending flag around, since
219: * the call to volCheckEjecting() doesn't result in an immediate
220: * update of lastReadyState...
221: */
222: if(needs_disk) {
223: [ioQ->ejectLock lock];
224: ioQ->numDiskIos++;
225: if(IOBuf->command == @selector(deviceEject:threadNum:)) {
226: ioQ->ejectPending = TRUE;
227: volCheckEjecting(IOBuf->device,
228: [IOBuf->device diskType]);
229: }
230: [ioQ->ejectLock unlockWith:ioQ->numDiskIos];
231: }
232:
233: [ioQ->qlock unlock];
234: xpr_uth("unix_thread %d: IOBuf 0x%x received\n",
235: threadNum, IOBuf, 3,4,5);
236: [IOBuf->device perform:IOBuf->command
237: with:(id)IOBuf
238: with:(id)threadNum];
239: [ioQ->qlock lock];
240: if(needs_disk) {
241: /*
242: * Enable possible waiting eject command.
243: */
244: [ioQ->ejectLock lock];
245: ioQ->numDiskIos--;
246: [ioQ->ejectLock unlockWith:ioQ->numDiskIos];
247: }
248: }
249:
250: /* end of IOThread.m */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.