xref: /reactos/sdk/lib/rtl/slist.c (revision 01c05f33)
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