Reimplemented dispatcher database lock and synchronization primitives.
[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: queue.c,v 1.10 2003/11/02 01:15:15 ekohl Exp $
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 <ddk/ntddk.h>
32 #include <ntos.h>
33 #include <internal/ke.h>
34 #include <internal/id.h>
35 #include <internal/ps.h>
36
37 #define NDEBUG
38 #include <internal/debug.h>
39
40 /* FUNCTIONS *****************************************************************/
41
42 /*
43 * @implemented
44 */
45 VOID STDCALL
46 KeInitializeQueue(IN PKQUEUE Queue,
47 IN ULONG Count OPTIONAL)
48 {
49 KeInitializeDispatcherHeader(&Queue->Header,
50 InternalQueueType,
51 sizeof(KQUEUE)/sizeof(ULONG),
52 0);
53 InitializeListHead(&Queue->EntryListHead);
54 InitializeListHead(&Queue->ThreadListHead);
55 Queue->CurrentCount = 0;
56 Queue->MaximumCount = (Count == 0) ? (ULONG) KeNumberProcessors : Count;
57 }
58
59
60 /*
61 * @implemented
62 */
63 LONG STDCALL
64 KeReadStateQueue(IN PKQUEUE Queue)
65 {
66 return(Queue->Header.SignalState);
67 }
68
69
70 LONG STDCALL
71 KiInsertQueue(
72 IN PKQUEUE Queue,
73 IN PLIST_ENTRY Entry,
74 BOOLEAN Head
75 )
76 {
77 ULONG InitialState;
78 KIRQL OldIrql;
79
80 DPRINT("KiInsertQueue(Queue %x, Entry %x)\n", Queue, Entry);
81
82 OldIrql = KeAcquireDispatcherDatabaseLock ();
83
84 InitialState = Queue->Header.SignalState;
85 Queue->Header.SignalState++;
86
87 if (Head)
88 {
89 InsertHeadList(&Queue->EntryListHead, Entry);
90 }
91 else
92 {
93 InsertTailList(&Queue->EntryListHead, Entry);
94 }
95
96 if (Queue->CurrentCount < Queue->MaximumCount && InitialState == 0)
97 {
98 KeDispatcherObjectWake(&Queue->Header);
99 }
100
101 KeReleaseDispatcherDatabaseLock(OldIrql);
102 return InitialState;
103 }
104
105
106
107 /*
108 * @implemented
109 */
110 LONG STDCALL
111 KeInsertHeadQueue(IN PKQUEUE Queue,
112 IN PLIST_ENTRY Entry)
113 {
114 return KiInsertQueue(Queue,Entry,TRUE);
115 }
116
117
118 /*
119 * @implemented
120 */
121 LONG STDCALL
122 KeInsertQueue(IN PKQUEUE Queue,
123 IN PLIST_ENTRY Entry)
124 {
125 return KiInsertQueue(Queue,Entry,FALSE);
126 }
127
128
129 /*
130 * @implemented
131 */
132 PLIST_ENTRY STDCALL
133 KeRemoveQueue(IN PKQUEUE Queue,
134 IN KPROCESSOR_MODE WaitMode,
135 IN PLARGE_INTEGER Timeout OPTIONAL)
136 {
137 PLIST_ENTRY ListEntry;
138 NTSTATUS Status;
139 PKTHREAD Thread = KeGetCurrentThread();
140 KIRQL OldIrql;
141
142 OldIrql = KeAcquireDispatcherDatabaseLock ();
143
144 //assiciate new thread with queue?
145 if (Thread->Queue != Queue)
146 {
147 //remove association from other queue
148 if (!IsListEmpty(&Thread->QueueListEntry))
149 {
150 RemoveEntryList(&Thread->QueueListEntry);
151 }
152
153 //associate with this queue
154 InsertHeadList(&Queue->ThreadListHead, &Thread->QueueListEntry);
155 Queue->CurrentCount++;
156 Thread->Queue = Queue;
157 }
158
159 if (Queue->CurrentCount <= Queue->MaximumCount && !IsListEmpty(&Queue->EntryListHead))
160 {
161 ListEntry = RemoveHeadList(&Queue->EntryListHead);
162 Queue->Header.SignalState--;
163 KeReleaseDispatcherDatabaseLock (OldIrql);
164 return ListEntry;
165 }
166
167 //need to wait for it...
168 KeReleaseDispatcherDatabaseLock (OldIrql);
169
170 Status = KeWaitForSingleObject(Queue,
171 WrQueue,
172 WaitMode,
173 TRUE,//Alertable,
174 Timeout);
175
176 if (Status == STATUS_TIMEOUT || Status == STATUS_USER_APC)
177 {
178 return (PVOID)Status;
179 }
180 else
181 {
182 OldIrql = KeAcquireDispatcherDatabaseLock ();
183 ListEntry = RemoveHeadList(&Queue->EntryListHead);
184 KeReleaseDispatcherDatabaseLock (OldIrql);
185 return ListEntry;
186 }
187
188 }
189
190
191 /*
192 * @implemented
193 */
194 PLIST_ENTRY STDCALL
195 KeRundownQueue(IN PKQUEUE Queue)
196 {
197 PLIST_ENTRY EnumEntry;
198 PKTHREAD Thread;
199 KIRQL OldIrql;
200
201 DPRINT("KeRundownQueue(Queue %x)\n", Queue);
202
203 //FIXME: should we wake thread waiting on a queue?
204
205 OldIrql = KeAcquireDispatcherDatabaseLock ();
206
207 // Clear Queue and QueueListEntry members of all threads associated with this queue
208 while (!IsListEmpty(&Queue->ThreadListHead))
209 {
210 EnumEntry = RemoveHeadList(&Queue->ThreadListHead);
211 InitializeListHead(EnumEntry);
212 Thread = CONTAINING_RECORD(EnumEntry, KTHREAD, QueueListEntry);
213 Thread->Queue = NULL;
214 }
215
216 if (!IsListEmpty(&Queue->EntryListHead))
217 EnumEntry = Queue->EntryListHead.Flink;
218 else
219 EnumEntry = NULL;
220
221 KeReleaseDispatcherDatabaseLock (OldIrql);
222
223 return EnumEntry;
224 }
225
226 /* EOF */