ddraw: Set dwMaxVertexCount to 2048.
[wine.git] / dlls / ntdll / thread.c
blob26cf99cdad0ac0444775c8800aaef25f058e2e9e
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 #include "ntstatus.h"
27 #define WIN32_NO_STATUS
28 #include "winternl.h"
29 #include "wine/debug.h"
30 #include "ntdll_misc.h"
31 #include "ddk/wdm.h"
32 #include "wine/exception.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(thread);
35 WINE_DECLARE_DEBUG_CHANNEL(relay);
36 WINE_DECLARE_DEBUG_CHANNEL(pid);
37 WINE_DECLARE_DEBUG_CHANNEL(timestamp);
39 struct _KUSER_SHARED_DATA *user_shared_data = (void *)0x7ffe0000;
41 struct debug_info
43 unsigned int str_pos; /* current position in strings buffer */
44 unsigned int out_pos; /* current position in output buffer */
45 char strings[1020]; /* buffer for temporary strings */
46 char output[1020]; /* current output line */
49 C_ASSERT( sizeof(struct debug_info) == 0x800 );
51 static int nb_debug_options;
52 static struct __wine_debug_channel *debug_options;
54 static inline struct debug_info *get_info(void)
56 #ifdef _WIN64
57 return (struct debug_info *)((TEB32 *)((char *)NtCurrentTeb() + 0x2000) + 1);
58 #else
59 return (struct debug_info *)(NtCurrentTeb() + 1);
60 #endif
63 static void init_options(void)
65 unsigned int offset = page_size * (sizeof(void *) / 4);
67 debug_options = (struct __wine_debug_channel *)((char *)NtCurrentTeb()->Peb + offset);
68 while (debug_options[nb_debug_options].name[0]) nb_debug_options++;
71 /* add a string to the output buffer */
72 static int append_output( struct debug_info *info, const char *str, size_t len )
74 if (len >= sizeof(info->output) - info->out_pos)
76 __wine_dbg_write( info->output, info->out_pos );
77 info->out_pos = 0;
78 ERR_(thread)( "debug buffer overflow:\n" );
79 __wine_dbg_write( str, len );
80 RtlRaiseStatus( STATUS_BUFFER_OVERFLOW );
82 memcpy( info->output + info->out_pos, str, len );
83 info->out_pos += len;
84 return len;
87 /***********************************************************************
88 * __wine_dbg_get_channel_flags (NTDLL.@)
90 * Get the flags to use for a given channel, possibly setting them too in case of lazy init
92 unsigned char __cdecl __wine_dbg_get_channel_flags( struct __wine_debug_channel *channel )
94 int min, max, pos, res;
95 unsigned char default_flags;
97 if (!debug_options) init_options();
99 min = 0;
100 max = nb_debug_options - 1;
101 while (min <= max)
103 pos = (min + max) / 2;
104 res = strcmp( channel->name, debug_options[pos].name );
105 if (!res) return debug_options[pos].flags;
106 if (res < 0) max = pos - 1;
107 else min = pos + 1;
109 /* no option for this channel */
110 default_flags = debug_options[nb_debug_options].flags;
111 if (channel->flags & (1 << __WINE_DBCL_INIT)) channel->flags = default_flags;
112 return default_flags;
115 /***********************************************************************
116 * __wine_dbg_strdup (NTDLL.@)
118 const char * __cdecl __wine_dbg_strdup( const char *str )
120 struct debug_info *info = get_info();
121 unsigned int pos = info->str_pos;
122 size_t n = strlen( str ) + 1;
124 assert( n <= sizeof(info->strings) );
125 if (pos + n > sizeof(info->strings)) pos = 0;
126 info->str_pos = pos + n;
127 return memcpy( info->strings + pos, str, n );
130 /***********************************************************************
131 * __wine_dbg_header (NTDLL.@)
133 int __cdecl __wine_dbg_header( enum __wine_debug_class cls, struct __wine_debug_channel *channel,
134 const char *function )
136 static const char * const classes[] = { "fixme", "err", "warn", "trace" };
137 struct debug_info *info = get_info();
138 char *pos = info->output;
140 if (!(__wine_dbg_get_channel_flags( channel ) & (1 << cls))) return -1;
142 /* only print header if we are at the beginning of the line */
143 if (info->out_pos) return 0;
145 if (TRACE_ON(timestamp))
147 ULONG ticks = NtGetTickCount();
148 pos += sprintf( pos, "%3lu.%03lu:", ticks / 1000, ticks % 1000 );
150 if (TRACE_ON(pid)) pos += sprintf( pos, "%04lx:", GetCurrentProcessId() );
151 pos += sprintf( pos, "%04lx:", GetCurrentThreadId() );
152 if (function && cls < ARRAY_SIZE( classes ))
153 pos += snprintf( pos, sizeof(info->output) - (pos - info->output), "%s:%s:%s ",
154 classes[cls], channel->name, function );
155 info->out_pos = pos - info->output;
156 return info->out_pos;
159 /***********************************************************************
160 * __wine_dbg_write (NTDLL.@)
162 int WINAPI __wine_dbg_write( const char *str, unsigned int len )
164 struct wine_dbg_write_params params = { str, len };
166 return WINE_UNIX_CALL( unix_wine_dbg_write, &params );
169 /***********************************************************************
170 * __wine_dbg_output (NTDLL.@)
172 int __cdecl __wine_dbg_output( const char *str )
174 struct debug_info *info = get_info();
175 const char *end = strrchr( str, '\n' );
176 int ret = 0;
178 if (end)
180 ret += append_output( info, str, end + 1 - str );
181 __wine_dbg_write( info->output, info->out_pos );
182 info->out_pos = 0;
183 str = end + 1;
185 if (*str) ret += append_output( info, str, strlen( str ));
186 return ret;
190 /***********************************************************************
191 * set_native_thread_name
193 void set_native_thread_name( DWORD tid, const char *name )
195 THREAD_NAME_INFORMATION info;
196 HANDLE h = GetCurrentThread();
197 WCHAR nameW[64];
199 if (tid != -1)
201 OBJECT_ATTRIBUTES attr;
202 CLIENT_ID cid;
204 attr.Length = sizeof(attr);
205 attr.RootDirectory = 0;
206 attr.Attributes = 0;
207 attr.ObjectName = NULL;
208 attr.SecurityDescriptor = NULL;
209 attr.SecurityQualityOfService = NULL;
211 cid.UniqueProcess = 0;
212 cid.UniqueThread = ULongToHandle( tid );
214 if (NtOpenThread( &h, THREAD_QUERY_LIMITED_INFORMATION, &attr, &cid )) return;
217 if (name)
219 mbstowcs( nameW, name, ARRAY_SIZE(nameW) );
220 nameW[ARRAY_SIZE(nameW) - 1] = '\0';
222 else
224 nameW[0] = '\0';
227 RtlInitUnicodeString( &info.ThreadName, nameW );
228 NtSetInformationThread( h, ThreadWineNativeThreadName, &info, sizeof(info) );
230 if (h != GetCurrentThread())
231 NtClose(h);
235 /***********************************************************************
236 * RtlExitUserThread (NTDLL.@)
238 void WINAPI RtlExitUserThread( ULONG status )
240 ULONG last;
242 NtQueryInformationThread( GetCurrentThread(), ThreadAmILastThread, &last, sizeof(last), NULL );
243 if (last) RtlExitUserProcess( status );
244 LdrShutdownThread();
245 for (;;) NtTerminateThread( GetCurrentThread(), status );
249 /***********************************************************************
250 * RtlCreateUserThread (NTDLL.@)
252 NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, SECURITY_DESCRIPTOR *descr,
253 BOOLEAN suspended, ULONG zero_bits,
254 SIZE_T stack_reserve, SIZE_T stack_commit,
255 PRTL_THREAD_START_ROUTINE start, void *param,
256 HANDLE *handle_ptr, CLIENT_ID *id )
258 ULONG flags = suspended ? THREAD_CREATE_FLAGS_CREATE_SUSPENDED : 0;
259 ULONG_PTR buffer[offsetof( PS_ATTRIBUTE_LIST, Attributes[2] ) / sizeof(ULONG_PTR)];
260 PS_ATTRIBUTE_LIST *attr_list = (PS_ATTRIBUTE_LIST *)buffer;
261 HANDLE handle, actctx;
262 TEB *teb;
263 ULONG ret;
264 NTSTATUS status;
265 CLIENT_ID client_id;
266 OBJECT_ATTRIBUTES attr;
268 attr_list->TotalLength = sizeof(buffer);
269 attr_list->Attributes[0].Attribute = PS_ATTRIBUTE_CLIENT_ID;
270 attr_list->Attributes[0].Size = sizeof(client_id);
271 attr_list->Attributes[0].ValuePtr = &client_id;
272 attr_list->Attributes[0].ReturnLength = NULL;
273 attr_list->Attributes[1].Attribute = PS_ATTRIBUTE_TEB_ADDRESS;
274 attr_list->Attributes[1].Size = sizeof(teb);
275 attr_list->Attributes[1].ValuePtr = &teb;
276 attr_list->Attributes[1].ReturnLength = NULL;
278 InitializeObjectAttributes( &attr, NULL, 0, NULL, descr );
280 RtlGetActiveActivationContext( &actctx );
281 if (actctx) flags |= THREAD_CREATE_FLAGS_CREATE_SUSPENDED;
283 status = NtCreateThreadEx( &handle, THREAD_ALL_ACCESS, &attr, process, start, param,
284 flags, zero_bits, stack_commit, stack_reserve, attr_list );
285 if (!status)
287 if (actctx)
289 ULONG_PTR cookie;
290 RtlActivateActivationContextEx( 0, teb, actctx, &cookie );
291 if (!suspended) NtResumeThread( handle, &ret );
293 if (id) *id = client_id;
294 if (handle_ptr) *handle_ptr = handle;
295 else NtClose( handle );
297 if (actctx) RtlReleaseActivationContext( actctx );
298 return status;
302 /**********************************************************************
303 * RtlCreateUserStack (NTDLL.@)
305 NTSTATUS WINAPI RtlCreateUserStack( SIZE_T commit, SIZE_T reserve, ULONG zero_bits,
306 SIZE_T commit_align, SIZE_T reserve_align, INITIAL_TEB *stack )
308 PROCESS_STACK_ALLOCATION_INFORMATION alloc;
309 NTSTATUS status;
311 TRACE("commit %#Ix, reserve %#Ix, zero_bits %lu, commit_align %#Ix, reserve_align %#Ix, stack %p\n",
312 commit, reserve, zero_bits, commit_align, reserve_align, stack);
314 if (!commit_align || !reserve_align)
315 return STATUS_INVALID_PARAMETER;
317 if (!commit || !reserve)
319 IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress );
320 if (!reserve) reserve = nt->OptionalHeader.SizeOfStackReserve;
321 if (!commit) commit = nt->OptionalHeader.SizeOfStackCommit;
324 reserve = (reserve + reserve_align - 1) & ~(reserve_align - 1);
325 commit = (commit + commit_align - 1) & ~(commit_align - 1);
327 if (reserve < commit) reserve = commit;
328 if (reserve < 0x100000) reserve = 0x100000;
329 reserve = (reserve + 0xffff) & ~0xffff; /* round to 64K boundary */
331 alloc.ReserveSize = reserve;
332 alloc.ZeroBits = zero_bits;
333 status = NtSetInformationProcess( GetCurrentProcess(), ProcessThreadStackAllocation,
334 &alloc, sizeof(alloc) );
335 if (!status)
337 void *addr = alloc.StackBase;
338 SIZE_T size = page_size;
340 NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_NOACCESS );
341 addr = (char *)alloc.StackBase + page_size;
342 NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE | PAGE_GUARD );
343 addr = (char *)alloc.StackBase + 2 * page_size;
344 size = reserve - 2 * page_size;
345 NtAllocateVirtualMemory( GetCurrentProcess(), &addr, 0, &size, MEM_COMMIT, PAGE_READWRITE );
347 /* note: limit is lower than base since the stack grows down */
348 stack->OldStackBase = 0;
349 stack->OldStackLimit = 0;
350 stack->DeallocationStack = alloc.StackBase;
351 stack->StackBase = (char *)alloc.StackBase + reserve;
352 stack->StackLimit = (char *)alloc.StackBase + 2 * page_size;
354 return status;
358 /**********************************************************************
359 * RtlFreeUserStack (NTDLL.@)
361 void WINAPI RtlFreeUserStack( void *stack )
363 SIZE_T size = 0;
365 TRACE("stack %p\n", stack);
367 NtFreeVirtualMemory( NtCurrentProcess(), &stack, &size, MEM_RELEASE );
371 /******************************************************************************
372 * RtlGetNtGlobalFlags (NTDLL.@)
374 ULONG WINAPI RtlGetNtGlobalFlags(void)
376 return NtCurrentTeb()->Peb->NtGlobalFlag;
380 /******************************************************************************
381 * RtlPushFrame (NTDLL.@)
383 void WINAPI RtlPushFrame( TEB_ACTIVE_FRAME *frame )
385 frame->Previous = NtCurrentTeb()->ActiveFrame;
386 NtCurrentTeb()->ActiveFrame = frame;
390 /******************************************************************************
391 * RtlPopFrame (NTDLL.@)
393 void WINAPI RtlPopFrame( TEB_ACTIVE_FRAME *frame )
395 NtCurrentTeb()->ActiveFrame = frame->Previous;
399 /******************************************************************************
400 * RtlGetFrame (NTDLL.@)
402 TEB_ACTIVE_FRAME * WINAPI RtlGetFrame(void)
404 return NtCurrentTeb()->ActiveFrame;
408 /******************************************************************************
409 * RtlIsCurrentThread (NTDLL.@)
411 BOOLEAN WINAPI RtlIsCurrentThread( HANDLE handle )
413 return handle == NtCurrentThread() || !NtCompareObjects( handle, NtCurrentThread() );
417 /***********************************************************************
418 * RtlSetThreadErrorMode (NTDLL.@)
420 NTSTATUS WINAPI RtlSetThreadErrorMode( DWORD mode, LPDWORD oldmode )
422 if (mode & ~0x70)
423 return STATUS_INVALID_PARAMETER_1;
425 if (oldmode)
426 *oldmode = NtCurrentTeb()->HardErrorMode;
428 NtCurrentTeb()->HardErrorMode = mode;
429 return STATUS_SUCCESS;
433 /***********************************************************************
434 * RtlGetThreadErrorMode (NTDLL.@)
436 DWORD WINAPI RtlGetThreadErrorMode( void )
438 return NtCurrentTeb()->HardErrorMode;
442 /***********************************************************************
443 * _errno (NTDLL.@)
445 int * CDECL _errno(void)
447 return (int *)&NtCurrentTeb()->TlsSlots[NTDLL_TLS_ERRNO];
451 /***********************************************************************
452 * Fibers
453 ***********************************************************************/
456 static GLOBAL_FLS_DATA fls_data = { { NULL }, { &fls_data.fls_list_head, &fls_data.fls_list_head } };
458 static RTL_CRITICAL_SECTION fls_section;
459 static RTL_CRITICAL_SECTION_DEBUG fls_critsect_debug =
461 0, 0, &fls_section,
462 { &fls_critsect_debug.ProcessLocksList, &fls_critsect_debug.ProcessLocksList },
463 0, 0, { (DWORD_PTR)(__FILE__ ": fls_section") }
465 static RTL_CRITICAL_SECTION fls_section = { &fls_critsect_debug, -1, 0, 0, 0, 0 };
467 #define MAX_FLS_DATA_COUNT 0xff0
469 static void lock_fls_data(void)
471 RtlEnterCriticalSection( &fls_section );
474 static void unlock_fls_data(void)
476 RtlLeaveCriticalSection( &fls_section );
479 static unsigned int fls_chunk_size( unsigned int chunk_index )
481 return 0x10 << chunk_index;
484 static unsigned int fls_index_from_chunk_index( unsigned int chunk_index, unsigned int index )
486 return 0x10 * ((1 << chunk_index) - 1) + index;
489 static unsigned int fls_chunk_index_from_index( unsigned int index, unsigned int *index_in_chunk )
491 unsigned int chunk_index = 0;
493 while (index >= fls_chunk_size( chunk_index ))
494 index -= fls_chunk_size( chunk_index++ );
496 *index_in_chunk = index;
497 return chunk_index;
500 TEB_FLS_DATA *fls_alloc_data(void)
502 TEB_FLS_DATA *fls;
504 if (!(fls = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*fls) )))
505 return NULL;
507 lock_fls_data();
508 InsertTailList( &fls_data.fls_list_head, &fls->fls_list_entry );
509 unlock_fls_data();
511 return fls;
515 /***********************************************************************
516 * RtlFlsAlloc (NTDLL.@)
518 NTSTATUS WINAPI DECLSPEC_HOTPATCH RtlFlsAlloc( PFLS_CALLBACK_FUNCTION callback, ULONG *ret_index )
520 unsigned int chunk_index, index, i;
521 FLS_INFO_CHUNK *chunk;
522 TEB_FLS_DATA *fls;
524 if (!(fls = NtCurrentTeb()->FlsSlots)
525 && !(NtCurrentTeb()->FlsSlots = fls = fls_alloc_data()))
526 return STATUS_NO_MEMORY;
528 lock_fls_data();
529 for (i = 0; i < ARRAY_SIZE(fls_data.fls_callback_chunks); ++i)
531 if (!fls_data.fls_callback_chunks[i] || fls_data.fls_callback_chunks[i]->count < fls_chunk_size( i ))
532 break;
535 if ((chunk_index = i) == ARRAY_SIZE(fls_data.fls_callback_chunks))
537 unlock_fls_data();
538 return STATUS_NO_MEMORY;
541 if ((chunk = fls_data.fls_callback_chunks[chunk_index]))
543 for (index = 0; index < fls_chunk_size( chunk_index ); ++index)
544 if (!chunk->callbacks[index].callback)
545 break;
546 assert( index < fls_chunk_size( chunk_index ));
548 else
550 fls_data.fls_callback_chunks[chunk_index] = chunk = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
551 offsetof(FLS_INFO_CHUNK, callbacks) + sizeof(*chunk->callbacks) * fls_chunk_size( chunk_index ));
552 if (!chunk)
554 unlock_fls_data();
555 return STATUS_NO_MEMORY;
558 if (chunk_index)
560 index = 0;
562 else
564 chunk->count = 1; /* FLS index 0 is prohibited. */
565 chunk->callbacks[0].callback = (void *)~(ULONG_PTR)0;
566 index = 1;
570 ++chunk->count;
571 chunk->callbacks[index].callback = callback ? callback : (PFLS_CALLBACK_FUNCTION)~(ULONG_PTR)0;
573 if ((*ret_index = fls_index_from_chunk_index( chunk_index, index )) > fls_data.fls_high_index)
574 fls_data.fls_high_index = *ret_index;
576 unlock_fls_data();
578 return STATUS_SUCCESS;
582 /***********************************************************************
583 * RtlFlsFree (NTDLL.@)
585 NTSTATUS WINAPI DECLSPEC_HOTPATCH RtlFlsFree( ULONG index )
587 PFLS_CALLBACK_FUNCTION callback;
588 unsigned int chunk_index, idx;
589 FLS_INFO_CHUNK *chunk;
590 LIST_ENTRY *entry;
592 lock_fls_data();
594 if (!index || index > fls_data.fls_high_index)
596 unlock_fls_data();
597 return STATUS_INVALID_PARAMETER;
600 chunk_index = fls_chunk_index_from_index( index, &idx );
601 if (!(chunk = fls_data.fls_callback_chunks[chunk_index])
602 || !(callback = chunk->callbacks[idx].callback))
604 unlock_fls_data();
605 return STATUS_INVALID_PARAMETER;
608 for (entry = fls_data.fls_list_head.Flink; entry != &fls_data.fls_list_head; entry = entry->Flink)
610 TEB_FLS_DATA *fls = CONTAINING_RECORD(entry, TEB_FLS_DATA, fls_list_entry);
612 if (fls->fls_data_chunks[chunk_index] && fls->fls_data_chunks[chunk_index][idx + 1])
614 if (callback != (void *)~(ULONG_PTR)0)
616 TRACE_(relay)("Calling FLS callback %p, arg %p.\n", callback,
617 fls->fls_data_chunks[chunk_index][idx + 1]);
619 callback( fls->fls_data_chunks[chunk_index][idx + 1] );
621 fls->fls_data_chunks[chunk_index][idx + 1] = NULL;
625 --chunk->count;
626 chunk->callbacks[idx].callback = NULL;
628 unlock_fls_data();
629 return STATUS_SUCCESS;
633 /***********************************************************************
634 * RtlFlsSetValue (NTDLL.@)
636 NTSTATUS WINAPI DECLSPEC_HOTPATCH RtlFlsSetValue( ULONG index, void *data )
638 unsigned int chunk_index, idx;
639 TEB_FLS_DATA *fls;
641 if (!index || index >= MAX_FLS_DATA_COUNT)
642 return STATUS_INVALID_PARAMETER;
644 if (!(fls = NtCurrentTeb()->FlsSlots)
645 && !(NtCurrentTeb()->FlsSlots = fls = fls_alloc_data()))
646 return STATUS_NO_MEMORY;
648 chunk_index = fls_chunk_index_from_index( index, &idx );
650 if (!fls->fls_data_chunks[chunk_index] &&
651 !(fls->fls_data_chunks[chunk_index] = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY,
652 (fls_chunk_size( chunk_index ) + 1) * sizeof(*fls->fls_data_chunks[chunk_index]) )))
653 return STATUS_NO_MEMORY;
655 fls->fls_data_chunks[chunk_index][idx + 1] = data;
657 return STATUS_SUCCESS;
661 /***********************************************************************
662 * RtlFlsGetValue (NTDLL.@)
664 NTSTATUS WINAPI DECLSPEC_HOTPATCH RtlFlsGetValue( ULONG index, void **data )
666 unsigned int chunk_index, idx;
667 TEB_FLS_DATA *fls;
669 if (!index || index >= MAX_FLS_DATA_COUNT || !(fls = NtCurrentTeb()->FlsSlots))
670 return STATUS_INVALID_PARAMETER;
672 chunk_index = fls_chunk_index_from_index( index, &idx );
674 *data = fls->fls_data_chunks[chunk_index] ? fls->fls_data_chunks[chunk_index][idx + 1] : NULL;
675 return STATUS_SUCCESS;
679 /***********************************************************************
680 * RtlProcessFlsData (NTDLL.@)
682 void WINAPI DECLSPEC_HOTPATCH RtlProcessFlsData( void *teb_fls_data, ULONG flags )
684 TEB_FLS_DATA *fls = teb_fls_data;
685 unsigned int i, index;
687 TRACE_(thread)( "teb_fls_data %p, flags %#lx.\n", teb_fls_data, flags );
689 if (flags & ~3)
690 FIXME_(thread)( "Unknown flags %#lx.\n", flags );
692 if (!fls)
693 return;
695 if (flags & 1)
697 lock_fls_data();
698 for (i = 0; i < ARRAY_SIZE(fls->fls_data_chunks); ++i)
700 if (!fls->fls_data_chunks[i] || !fls_data.fls_callback_chunks[i]
701 || !fls_data.fls_callback_chunks[i]->count)
702 continue;
704 for (index = 0; index < fls_chunk_size( i ); ++index)
706 PFLS_CALLBACK_FUNCTION callback = fls_data.fls_callback_chunks[i]->callbacks[index].callback;
708 if (!fls->fls_data_chunks[i][index + 1])
709 continue;
711 if (callback && callback != (void *)~(ULONG_PTR)0)
713 TRACE_(relay)("Calling FLS callback %p, arg %p.\n", callback,
714 fls->fls_data_chunks[i][index + 1]);
716 callback( fls->fls_data_chunks[i][index + 1] );
718 fls->fls_data_chunks[i][index + 1] = NULL;
721 /* Not using RemoveEntryList() as Windows does not zero list entry here. */
722 fls->fls_list_entry.Flink->Blink = fls->fls_list_entry.Blink;
723 fls->fls_list_entry.Blink->Flink = fls->fls_list_entry.Flink;
724 unlock_fls_data();
727 if (flags & 2)
729 for (i = 0; i < ARRAY_SIZE(fls->fls_data_chunks); ++i)
730 RtlFreeHeap( GetProcessHeap(), 0, fls->fls_data_chunks[i] );
732 RtlFreeHeap( GetProcessHeap(), 0, fls );