1c2c66affSColin Finck /*
2c2c66affSColin Finck * COPYRIGHT: See COPYING in the top level directory
3c2c66affSColin Finck * PROJECT: ReactOS Runtime Library
4c2c66affSColin Finck * PURPOSE: Slist Routines
5c2c66affSColin Finck * FILE: lib/rtl/slist.c
6c2c66affSColin Finck * PROGRAMERS: Stefan Ginsberg (stefan.ginsberg@reactos.org)
7c2c66affSColin Finck * Timo Kreuzer (timo.kreuzer@reactos.org)
8c2c66affSColin Finck */
9c2c66affSColin Finck
10c2c66affSColin Finck /* INCLUDES *****************************************************************/
11c2c66affSColin Finck
12c2c66affSColin Finck #include <rtl.h>
13c2c66affSColin Finck
14c2c66affSColin Finck #define NDEBUG
15c2c66affSColin Finck #include <debug.h>
16c2c66affSColin Finck
17c2c66affSColin Finck #ifdef _WIN64
18c2c66affSColin Finck BOOLEAN RtlpUse16ByteSLists = -1;
19c2c66affSColin Finck #endif
20c2c66affSColin Finck
21c2c66affSColin Finck /* FUNCTIONS ***************************************************************/
22c2c66affSColin Finck
23c2c66affSColin Finck VOID
24c2c66affSColin Finck NTAPI
RtlInitializeSListHead(_Out_ PSLIST_HEADER SListHead)25c2c66affSColin Finck RtlInitializeSListHead(
26c2c66affSColin Finck _Out_ PSLIST_HEADER SListHead)
27c2c66affSColin Finck {
28c2c66affSColin Finck #if defined(_WIN64)
29c2c66affSColin Finck /* Make sure the header is 16 byte aligned */
30c2c66affSColin Finck if (((ULONG_PTR)SListHead & 0xf) != 0)
31c2c66affSColin Finck {
32c2c66affSColin Finck DPRINT1("Unaligned SListHead: 0x%p\n", SListHead);
33c2c66affSColin Finck RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT);
34c2c66affSColin Finck }
35c2c66affSColin Finck
36c2c66affSColin Finck /* Initialize the Region member */
37c2c66affSColin Finck #if defined(_IA64_)
38c2c66affSColin Finck /* On Itanium we store the region in the list head */
39c2c66affSColin Finck SListHead->Region = (ULONG_PTR)SListHead & VRN_MASK;
40c2c66affSColin Finck #else
41c2c66affSColin Finck /* On amd64 we don't need to store anything */
42c2c66affSColin Finck SListHead->Region = 0;
43c2c66affSColin Finck #endif /* _IA64_ */
44c2c66affSColin Finck #endif /* _WIN64 */
45c2c66affSColin Finck
46c2c66affSColin Finck SListHead->Alignment = 0;
47c2c66affSColin Finck }
48c2c66affSColin Finck
49c2c66affSColin Finck PSLIST_ENTRY
50c2c66affSColin Finck NTAPI
RtlFirstEntrySList(_In_ const SLIST_HEADER * SListHead)51c2c66affSColin Finck RtlFirstEntrySList(
52c2c66affSColin Finck _In_ const SLIST_HEADER *SListHead)
53c2c66affSColin Finck {
54c2c66affSColin Finck #if defined(_WIN64)
55c2c66affSColin Finck /* Check if the header is initialized as 16 byte header */
56c2c66affSColin Finck if (SListHead->Header16.HeaderType)
57c2c66affSColin Finck {
58c2c66affSColin Finck return (PVOID)(SListHead->Region & ~0xFLL);
59c2c66affSColin Finck }
60c2c66affSColin Finck else
61c2c66affSColin Finck {
62c2c66affSColin Finck union {
63c2c66affSColin Finck ULONG64 Region;
64c2c66affSColin Finck struct {
65c2c66affSColin Finck ULONG64 Reserved:4;
66c2c66affSColin Finck ULONG64 NextEntry:39;
67c2c66affSColin Finck ULONG64 Reserved2:21;
68c2c66affSColin Finck } Bits;
69c2c66affSColin Finck } Pointer;
70c2c66affSColin Finck
71c2c66affSColin Finck #if defined(_IA64_)
72c2c66affSColin Finck /* On Itanium we stored the region in the list head */
73c2c66affSColin Finck Pointer.Region = SListHead->Region;
74c2c66affSColin Finck #else
75c2c66affSColin Finck /* On amd64 we just use the list head itself */
76c2c66affSColin Finck Pointer.Region = (ULONG64)SListHead;
77c2c66affSColin Finck #endif
78c2c66affSColin Finck Pointer.Bits.NextEntry = SListHead->Header8.NextEntry;
79c2c66affSColin Finck return (PVOID)Pointer.Region;
80c2c66affSColin Finck }
81c2c66affSColin Finck #else
82c2c66affSColin Finck return SListHead->Next.Next;
83c2c66affSColin Finck #endif
84c2c66affSColin Finck }
85c2c66affSColin Finck
86c2c66affSColin Finck WORD
87c2c66affSColin Finck NTAPI
RtlQueryDepthSList(_In_ PSLIST_HEADER SListHead)88c2c66affSColin Finck RtlQueryDepthSList(
89c2c66affSColin Finck _In_ PSLIST_HEADER SListHead)
90c2c66affSColin Finck {
91c2c66affSColin Finck #if defined(_WIN64)
92c2c66affSColin Finck return (USHORT)(SListHead->Alignment & 0xffff);
93c2c66affSColin Finck #else
94c2c66affSColin Finck return SListHead->Depth;
95c2c66affSColin Finck #endif
96c2c66affSColin Finck }
97c2c66affSColin Finck
98c2c66affSColin Finck PSLIST_ENTRY
99c2c66affSColin Finck FASTCALL
RtlInterlockedPushListSList(_Inout_ PSLIST_HEADER SListHead,_Inout_ __drv_aliasesMem PSLIST_ENTRY List,_Inout_ PSLIST_ENTRY ListEnd,_In_ ULONG Count)100c2c66affSColin Finck RtlInterlockedPushListSList(
101c2c66affSColin Finck _Inout_ PSLIST_HEADER SListHead,
102c2c66affSColin Finck _Inout_ __drv_aliasesMem PSLIST_ENTRY List,
103c2c66affSColin Finck _Inout_ PSLIST_ENTRY ListEnd,
104c2c66affSColin Finck _In_ ULONG Count)
105c2c66affSColin Finck {
106c2c66affSColin Finck #ifdef _WIN64
107f4d4b31cSTimo Kreuzer SLIST_HEADER OldSListHead, NewSListHead;
108f4d4b31cSTimo Kreuzer PSLIST_ENTRY FirstEntry;
109f4d4b31cSTimo Kreuzer
110f4d4b31cSTimo Kreuzer ASSERT(((ULONG_PTR)SListHead & 0xF) == 0);
111f4d4b31cSTimo Kreuzer ASSERT(((ULONG_PTR)List & 0xF) == 0);
112f4d4b31cSTimo Kreuzer
113f4d4b31cSTimo Kreuzer if (RtlpUse16ByteSLists)
114f4d4b31cSTimo Kreuzer {
115f4d4b31cSTimo Kreuzer BOOLEAN exchanged;
116f4d4b31cSTimo Kreuzer
117f4d4b31cSTimo Kreuzer do
118f4d4b31cSTimo Kreuzer {
119f4d4b31cSTimo Kreuzer /* Capture the current SListHead */
120f4d4b31cSTimo Kreuzer OldSListHead = *SListHead;
121f4d4b31cSTimo Kreuzer
122f4d4b31cSTimo Kreuzer /* Link the last list entry */
123f4d4b31cSTimo Kreuzer FirstEntry = (PSLIST_ENTRY)(SListHead->Region & ~0xFLL);
124f4d4b31cSTimo Kreuzer ListEnd->Next = FirstEntry;
125f4d4b31cSTimo Kreuzer
126f4d4b31cSTimo Kreuzer /* Set up new SListHead */
127f4d4b31cSTimo Kreuzer NewSListHead = OldSListHead;
128f4d4b31cSTimo Kreuzer NewSListHead.Header16.Depth += Count;
129f4d4b31cSTimo Kreuzer NewSListHead.Header16.Sequence++;
130f4d4b31cSTimo Kreuzer NewSListHead.Region = (ULONG64)List;
131f4d4b31cSTimo Kreuzer NewSListHead.Header16.HeaderType = 1;
132f4d4b31cSTimo Kreuzer NewSListHead.Header16.Init = 1;
133f4d4b31cSTimo Kreuzer
134f4d4b31cSTimo Kreuzer /* Atomically exchange the SlistHead with the new one */
135*01c05f33STimo Kreuzer exchanged = _InterlockedCompareExchange128((PLONG64)SListHead,
136f4d4b31cSTimo Kreuzer NewSListHead.Region,
137f4d4b31cSTimo Kreuzer NewSListHead.Alignment,
138*01c05f33STimo Kreuzer (PLONG64)&OldSListHead);
139f4d4b31cSTimo Kreuzer } while (!exchanged);
140f4d4b31cSTimo Kreuzer
141f4d4b31cSTimo Kreuzer return FirstEntry;
142f4d4b31cSTimo Kreuzer }
143f4d4b31cSTimo Kreuzer else
144f4d4b31cSTimo Kreuzer {
145f4d4b31cSTimo Kreuzer ULONG64 Compare;
146f4d4b31cSTimo Kreuzer
147f4d4b31cSTimo Kreuzer /* ListHead and List must be in the same region */
148f4d4b31cSTimo Kreuzer ASSERT(((ULONG64)SListHead & 0xFFFFF80000000000ull) ==
149f4d4b31cSTimo Kreuzer ((ULONG64)List & 0xFFFFF80000000000ull));
150f4d4b31cSTimo Kreuzer
151f4d4b31cSTimo Kreuzer /* Read the header */
152f4d4b31cSTimo Kreuzer OldSListHead = *SListHead;
153f4d4b31cSTimo Kreuzer
154f4d4b31cSTimo Kreuzer do
155f4d4b31cSTimo Kreuzer {
156f4d4b31cSTimo Kreuzer /* Construct the address from the header bits and the list head pointer */
157f4d4b31cSTimo Kreuzer FirstEntry = (PSLIST_ENTRY)((OldSListHead.Header8.NextEntry << 4) |
158f4d4b31cSTimo Kreuzer ((ULONG64)SListHead & 0xFFFFF80000000000ull));
159f4d4b31cSTimo Kreuzer
160f4d4b31cSTimo Kreuzer /* Link the last list entry */
161f4d4b31cSTimo Kreuzer ListEnd->Next = FirstEntry;
162f4d4b31cSTimo Kreuzer
163f4d4b31cSTimo Kreuzer /* Create a new header */
164f4d4b31cSTimo Kreuzer NewSListHead = OldSListHead;
165f4d4b31cSTimo Kreuzer NewSListHead.Header8.NextEntry = (ULONG64)List >> 4;
166f4d4b31cSTimo Kreuzer NewSListHead.Header8.Depth += Count;
167f4d4b31cSTimo Kreuzer NewSListHead.Header8.Sequence++;
168f4d4b31cSTimo Kreuzer
169f4d4b31cSTimo Kreuzer /* Try to exchange atomically */
170f4d4b31cSTimo Kreuzer Compare = OldSListHead.Alignment;
171f4d4b31cSTimo Kreuzer OldSListHead.Alignment = InterlockedCompareExchange64((PLONG64)&SListHead->Alignment,
172f4d4b31cSTimo Kreuzer NewSListHead.Alignment,
173f4d4b31cSTimo Kreuzer Compare);
174f4d4b31cSTimo Kreuzer } while (OldSListHead.Alignment != Compare);
175f4d4b31cSTimo Kreuzer
176f4d4b31cSTimo Kreuzer /* Return the old first entry */
177f4d4b31cSTimo Kreuzer return FirstEntry;
178f4d4b31cSTimo Kreuzer }
179c2c66affSColin Finck #else
180c2c66affSColin Finck SLIST_HEADER OldHeader, NewHeader;
181c2c66affSColin Finck ULONGLONG Compare;
182c2c66affSColin Finck
183c2c66affSColin Finck /* Read the header */
184c2c66affSColin Finck OldHeader = *SListHead;
185c2c66affSColin Finck
186c2c66affSColin Finck do
187c2c66affSColin Finck {
188c2c66affSColin Finck /* Link the last list entry */
189c2c66affSColin Finck ListEnd->Next = OldHeader.Next.Next;
190c2c66affSColin Finck
191c2c66affSColin Finck /* Create a new header */
192c2c66affSColin Finck NewHeader = OldHeader;
193c2c66affSColin Finck NewHeader.Next.Next = List;
194c2c66affSColin Finck NewHeader.Depth += Count;
195c2c66affSColin Finck NewHeader.Sequence++;
196c2c66affSColin Finck
197c2c66affSColin Finck /* Try to exchange atomically */
198c2c66affSColin Finck Compare = OldHeader.Alignment;
199c2c66affSColin Finck OldHeader.Alignment = InterlockedCompareExchange64((PLONGLONG)&SListHead->Alignment,
200c2c66affSColin Finck NewHeader.Alignment,
201c2c66affSColin Finck Compare);
202c2c66affSColin Finck }
203c2c66affSColin Finck while (OldHeader.Alignment != Compare);
204c2c66affSColin Finck
205c2c66affSColin Finck /* Return the old first entry */
206c2c66affSColin Finck return OldHeader.Next.Next;
207c2c66affSColin Finck #endif /* _WIN64 */
208c2c66affSColin Finck }
209c2c66affSColin Finck
210c2c66affSColin Finck
211c2c66affSColin Finck #if !defined(_M_IX86) && !defined(_M_AMD64)
212c2c66affSColin Finck
213c2c66affSColin Finck _WARN("C based S-List functions can bugcheck, if not handled properly in kernel")
214c2c66affSColin Finck
215c2c66affSColin Finck #ifdef _WIN64
216c2c66affSColin Finck #error "No generic S-List functions for WIN64!"
217c2c66affSColin Finck #endif
218c2c66affSColin Finck
219c2c66affSColin Finck /* This variable must be used in kernel mode to prevent the system from
220c2c66affSColin Finck bugchecking on non-present kernel memory. If this variable is set to TRUE
221c2c66affSColin Finck an exception needs to be dispatched. */
222c2c66affSColin Finck BOOLEAN RtlpExpectSListFault;
223c2c66affSColin Finck
224c2c66affSColin Finck PSLIST_ENTRY
225c2c66affSColin Finck NTAPI
RtlInterlockedPushEntrySList(_Inout_ PSLIST_HEADER SListHead,_Inout_ __drv_aliasesMem PSLIST_ENTRY SListEntry)226c2c66affSColin Finck RtlInterlockedPushEntrySList(
227c2c66affSColin Finck _Inout_ PSLIST_HEADER SListHead,
228c2c66affSColin Finck _Inout_ __drv_aliasesMem PSLIST_ENTRY SListEntry)
229c2c66affSColin Finck {
230c2c66affSColin Finck SLIST_HEADER OldHeader, NewHeader;
231c2c66affSColin Finck ULONGLONG Compare;
232c2c66affSColin Finck
233c2c66affSColin Finck /* Read the header */
234c2c66affSColin Finck OldHeader = *SListHead;
235c2c66affSColin Finck
236c2c66affSColin Finck do
237c2c66affSColin Finck {
238c2c66affSColin Finck /* Link the list entry */
239c2c66affSColin Finck SListEntry->Next = OldHeader.Next.Next;
240c2c66affSColin Finck
241c2c66affSColin Finck /* Create a new header */
242c2c66affSColin Finck NewHeader = OldHeader;
243c2c66affSColin Finck NewHeader.Next.Next = SListEntry;
244c2c66affSColin Finck NewHeader.Depth++;
245c2c66affSColin Finck NewHeader.Sequence++;
246c2c66affSColin Finck
247c2c66affSColin Finck /* Try to exchange atomically */
248c2c66affSColin Finck Compare = OldHeader.Alignment;
249c2c66affSColin Finck OldHeader.Alignment = InterlockedCompareExchange64((PLONGLONG)&SListHead->Alignment,
250c2c66affSColin Finck NewHeader.Alignment,
251c2c66affSColin Finck Compare);
252c2c66affSColin Finck }
253c2c66affSColin Finck while (OldHeader.Alignment != Compare);
254c2c66affSColin Finck
255c2c66affSColin Finck /* Return the old first entry */
256c2c66affSColin Finck return OldHeader.Next.Next;
257c2c66affSColin Finck }
258c2c66affSColin Finck
259c2c66affSColin Finck PSLIST_ENTRY
260c2c66affSColin Finck NTAPI
RtlInterlockedPopEntrySList(_Inout_ PSLIST_HEADER SListHead)261c2c66affSColin Finck RtlInterlockedPopEntrySList(
262c2c66affSColin Finck _Inout_ PSLIST_HEADER SListHead)
263c2c66affSColin Finck {
264c2c66affSColin Finck SLIST_HEADER OldHeader, NewHeader;
265c2c66affSColin Finck ULONGLONG Compare;
266c2c66affSColin Finck
267c2c66affSColin Finck restart:
268c2c66affSColin Finck
269c2c66affSColin Finck /* Read the header */
270c2c66affSColin Finck OldHeader = *SListHead;
271c2c66affSColin Finck
272c2c66affSColin Finck do
273c2c66affSColin Finck {
274c2c66affSColin Finck /* Check for empty list */
275c2c66affSColin Finck if (OldHeader.Next.Next == NULL)
276c2c66affSColin Finck {
277c2c66affSColin Finck return NULL;
278c2c66affSColin Finck }
279c2c66affSColin Finck
280c2c66affSColin Finck /* Create a new header */
281c2c66affSColin Finck NewHeader = OldHeader;
282c2c66affSColin Finck
283c2c66affSColin Finck /* HACK to let the kernel know that we are doing slist-magic */
284c2c66affSColin Finck RtlpExpectSListFault = TRUE;
285c2c66affSColin Finck
286c2c66affSColin Finck /* Wrapped in SEH, since OldHeader.Next.Next can already be freed */
287c2c66affSColin Finck _SEH2_TRY
288c2c66affSColin Finck {
289c2c66affSColin Finck NewHeader.Next = *OldHeader.Next.Next;
290c2c66affSColin Finck }
291c2c66affSColin Finck _SEH2_EXCEPT((SListHead->Next.Next != OldHeader.Next.Next) ?
292c2c66affSColin Finck EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
293c2c66affSColin Finck {
294c2c66affSColin Finck /* We got an exception and the list head changed.
295c2c66affSColin Finck Restart the whole operation. */
296c2c66affSColin Finck RtlpExpectSListFault = FALSE;
297c2c66affSColin Finck goto restart;
298c2c66affSColin Finck }
299c2c66affSColin Finck _SEH2_END;
300c2c66affSColin Finck
301c2c66affSColin Finck /* We are done */
302c2c66affSColin Finck RtlpExpectSListFault = FALSE;
303c2c66affSColin Finck
304c2c66affSColin Finck /* Adjust depth */
305c2c66affSColin Finck NewHeader.Depth--;
306c2c66affSColin Finck
307c2c66affSColin Finck /* Try to exchange atomically */
308c2c66affSColin Finck Compare = OldHeader.Alignment;
309c2c66affSColin Finck OldHeader.Alignment = InterlockedCompareExchange64((PLONGLONG)SListHead->Alignment,
310c2c66affSColin Finck NewHeader.Alignment,
311c2c66affSColin Finck Compare);
312c2c66affSColin Finck }
313c2c66affSColin Finck while (OldHeader.Alignment != Compare);
314c2c66affSColin Finck
315c2c66affSColin Finck return OldHeader.Next.Next;
316c2c66affSColin Finck }
317c2c66affSColin Finck
318c2c66affSColin Finck PSLIST_ENTRY
319c2c66affSColin Finck NTAPI
RtlInterlockedFlushSList(_Inout_ PSLIST_HEADER SListHead)320c2c66affSColin Finck RtlInterlockedFlushSList(
321c2c66affSColin Finck _Inout_ PSLIST_HEADER SListHead)
322c2c66affSColin Finck {
323c2c66affSColin Finck SLIST_HEADER OldHeader, NewHeader;
324c2c66affSColin Finck ULONGLONG Compare;
325c2c66affSColin Finck
326c2c66affSColin Finck /* Read the header */
327c2c66affSColin Finck OldHeader = *SListHead;
328c2c66affSColin Finck
329c2c66affSColin Finck do
330c2c66affSColin Finck {
331c2c66affSColin Finck /* Check for empty list */
332c2c66affSColin Finck if (OldHeader.Next.Next == NULL)
333c2c66affSColin Finck {
334c2c66affSColin Finck return NULL;
335c2c66affSColin Finck }
336c2c66affSColin Finck
337c2c66affSColin Finck /* Create a new header (keep the sequence number) */
338c2c66affSColin Finck NewHeader = OldHeader;
339c2c66affSColin Finck NewHeader.Next.Next = NULL;
340c2c66affSColin Finck NewHeader.Depth = 0;
341c2c66affSColin Finck
342c2c66affSColin Finck /* Try to exchange atomically */
343c2c66affSColin Finck Compare = OldHeader.Alignment;
344c2c66affSColin Finck OldHeader.Alignment = InterlockedCompareExchange64((PLONGLONG)&SListHead->Alignment,
345c2c66affSColin Finck NewHeader.Alignment,
346c2c66affSColin Finck Compare);
347c2c66affSColin Finck }
348c2c66affSColin Finck while (OldHeader.Alignment != Compare);
349c2c66affSColin Finck
350c2c66affSColin Finck /* Return the old first entry */
351c2c66affSColin Finck return OldHeader.Next.Next;
352c2c66affSColin Finck
353c2c66affSColin Finck }
354c2c66affSColin Finck
355c2c66affSColin Finck #ifdef _MSC_VER
356c2c66affSColin Finck #pragma comment(linker, "/alternatename:ExpInterlockedPopEntrySList=RtlInterlockedPopEntrySList")
357c2c66affSColin Finck #pragma comment(linker, "/alternatename:ExpInterlockedPushEntrySList=RtlInterlockedPushEntrySList")
358c2c66affSColin Finck #pragma comment(linker, "/alternatename:ExpInterlockedFlushSList=RtlInterlockedFlushSList")
359c2c66affSColin Finck #else
360c2c66affSColin Finck #pragma redefine_extname RtlInterlockedPopEntrySList ExpInterlockedPopEntrySList
361c2c66affSColin Finck #pragma redefine_extname RtlInterlockedPushEntrySList ExpInterlockedPushEntrySList
362c2c66affSColin Finck #pragma redefine_extname RtlInterlockedFlushSList ExpInterlockedFlushSList
363c2c66affSColin Finck #endif
364c2c66affSColin Finck
365c2c66affSColin Finck #endif
366c2c66affSColin Finck
367