hidclass.sys: Remove old reports from WINE_HIDP_PREPARSED_DATA.
[wine.git] / dlls / ntdll / thread.c
blob37dc7c8ab375df8872c548b6220e5badfcb4327c
1 /*
2 * NT threads support
4 * Copyright 1996, 2003 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <assert.h>
22 #include <stdarg.h>
23 #include <limits.h>
24 #include <sys/types.h>
26 #define NONAMELESSUNION
27 #include "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "winternl.h"
30 #include "wine/debug.h"
31 #include "ntdll_misc.h"
32 #include "ddk/wdm.h"
33 #include "wine/exception.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(thread);
36 WINE_DECLARE_DEBUG_CHANNEL(relay);
37 WINE_DECLARE_DEBUG_CHANNEL(pid);
38 WINE_DECLARE_DEBUG_CHANNEL(timestamp);
40 struct _KUSER_SHARED_DATA *user_shared_data = (void *)0x7ffe0000;
42 struct debug_info
44 unsigned int str_pos; /* current position in strings buffer */
45 unsigned int out_pos; /* current position in output buffer */
46 char strings[1020]; /* buffer for temporary strings */
47 char output[1020]; /* current output line */
50 C_ASSERT( sizeof(struct debug_info) == 0x800 );
52 static int nb_debug_options;
53 static struct __wine_debug_channel *debug_options;
55 static inline struct debug_info *get_info(void)
57 #ifdef _WIN64
58 return (struct debug_info *)((TEB32 *)((char *)NtCurrentTeb() + 0x2000) + 1);
59 #else
60 return (struct debug_info *)(NtCurrentTeb() + 1);
61 #endif
64 static void init_options(void)
66 unsigned int offset = page_size * (sizeof(void *) / 4);
68 debug_options = (struct __wine_debug_channel *)((char *)NtCurrentTeb()->Peb + offset);
69 while (debug_options[nb_debug_options].name[0]) nb_debug_options++;
72 /* add a string to the output buffer */
73 static int append_output( struct debug_info *info, const char *str, size_t len )
75 if (len >= sizeof(info->output) - info->out_pos)
77 __wine_dbg_write( info->output, info->out_pos );
78 info->out_pos = 0;
79 ERR_(thread)( "debug buffer overflow:\n" );
80 __wine_dbg_write( str, len );
81 RtlRaiseStatus( STATUS_BUFFER_OVERFLOW );
83 memcpy( info->output + info->out_pos, str, len );
84 info->out_pos += len;
85 return len;
88 /***********************************************************************
89 * __wine_dbg_get_channel_flags (NTDLL.@)
91 * Get the flags to use for a given channel, possibly setting them too in case of lazy init
93 unsigned char __cdecl __wine_dbg_get_channel_flags( struct __wine_debug_channel *channel )
95 int min, max, pos, res;
96 unsigned char default_flags;
98 if (!debug_options) init_options();
100 min = 0;
101 max = nb_debug_options - 1;
102 while (min <= max)
104 pos = (min + max) / 2;
105 res = strcmp( channel->name, debug_options[pos].name );
106 if (!res) return debug_options[pos].flags;
107 if (res < 0) max = pos - 1;
108 else min = pos + 1;
110 /* no option for this channel */
111 default_flags = debug_options[nb_debug_options].flags;
112 if (channel->flags & (1 << __WINE_DBCL_INIT)) channel->flags = default_flags;
113 return default_flags;
116 /***********************************************************************
117 * __wine_dbg_strdup (NTDLL.@)
119 const char * __cdecl __wine_dbg_strdup( const char *str )
121 struct debug_info *info = get_info();
122 unsigned int pos = info->str_pos;
123 size_t n = strlen( str ) + 1;
125 assert( n <= sizeof(info->strings) );
126 if (pos + n > sizeof(info->strings)) pos = 0;
127 info->str_pos = pos + n;
128 return memcpy( info->strings + pos, str, n );
131 /***********************************************************************
132 * __wine_dbg_header (NTDLL.@)
134 int __cdecl __wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
135 const char *function )
137 static const char * const classes[] = { "fixme", "err", "warn", "trace" };
138 struct debug_info *info = get_info();
139 char *pos = info->output;
141 if (!(__wine_dbg_get_channel_flags( channel ) & (1 << cls))) return -1;
143 /* only print header if we are at the beginning of the line */
144 if (info->out_pos) return 0;
146 if (TRACE_ON(timestamp))
148 ULONG ticks = NtGetTickCount();
149 pos += sprintf( pos, "%3u.%03u:", ticks / 1000, ticks % 1000 );
151 if (TRACE_ON(pid)) pos += sprintf( pos, "%04x:", GetCurrentProcessId() );
152 pos += sprintf( pos, "%04x:", GetCurrentThreadId() );
153 if (function && cls < ARRAY_SIZE( classes ))
154 pos += snprintf( pos, sizeof(info->output) - (pos - info->output), "%s:%s:%s ",
155 classes[cls], channel->name, function );
156 info->out_pos = pos - info->output;
157 return info->out_pos;
160 /***********************************************************************
161 * __wine_dbg_output (NTDLL.@)
163 int __cdecl __wine_dbg_output( const char *str )
165 struct debug_info *info = get_info();
166 const char *end = strrchr( str, '\n' );
167 int ret = 0;
169 if (end)
171 ret += append_output( info, str, end + 1 - str );
172 __wine_dbg_write( info->output, info->out_pos );
173 info->out_pos = 0;
174 str = end + 1;
176 if (*str) ret += append_output( info, str, strlen( str ));
177 return ret;
181 /***********************************************************************
182 * RtlExitUserThread (NTDLL.@)
184 void WINAPI RtlExitUserThread( ULONG status )
186 ULONG last;
188 NtQueryInformationThread( GetCurrentThread(), ThreadAmILastThread, &last, sizeof(last), NULL );
189 if (last) RtlExitUserProcess( status );
190 LdrShutdownThread();
191 for (;;) NtTerminateThread( GetCurrentThread(), status );
195 /***********************************************************************
196 * RtlUserThreadStart (NTDLL.@)
198 #ifdef __i386__
199 __ASM_STDCALL_FUNC( RtlUserThreadStart, 8,
200 "movl %ebx,8(%esp)\n\t" /* arg */
201 "movl %eax,4(%esp)\n\t" /* entry */
202 "jmp " __ASM_NAME("call_thread_func") )
204 /* wrapper to call BaseThreadInitThunk */
205 extern void DECLSPEC_NORETURN call_thread_func_wrapper( void *thunk, PRTL_THREAD_START_ROUTINE entry, void *arg );
206 __ASM_GLOBAL_FUNC( call_thread_func_wrapper,
207 "pushl %ebp\n\t"
208 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
209 __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
210 "movl %esp,%ebp\n\t"
211 __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
212 "subl $4,%esp\n\t"
213 "andl $~0xf,%esp\n\t"
214 "xorl %ecx,%ecx\n\t"
215 "movl 12(%ebp),%edx\n\t"
216 "movl 16(%ebp),%eax\n\t"
217 "movl %eax,(%esp)\n\t"
218 "call *8(%ebp)" )
220 void DECLSPEC_HIDDEN call_thread_func( PRTL_THREAD_START_ROUTINE entry, void *arg )
222 __TRY
224 TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", entry, arg );
225 call_thread_func_wrapper( pBaseThreadInitThunk, entry, arg );
227 __EXCEPT(call_unhandled_exception_filter)
229 NtTerminateProcess( GetCurrentProcess(), GetExceptionCode() );
231 __ENDTRY
234 #else /* __i386__ */
236 void WINAPI RtlUserThreadStart( PRTL_THREAD_START_ROUTINE entry, void *arg )
238 __TRY
240 TRACE_(relay)( "\1Starting thread proc %p (arg=%p)\n", entry, arg );
241 pBaseThreadInitThunk( 0, (LPTHREAD_START_ROUTINE)entry, arg );
243 __EXCEPT(call_unhandled_exception_filter)
245 NtTerminateProcess( GetCurrentProcess(), GetExceptionCode() );
247 __ENDTRY
250 #endif /* __i386__ */
253 /***********************************************************************
254 * RtlCreateUserThread (NTDLL.@)
256 NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, SECURITY_DESCRIPTOR *descr,
257 BOOLEAN suspended, ULONG zero_bits,
258 SIZE_T stack_reserve, SIZE_T stack_commit,
259 PRTL_THREAD_START_ROUTINE start, void *param,
260 HANDLE *handle_ptr, CLIENT_ID *id )
262 ULONG flags = suspended ? THREAD_CREATE_FLAGS_CREATE_SUSPENDED : 0;
263 ULONG_PTR buffer[offsetof( PS_ATTRIBUTE_LIST, Attributes[2] ) / sizeof(ULONG_PTR)];
264 PS_ATTRIBUTE_LIST *attr_list = (PS_ATTRIBUTE_LIST *)buffer;
265 HANDLE handle, actctx;
266 TEB *teb;
267 ULONG ret;
268 NTSTATUS status;
269 CLIENT_ID client_id;
270 OBJECT_ATTRIBUTES attr;
272 attr_list->TotalLength = sizeof(buffer);
273 attr_list->Attributes[0].Attribute = PS_ATTRIBUTE_CLIENT_ID;
274 attr_list->Attributes[0].Size = sizeof(client_id);
275 attr_list->Attributes[0].ValuePtr = &client_id;
276 attr_list->Attributes[0].ReturnLength = NULL;
277 attr_list->Attributes[1].Attribute = PS_ATTRIBUTE_TEB_ADDRESS;
278 attr_list->Attributes[1].Size = sizeof(teb);
279 attr_list->Attributes[1].ValuePtr = &teb;
280 attr_list->Attributes[1].ReturnLength = NULL;
282 InitializeObjectAttributes( &attr, NULL, 0, NULL, descr );
284 RtlGetActiveActivationContext( &actctx );
285 if (actctx) flags |= THREAD_CREATE_FLAGS_CREATE_SUSPENDED;
287 status = NtCreateThreadEx( &handle, THREAD_ALL_ACCESS, &attr, process, start, param,
288 flags, zero_bits, stack_commit, stack_reserve, attr_list );
289 if (!status)
291 if (actctx)
293 ULONG_PTR cookie;
294 RtlActivateActivationContextEx( 0, teb, actctx, &cookie );
295 if (!suspended) NtResumeThread( handle, &ret );
297 if (id) *id = client_id;
298 if (handle_ptr) *handle_ptr = handle;
299 else NtClose( handle );
301 if (actctx) RtlReleaseActivationContext( actctx );
302 return status;
306 /**********************************************************************
307 * RtlCreateUserStack (NTDLL.@)
309 NTSTATUS WINAPI RtlCreateUserStack( SIZE_T commit, SIZE_T reserve, ULONG zero_bits,
310 SIZE_T commit_align, SIZE_T reserve_align, INITIAL_TEB *stack )
312 PROCESS_STACK_ALLOCATION_INFORMATION alloc;
313 NTSTATUS status;
315 TRACE("commit %#lx, reserve %#lx, zero_bits %u, commit_align %#lx, reserve_align %#lx, stack %p\n",
316 commit, reserve, zero_bits, commit_align, reserve_align, stack);
318 if (!commit_align || !reserve_align)
319 return STATUS_INVALID_PARAMETER;
321 if (!commit || !reserve)
323 IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress );
324 if (!reserve) reserve = nt->OptionalHeader.SizeOfStackReserve;
325 if (!commit) commit = nt->OptionalHeader.SizeOfStackCommit;
328 reserve = (reserve + reserve_align - 1) & ~(reserve_align - 1);
329 commit = (commit + commit_align - 1) & ~(commit_align - 1);
331 if (reserve < commit) reserve = commit;
332 if (reserve < 0x100000) reserve = 0x100000;
333 reserve = (reserve + 0xffff) & ~0xffff; /* round to 64K boundary */
335 alloc.ReserveSize = reserve;
336 alloc.ZeroBits = zero_bits;
337 status = NtSetInformationProcess( GetCurrentProcess(), ProcessThreadStackAllocation,
338 &alloc, sizeof(alloc) );
339 if (!status)
341 void *addr = alloc.StackBase;
342 SIZE_T size = page_size;
344 NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_NOACCESS );
345 addr = (char *)alloc.StackBase + page_size;
346 NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD );
347 addr = (char *)alloc.StackBase + 2 * page_size;
348 size = reserve - 2 * page_size;
349 NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE );
351 /* note: limit is lower than base since the stack grows down */
352 stack->OldStackBase = 0;
353 stack->OldStackLimit = 0;
354 stack->DeallocationStack = alloc.StackBase;
355 stack->StackBase = (char *)alloc.StackBase + reserve;
356 stack->StackLimit = (char *)alloc.StackBase + 2 * page_size;
358 return status;
362 /**********************************************************************
363 * RtlFreeUserStack (NTDLL.@)
365 void WINAPI RtlFreeUserStack( void *stack )
367 SIZE_T size = 0;
369 TRACE("stack %p\n", stack);
371 NtFreeVirtualMemory( NtCurrentProcess(), &stack, &size, MEM_RELEASE );
375 /******************************************************************************
376 * RtlGetNtGlobalFlags (NTDLL.@)
378 ULONG WINAPI RtlGetNtGlobalFlags(void)
380 return NtCurrentTeb()->Peb->NtGlobalFlag;
384 /******************************************************************************
385 * RtlPushFrame (NTDLL.@)
387 void WINAPI RtlPushFrame( TEB_ACTIVE_FRAME *frame )
389 frame->Previous = NtCurrentTeb()->ActiveFrame;
390 NtCurrentTeb()->ActiveFrame = frame;
394 /******************************************************************************
395 * RtlPopFrame (NTDLL.@)
397 void WINAPI RtlPopFrame( TEB_ACTIVE_FRAME *frame )
399 NtCurrentTeb()->ActiveFrame = frame->Previous;
403 /******************************************************************************
404 * RtlGetFrame (NTDLL.@)
406 TEB_ACTIVE_FRAME * WINAPI RtlGetFrame(void)
408 return NtCurrentTeb()->ActiveFrame;
412 /***********************************************************************
413 * Fibers
414 ***********************************************************************/
417 static GLOBAL_FLS_DATA fls_data = { { NULL }, { &fls_data.fls_list_head, &fls_data.fls_list_head } };
419 static RTL_CRITICAL_SECTION fls_section;
420 static RTL_CRITICAL_SECTION_DEBUG fls_critsect_debug =
422 0, 0, &fls_section,
423 { &fls_critsect_debug.ProcessLocksList, &fls_critsect_debug.ProcessLocksList },
424 0, 0, { (DWORD_PTR)(__FILE__ ": fls_section") }
426 static RTL_CRITICAL_SECTION fls_section = { &fls_critsect_debug, -1, 0, 0, 0, 0 };
428 #define MAX_FLS_DATA_COUNT 0xff0
430 static void lock_fls_data(void)
432 RtlEnterCriticalSection( &fls_section );
435 static void unlock_fls_data(void)
437 RtlLeaveCriticalSection( &fls_section );
440 static unsigned int fls_chunk_size( unsigned int chunk_index )
442 return 0x10 << chunk_index;
445 static unsigned int fls_index_from_chunk_index( unsigned int chunk_index, unsigned int index )
447 return 0x10 * ((1 << chunk_index) - 1) + index;
450 static unsigned int fls_chunk_index_from_index( unsigned int index, unsigned int *index_in_chunk )
452 unsigned int chunk_index = 0;
454 while (index >= fls_chunk_size( chunk_index ))
455 index -= fls_chunk_size( chunk_index++ );
457 *index_in_chunk = index;
458 return chunk_index;
461 TEB_FLS_DATA *fls_alloc_data(void)
463 TEB_FLS_DATA *fls;
465 if (!(fls = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*fls) )))
466 return NULL;
468 lock_fls_data();
469 InsertTailList( &fls_data.fls_list_head, &fls->fls_list_entry );
470 unlock_fls_data();
472 return fls;
476 /***********************************************************************
477 * RtlFlsAlloc (NTDLL.@)
479 NTSTATUS WINAPI DECLSPEC_HOTPATCH RtlFlsAlloc( PFLS_CALLBACK_FUNCTION callback, ULONG *ret_index )
481 unsigned int chunk_index, index, i;
482 FLS_INFO_CHUNK *chunk;
483 TEB_FLS_DATA *fls;
485 if (!(fls = NtCurrentTeb()->FlsSlots)
486 && !(NtCurrentTeb()->FlsSlots = fls = fls_alloc_data()))
487 return STATUS_NO_MEMORY;
489 lock_fls_data();
490 for (i = 0; i < ARRAY_SIZE(fls_data.fls_callback_chunks); ++i)
492 if (!fls_data.fls_callback_chunks[i] || fls_data.fls_callback_chunks[i]->count < fls_chunk_size( i ))
493 break;
496 if ((chunk_index = i) == ARRAY_SIZE(fls_data.fls_callback_chunks))
498 unlock_fls_data();
499 return STATUS_NO_MEMORY;
502 if ((chunk = fls_data.fls_callback_chunks[chunk_index]))
504 for (index = 0; index < fls_chunk_size( chunk_index ); ++index)
505 if (!chunk->callbacks[index].callback)
506 break;
507 assert( index < fls_chunk_size( chunk_index ));
509 else
511 fls_data.fls_callback_chunks[chunk_index] = chunk = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
512 offsetof(FLS_INFO_CHUNK, callbacks) + sizeof(*chunk->callbacks) * fls_chunk_size( chunk_index ));
513 if (!chunk)
515 unlock_fls_data();
516 return STATUS_NO_MEMORY;
519 if (chunk_index)
521 index = 0;
523 else
525 chunk->count = 1; /* FLS index 0 is prohibited. */
526 chunk->callbacks[0].callback = (void *)~(ULONG_PTR)0;
527 index = 1;
531 ++chunk->count;
532 chunk->callbacks[index].callback = callback ? callback : (PFLS_CALLBACK_FUNCTION)~(ULONG_PTR)0;
534 if ((*ret_index = fls_index_from_chunk_index( chunk_index, index )) > fls_data.fls_high_index)
535 fls_data.fls_high_index = *ret_index;
537 unlock_fls_data();
539 return STATUS_SUCCESS;
543 /***********************************************************************
544 * RtlFlsFree (NTDLL.@)
546 NTSTATUS WINAPI DECLSPEC_HOTPATCH RtlFlsFree( ULONG index )
548 PFLS_CALLBACK_FUNCTION callback;
549 unsigned int chunk_index, idx;
550 FLS_INFO_CHUNK *chunk;
551 LIST_ENTRY *entry;
553 lock_fls_data();
555 if (!index || index > fls_data.fls_high_index)
557 unlock_fls_data();
558 return STATUS_INVALID_PARAMETER;
561 chunk_index = fls_chunk_index_from_index( index, &idx );
562 if (!(chunk = fls_data.fls_callback_chunks[chunk_index])
563 || !(callback = chunk->callbacks[idx].callback))
565 unlock_fls_data();
566 return STATUS_INVALID_PARAMETER;
569 for (entry = fls_data.fls_list_head.Flink; entry != &fls_data.fls_list_head; entry = entry->Flink)
571 TEB_FLS_DATA *fls = CONTAINING_RECORD(entry, TEB_FLS_DATA, fls_list_entry);
573 if (fls->fls_data_chunks[chunk_index] && fls->fls_data_chunks[chunk_index][idx + 1])
575 if (callback != (void *)~(ULONG_PTR)0)
577 TRACE_(relay)("Calling FLS callback %p, arg %p.\n", callback,
578 fls->fls_data_chunks[chunk_index][idx + 1]);
580 callback( fls->fls_data_chunks[chunk_index][idx + 1] );
582 fls->fls_data_chunks[chunk_index][idx + 1] = NULL;
586 --chunk->count;
587 chunk->callbacks[idx].callback = NULL;
589 unlock_fls_data();
590 return STATUS_SUCCESS;
594 /***********************************************************************
595 * RtlFlsSetValue (NTDLL.@)
597 NTSTATUS WINAPI DECLSPEC_HOTPATCH RtlFlsSetValue( ULONG index, void *data )
599 unsigned int chunk_index, idx;
600 TEB_FLS_DATA *fls;
602 if (!index || index >= MAX_FLS_DATA_COUNT)
603 return STATUS_INVALID_PARAMETER;
605 if (!(fls = NtCurrentTeb()->FlsSlots)
606 && !(NtCurrentTeb()->FlsSlots = fls = fls_alloc_data()))
607 return STATUS_NO_MEMORY;
609 chunk_index = fls_chunk_index_from_index( index, &idx );
611 if (!fls->fls_data_chunks[chunk_index] &&
612 !(fls->fls_data_chunks[chunk_index] = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
613 (fls_chunk_size( chunk_index ) + 1) * sizeof(*fls->fls_data_chunks[chunk_index]) )))
614 return STATUS_NO_MEMORY;
616 fls->fls_data_chunks[chunk_index][idx + 1] = data;
618 return STATUS_SUCCESS;
622 /***********************************************************************
623 * RtlFlsGetValue (NTDLL.@)
625 NTSTATUS WINAPI DECLSPEC_HOTPATCH RtlFlsGetValue( ULONG index, void **data )
627 unsigned int chunk_index, idx;
628 TEB_FLS_DATA *fls;
630 if (!index || index >= MAX_FLS_DATA_COUNT || !(fls = NtCurrentTeb()->FlsSlots))
631 return STATUS_INVALID_PARAMETER;
633 chunk_index = fls_chunk_index_from_index( index, &idx );
635 *data = fls->fls_data_chunks[chunk_index] ? fls->fls_data_chunks[chunk_index][idx + 1] : NULL;
636 return STATUS_SUCCESS;
640 /***********************************************************************
641 * RtlProcessFlsData (NTDLL.@)
643 void WINAPI DECLSPEC_HOTPATCH RtlProcessFlsData( void *teb_fls_data, ULONG flags )
645 TEB_FLS_DATA *fls = teb_fls_data;
646 unsigned int i, index;
648 TRACE_(thread)( "teb_fls_data %p, flags %#x.\n", teb_fls_data, flags );
650 if (flags & ~3)
651 FIXME_(thread)( "Unknown flags %#x.\n", flags );
653 if (!fls)
654 return;
656 if (flags & 1)
658 lock_fls_data();
659 for (i = 0; i < ARRAY_SIZE(fls->fls_data_chunks); ++i)
661 if (!fls->fls_data_chunks[i] || !fls_data.fls_callback_chunks[i]
662 || !fls_data.fls_callback_chunks[i]->count)
663 continue;
665 for (index = 0; index < fls_chunk_size( i ); ++index)
667 PFLS_CALLBACK_FUNCTION callback = fls_data.fls_callback_chunks[i]->callbacks[index].callback;
669 if (!fls->fls_data_chunks[i][index + 1])
670 continue;
672 if (callback && callback != (void *)~(ULONG_PTR)0)
674 TRACE_(relay)("Calling FLS callback %p, arg %p.\n", callback,
675 fls->fls_data_chunks[i][index + 1]);
677 callback( fls->fls_data_chunks[i][index + 1] );
679 fls->fls_data_chunks[i][index + 1] = NULL;
682 /* Not using RemoveEntryList() as Windows does not zero list entry here. */
683 fls->fls_list_entry.Flink->Blink = fls->fls_list_entry.Blink;
684 fls->fls_list_entry.Blink->Flink = fls->fls_list_entry.Flink;
685 unlock_fls_data();
688 if (flags & 2)
690 for (i = 0; i < ARRAY_SIZE(fls->fls_data_chunks); ++i)
691 RtlFreeHeap( GetProcessHeap(), 0, fls->fls_data_chunks[i] );
693 RtlFreeHeap( GetProcessHeap(), 0, fls );