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
24 #include <sys/types.h>
27 #define WIN32_NO_STATUS
29 #include "wine/debug.h"
30 #include "ntdll_misc.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;
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)
57 return (struct debug_info
*)((TEB32
*)((char *)NtCurrentTeb() + 0x2000) + 1);
59 return (struct debug_info
*)(NtCurrentTeb() + 1);
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
);
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
);
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();
100 max
= nb_debug_options
- 1;
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;
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
, ¶ms
);
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' );
180 ret
+= append_output( info
, str
, end
+ 1 - str
);
181 __wine_dbg_write( info
->output
, info
->out_pos
);
185 if (*str
) ret
+= append_output( info
, str
, strlen( str
));
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();
201 OBJECT_ATTRIBUTES attr
;
204 attr
.Length
= sizeof(attr
);
205 attr
.RootDirectory
= 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;
219 mbstowcs( nameW
, name
, ARRAY_SIZE(nameW
) );
220 nameW
[ARRAY_SIZE(nameW
) - 1] = '\0';
227 RtlInitUnicodeString( &info
.ThreadName
, nameW
);
228 NtSetInformationThread( h
, ThreadWineNativeThreadName
, &info
, sizeof(info
) );
230 if (h
!= GetCurrentThread())
235 /***********************************************************************
236 * RtlExitUserThread (NTDLL.@)
238 void WINAPI
RtlExitUserThread( ULONG status
)
242 NtQueryInformationThread( GetCurrentThread(), ThreadAmILastThread
, &last
, sizeof(last
), NULL
);
243 if (last
) RtlExitUserProcess( status
);
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
;
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
);
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
);
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
;
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
) );
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
;
358 /**********************************************************************
359 * RtlFreeUserStack (NTDLL.@)
361 void WINAPI
RtlFreeUserStack( void *stack
)
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
)
423 return STATUS_INVALID_PARAMETER_1
;
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 /***********************************************************************
445 int * CDECL
_errno(void)
447 return (int *)&NtCurrentTeb()->TlsSlots
[NTDLL_TLS_ERRNO
];
451 /***********************************************************************
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
=
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
;
500 TEB_FLS_DATA
*fls_alloc_data(void)
504 if (!(fls
= RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*fls
) )))
508 InsertTailList( &fls_data
.fls_list_head
, &fls
->fls_list_entry
);
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
;
524 if (!(fls
= NtCurrentTeb()->FlsSlots
)
525 && !(NtCurrentTeb()->FlsSlots
= fls
= fls_alloc_data()))
526 return STATUS_NO_MEMORY
;
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
))
535 if ((chunk_index
= i
) == ARRAY_SIZE(fls_data
.fls_callback_chunks
))
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
)
546 assert( index
< fls_chunk_size( chunk_index
));
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
));
555 return STATUS_NO_MEMORY
;
564 chunk
->count
= 1; /* FLS index 0 is prohibited. */
565 chunk
->callbacks
[0].callback
= (void *)~(ULONG_PTR
)0;
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
;
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
;
594 if (!index
|| index
> fls_data
.fls_high_index
)
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
))
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
;
626 chunk
->callbacks
[idx
].callback
= NULL
;
629 return STATUS_SUCCESS
;
633 /***********************************************************************
634 * RtlFlsSetValue (NTDLL.@)
636 NTSTATUS WINAPI DECLSPEC_HOTPATCH
RtlFlsSetValue( ULONG index
, void *data
)
638 unsigned int chunk_index
, idx
;
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
;
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
);
690 FIXME_(thread
)( "Unknown flags %#lx.\n", flags
);
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
)
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])
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
;
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
);