35095eed030b42afca340f57e9d48a19f8c07d54
[reactos.git] / reactos / ntoskrnl / ke / queue.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/ke/queue.c
23 * PURPOSE: Implements kernel queues
24 * PROGRAMMER: Eric Kohl (ekohl@rz-online.de)
25 * UPDATE HISTORY:
26 * Created 04/01/2002
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include <ntoskrnl.h>
32 #define NDEBUG
33 #include <internal/debug.h>
34
35 /* FUNCTIONS *****************************************************************/
36
37
38 /*
39 * @implemented
40 */
41 VOID STDCALL
42 KeInitializeQueue(IN PKQUEUE Queue,
43 IN ULONG Count OPTIONAL)
44 {
45 KeInitializeDispatcherHeader(&Queue->Header,
46 InternalQueueType,
47 sizeof(KQUEUE)/sizeof(ULONG),
48 0);
49 InitializeListHead(&Queue->EntryListHead);
50 InitializeListHead(&Queue->ThreadListHead);
51 Queue->CurrentCount = 0;
52 Queue->MaximumCount = (Count == 0) ? (ULONG) KeNumberProcessors : Count;
53 }
54
55
56 /*
57 * @implemented
58 *
59 * Returns number of entries in the queue
60 */
61 LONG STDCALL
62 KeReadStateQueue(IN PKQUEUE Queue)
63 {
64 return(Queue->Header.SignalState);
65 }
66
67 /*
68 * Returns the previous number of entries in the queue
69 */
70 LONG STDCALL
71 KiInsertQueue(
72 IN PKQUEUE Queue,
73 IN PLIST_ENTRY Entry,
74 BOOLEAN Head
75 )
76 {
77 ULONG InitialState;
78
79 DPRINT("KiInsertQueue(Queue %x, Entry %x)\n", Queue, Entry);
80
81 InitialState = Queue->Header.SignalState;
82
83 if (Head)
84 {
85 InsertHeadList(&Queue->EntryListHead, Entry);
86 }
87 else
88 {
89 InsertTailList(&Queue->EntryListHead, Entry);
90 }
91
92 //inc. num entries in queue
93 Queue->Header.SignalState++;
94
95 /* Why the KeGetCurrentThread()->Queue != Queue?
96 * KiInsertQueue might be called from an APC for the current thread.
97 * -Gunnar
98 */
99 if (Queue->CurrentCount < Queue->MaximumCount &&
100 !IsListEmpty(&Queue->Header.WaitListHead) &&
101 KeGetCurrentThread()->Queue != Queue)
102 {
103 KiDispatcherObjectWake(&Queue->Header, IO_NO_INCREMENT);
104 }
105
106 return InitialState;
107 }
108
109
110
111 /*
112 * @implemented
113 */
114 LONG STDCALL
115 KeInsertHeadQueue(IN PKQUEUE Queue,
116 IN PLIST_ENTRY Entry)
117 {
118 LONG Result;
119 KIRQL OldIrql;
120
121 OldIrql = KeAcquireDispatcherDatabaseLock();
122 Result = KiInsertQueue(Queue,Entry,TRUE);
123 KeReleaseDispatcherDatabaseLock(OldIrql);
124
125 return Result;
126 }
127
128
129 /*
130 * @implemented
131 */
132 LONG STDCALL
133 KeInsertQueue(IN PKQUEUE Queue,
134 IN PLIST_ENTRY Entry)
135 {
136 LONG Result;
137 KIRQL OldIrql;
138
139 OldIrql = KeAcquireDispatcherDatabaseLock();
140 Result = KiInsertQueue(Queue,Entry,FALSE);
141 KeReleaseDispatcherDatabaseLock(OldIrql);
142
143 return Result;
144 }
145
146
147 /*
148 * @implemented
149 */
150 PLIST_ENTRY STDCALL
151 KeRemoveQueue(IN PKQUEUE Queue,
152 IN KPROCESSOR_MODE WaitMode,
153 IN PLARGE_INTEGER Timeout OPTIONAL)
154 {
155
156 PLIST_ENTRY ListEntry;
157 NTSTATUS Status;
158 PKTHREAD Thread = KeGetCurrentThread();
159 KIRQL OldIrql;
160
161 OldIrql = KeAcquireDispatcherDatabaseLock ();
162
163 if (Thread->Queue != Queue)
164 {
165 /*
166 * INVESTIGATE: What is the Thread->QueueListEntry used for? It's linked it into the
167 * Queue->ThreadListHead when the thread registers with the queue and unlinked when
168 * the thread registers with a new queue. The Thread->Queue already tells us what
169 * queue the thread is registered with.
170 * -Gunnar
171 */
172
173 //unregister thread from previous queue (if any)
174 if (Thread->Queue)
175 {
176 RemoveEntryList(&Thread->QueueListEntry);
177 Thread->Queue->CurrentCount--;
178
179 if (Thread->Queue->CurrentCount < Thread->Queue->MaximumCount &&
180 !IsListEmpty(&Thread->Queue->EntryListHead))
181 {
182 KiDispatcherObjectWake(&Thread->Queue->Header, 0);
183 }
184 }
185
186 // register thread with this queue
187 InsertTailList(&Queue->ThreadListHead, &Thread->QueueListEntry);
188 Thread->Queue = Queue;
189 }
190 else /* if (Thread->Queue == Queue) */
191 {
192 //dec. num running threads
193 Queue->CurrentCount--;
194 }
195
196
197
198
199 while (TRUE)
200 {
201 if (Queue->CurrentCount < Queue->MaximumCount && !IsListEmpty(&Queue->EntryListHead))
202 {
203 ListEntry = RemoveHeadList(&Queue->EntryListHead);
204 //dec. num entries in queue
205 Queue->Header.SignalState--;
206 //inc. num running threads
207 Queue->CurrentCount++;
208
209 KeReleaseDispatcherDatabaseLock(OldIrql);
210 return ListEntry;
211 }
212 else
213 {
214 //inform KeWaitXxx that we are holding disp. lock
215 Thread->WaitNext = TRUE;
216 Thread->WaitIrql = OldIrql;
217
218 Status = KeWaitForSingleObject(Queue,
219 WrQueue,
220 WaitMode,
221 TRUE, //bAlertable
222 Timeout);
223
224 if (Status == STATUS_TIMEOUT || Status == STATUS_USER_APC)
225 {
226 return (PVOID)Status;
227 }
228
229 OldIrql = KeAcquireDispatcherDatabaseLock ();
230 }
231 }
232 }
233
234
235 /*
236 * @implemented
237 */
238 PLIST_ENTRY STDCALL
239 KeRundownQueue(IN PKQUEUE Queue)
240 {
241 PLIST_ENTRY EnumEntry;
242 PKTHREAD Thread;
243 KIRQL OldIrql;
244
245 DPRINT("KeRundownQueue(Queue %x)\n", Queue);
246
247 /* I'm just guessing how this should work:-/
248 * -Gunnar
249 */
250
251 OldIrql = KeAcquireDispatcherDatabaseLock ();
252
253 //no thread must wait on queue at rundown
254 ASSERT(IsListEmpty(&Queue->Header.WaitListHead));
255
256 // unlink threads and clear their Thread->Queue
257 while (!IsListEmpty(&Queue->ThreadListHead))
258 {
259 EnumEntry = RemoveHeadList(&Queue->ThreadListHead);
260 Thread = CONTAINING_RECORD(EnumEntry, KTHREAD, QueueListEntry);
261 Thread->Queue = NULL;
262 }
263
264 if (IsListEmpty(&Queue->EntryListHead))
265 {
266 EnumEntry = NULL;
267 }
268 else
269 {
270 EnumEntry = Queue->EntryListHead.Flink;
271 }
272
273 KeReleaseDispatcherDatabaseLock (OldIrql);
274
275 return EnumEntry;
276 }
277
278 /* EOF */