4 * Copyright 1996 Alexandre Julliard
17 #include "selectors.h"
21 #include "stackframe.h"
30 static void THREAD_Destroy( K32OBJ *obj );
32 const K32OBJ_OPS THREAD_Ops =
34 THREAD_Destroy /* destroy */
37 /* Is threading code initialized? */
38 BOOL32 THREAD_InitDone = FALSE;
41 /***********************************************************************
44 * Return the current thread THDB pointer.
46 THDB *THREAD_Current(void)
48 if (!THREAD_InitDone) return NULL;
49 return (THDB *)((char *)NtCurrentTeb() - (int)&((THDB *)0)->teb);
52 /***********************************************************************
55 BOOL32 THREAD_IsWin16( THDB *thdb )
57 if (!thdb || !thdb->process)
61 TDB* pTask = (TDB*)GlobalLock16( thdb->process->task );
62 return !pTask || pTask->thdb == thdb;
66 /***********************************************************************
69 * Convert a thread id to a THDB, making sure it is valid.
71 THDB *THREAD_IdToTHDB( DWORD id )
75 if (!id) return THREAD_Current();
76 thdb = THREAD_ID_TO_THDB( id );
77 if (!K32OBJ_IsValid( &thdb->header, K32OBJ_THREAD ))
79 /* Allow task handles to be used; convert to main thread */
82 TDB *pTask = (TDB *)GlobalLock16( id );
83 if (pTask) return pTask->thdb;
86 SetLastError( ERROR_INVALID_PARAMETER );
93 /***********************************************************************
96 * Add a thread to a queue.
98 void THREAD_AddQueue( THREAD_QUEUE *queue, THDB *thread )
100 THREAD_ENTRY *entry = HeapAlloc( SystemHeap, HEAP_NO_SERIALIZE,
104 entry->thread = thread;
107 entry->next = (*queue)->next;
108 (*queue)->next = entry;
110 else entry->next = entry;
115 /***********************************************************************
118 * Remove a thread from a queue.
120 void THREAD_RemoveQueue( THREAD_QUEUE *queue, THDB *thread )
122 THREAD_ENTRY *entry = *queue;
124 if (entry->next == entry) /* Only one element in the queue */
126 assert( entry->thread == thread );
132 while (entry->next->thread != thread)
135 assert( entry != *queue ); /* Have we come all the way around? */
137 if ((next = entry->next) == *queue) *queue = entry;
138 entry->next = entry->next->next;
139 entry = next; /* This is the one we want to free */
141 HeapFree( SystemHeap, 0, entry );
146 /***********************************************************************
149 THDB *THREAD_Create( PDB32 *pdb, DWORD stack_size, BOOL32 alloc_stack16,
150 int *server_thandle, int *server_phandle,
151 LPTHREAD_START_ROUTINE start_addr, LPVOID param )
155 THDB *thdb = HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(THDB) );
156 if (!thdb) return NULL;
157 thdb->header.type = K32OBJ_THREAD;
158 thdb->header.refcount = 1;
160 thdb->teb.except = (void *)-1;
161 thdb->teb.htask16 = 0; /* FIXME */
162 thdb->teb.self = &thdb->teb;
163 thdb->teb.flags = (pdb->flags & PDB32_WIN16_PROC)? 0 : TEBF_WIN32;
164 thdb->teb.tls_ptr = thdb->tls_array;
165 thdb->teb.process = pdb;
166 thdb->wait_list = &thdb->wait_struct;
167 thdb->exit_code = 0x103; /* STILL_ACTIVE */
168 thdb->entry_point = start_addr;
169 thdb->entry_arg = param;
172 /* Allocate the stack */
175 * If stacksize smaller than 1 MB, allocate 1MB
176 * (one program wanted only 10 kB, which is recommendable, but some WINE
177 * functions, noteably in the files subdir, push HUGE structures and
178 * arrays on the stack. They probably shouldn't.)
179 * If stacksize larger than 16 MB, warn the user. (We could shrink the stack
180 * but this could give more or less unexplainable crashes.)
182 if (stack_size<1024*1024)
183 stack_size = 1024 * 1024;
184 if (stack_size >= 16*1024*1024)
185 WARN(thread,"Thread stack size is %ld MB.\n",stack_size/1024/1024);
186 thdb->stack_base = VirtualAlloc(NULL,
187 stack_size + (alloc_stack16 ? 0x10000 : 0),
188 MEM_COMMIT, PAGE_EXECUTE_READWRITE );
189 if (!thdb->stack_base) goto error;
190 /* Set a guard page at the bottom of the stack */
191 VirtualProtect( thdb->stack_base, 1, PAGE_EXECUTE_READWRITE | PAGE_GUARD,
193 thdb->teb.stack_top = (char *)thdb->stack_base + stack_size;
194 thdb->teb.stack_low = thdb->stack_base;
195 thdb->exit_stack = thdb->teb.stack_top;
197 /* Allocate the TEB selector (%fs register) */
199 thdb->teb_sel = SELECTOR_AllocBlock( &thdb->teb, 0x1000, SEGMENT_DATA,
201 if (!thdb->teb_sel) goto error;
203 /* Allocate the 16-bit stack selector */
207 thdb->teb.stack_sel = SELECTOR_AllocBlock( thdb->teb.stack_top,
208 0x10000, SEGMENT_DATA,
210 if (!thdb->teb.stack_sel) goto error;
211 thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( thdb->teb.stack_sel,
212 0x10000 - sizeof(STACK16FRAME) );
215 /* Create the thread socket */
217 if (CLIENT_NewThread( thdb, server_thandle, server_phandle )) goto error;
219 /* Add thread to process's list of threads */
221 THREAD_AddQueue( &pdb->thread_list, thdb );
223 /* Create the thread event */
225 if (!(thdb->event = CreateEvent32A( NULL, FALSE, FALSE, NULL ))) goto error;
226 thdb->event = ConvertToGlobalHandle( thdb->event );
232 if (thdb->socket != -1) close( thdb->socket );
233 if (thdb->event) CloseHandle( thdb->event );
234 if (thdb->teb.stack_sel) SELECTOR_FreeBlock( thdb->teb.stack_sel, 1 );
235 if (thdb->teb_sel) SELECTOR_FreeBlock( thdb->teb_sel, 1 );
236 if (thdb->stack_base) VirtualFree( thdb->stack_base, 0, MEM_RELEASE );
237 HeapFree( SystemHeap, 0, thdb );
242 /***********************************************************************
245 static void THREAD_Destroy( K32OBJ *ptr )
247 THDB *thdb = (THDB *)ptr;
249 assert( ptr->type == K32OBJ_THREAD );
250 ptr->type = K32OBJ_UNKNOWN;
252 /* Free the associated memory */
256 /* Check if we are deleting the current thread */
259 if (fs == thdb->teb_sel)
266 CloseHandle( thdb->event );
267 close( thdb->socket );
268 SELECTOR_FreeBlock( thdb->teb_sel, 1 );
269 if (thdb->teb.stack_sel) SELECTOR_FreeBlock( thdb->teb.stack_sel, 1 );
270 HeapFree( SystemHeap, 0, thdb );
275 /***********************************************************************
278 * Start execution of a newly created thread. Does not return.
280 void THREAD_Start( THDB *thdb )
282 LPTHREAD_START_ROUTINE func = (LPTHREAD_START_ROUTINE)thdb->entry_point;
283 assert( THREAD_Current() == thdb );
285 MODULE_InitializeDLLs( thdb->process, 0, DLL_THREAD_ATTACH, NULL );
286 ExitThread( func( thdb->entry_arg ) );
290 /***********************************************************************
291 * CreateThread (KERNEL32.63)
293 HANDLE32 WINAPI CreateThread( SECURITY_ATTRIBUTES *sa, DWORD stack,
294 LPTHREAD_START_ROUTINE start, LPVOID param,
295 DWORD flags, LPDWORD id )
297 int server_handle = -1;
298 HANDLE32 handle = INVALID_HANDLE_VALUE32;
299 BOOL32 inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
301 THDB *thread = THREAD_Create( PROCESS_Current(), stack,
302 TRUE, &server_handle, NULL, start, param );
303 if (!thread) return INVALID_HANDLE_VALUE32;
304 handle = HANDLE_Alloc( PROCESS_Current(), &thread->header,
305 THREAD_ALL_ACCESS, inherit, server_handle );
306 if (handle == INVALID_HANDLE_VALUE32) goto error;
307 if (SYSDEPS_SpawnThread( thread ) == -1) goto error;
308 if (id) *id = THDB_TO_THREAD_ID( thread );
312 if (handle != INVALID_HANDLE_VALUE32) CloseHandle( handle );
313 K32OBJ_DecCount( &thread->header );
314 return INVALID_HANDLE_VALUE32;
318 /***********************************************************************
319 * ExitThread [KERNEL32.215] Ends a thread
324 void WINAPI ExitThread(
325 DWORD code) /* [in] Exit code for this thread */
327 THDB *thdb = THREAD_Current();
330 /* Remove thread from process's list */
331 THREAD_RemoveQueue( &thdb->process->thread_list, thdb );
333 MODULE_InitializeDLLs( thdb->process, 0, DLL_THREAD_DETACH, NULL );
336 thdb->exit_code = code;
338 /* cleanup the message queue, if there's one */
340 USER_QueueCleanup( thdb->teb.queue );
342 /* FIXME: should free the stack somehow */
344 /* FIXME: We cannot do this; once the current thread is destroyed,
345 synchronization primitives do not work properly. */
346 K32OBJ_DecCount( &thdb->header );
348 /* Completely unlock the system lock just in case */
349 count = SYSTEM_LOCK_COUNT();
350 while (count--) SYSTEM_UNLOCK();
351 SYSDEPS_ExitThread();
355 /***********************************************************************
356 * GetCurrentThread [KERNEL32.200] Gets pseudohandle for current thread
359 * Pseudohandle for the current thread
361 HANDLE32 WINAPI GetCurrentThread(void)
363 return CURRENT_THREAD_PSEUDOHANDLE;
367 /***********************************************************************
368 * GetCurrentThreadId [KERNEL32.201] Returns thread identifier.
371 * Thread identifier of calling thread
373 DWORD WINAPI GetCurrentThreadId(void)
375 return THDB_TO_THREAD_ID( THREAD_Current() );
379 /**********************************************************************
380 * GetLastError [KERNEL.148] [KERNEL32.227] Returns last-error code.
383 * Calling thread's last error code value.
385 DWORD WINAPI GetLastError(void)
387 THDB *thread = THREAD_Current();
388 DWORD ret = thread->last_error;
389 TRACE(thread,"0x%lx\n",ret);
394 /**********************************************************************
395 * SetLastError [KERNEL.147] [KERNEL32.497] Sets the last-error code.
400 void WINAPI SetLastError(
401 DWORD error) /* [in] Per-thread error code */
403 THDB *thread = THREAD_Current();
404 /* This one must work before we have a thread (FIXME) */
406 TRACE(thread,"%p error=0x%lx\n",thread,error);
409 thread->last_error = error;
413 /**********************************************************************
414 * SetLastErrorEx [USER32.485] Sets the last-error code.
419 void WINAPI SetLastErrorEx(
420 DWORD error, /* [in] Per-thread error code */
421 DWORD type) /* [in] Error type */
423 TRACE(thread, "(0x%08lx, 0x%08lx)\n", error,type);
430 /* Fall through for now */
432 FIXME(thread, "(error=%08lx, type=%08lx): Unhandled type\n", error,type);
435 SetLastError( error );
439 /**********************************************************************
442 DWORD THREAD_TlsAlloc(THDB *thread)
444 DWORD i, mask, ret = 0;
445 DWORD *bits = thread->process->tls_bits;
446 EnterCriticalSection( &thread->process->crit_section );
447 if (*bits == 0xffffffff)
451 if (*bits == 0xffffffff)
453 LeaveCriticalSection( &thread->process->crit_section );
454 SetLastError( ERROR_NO_MORE_ITEMS );
458 for (i = 0, mask = 1; i < 32; i++, mask <<= 1) if (!(*bits & mask)) break;
460 LeaveCriticalSection( &thread->process->crit_section );
465 /**********************************************************************
466 * TlsAlloc [KERNEL32.530] Allocates a TLS index.
468 * Allocates a thread local storage index
472 * Failure: 0xFFFFFFFF
474 DWORD WINAPI TlsAlloc(void)
476 return THREAD_TlsAlloc(THREAD_Current());
480 /**********************************************************************
481 * TlsFree [KERNEL32.531] Releases a TLS index.
483 * Releases a thread local storage index, making it available for reuse
489 BOOL32 WINAPI TlsFree(
490 DWORD index) /* [in] TLS Index to free */
493 THDB *thread = THREAD_Current();
494 DWORD *bits = thread->process->tls_bits;
497 SetLastError( ERROR_INVALID_PARAMETER );
500 EnterCriticalSection( &thread->process->crit_section );
501 if (index >= 32) bits++;
502 mask = (1 << (index & 31));
503 if (!(*bits & mask)) /* already free? */
505 LeaveCriticalSection( &thread->process->crit_section );
506 SetLastError( ERROR_INVALID_PARAMETER );
510 thread->tls_array[index] = 0;
511 /* FIXME: should zero all other thread values */
512 LeaveCriticalSection( &thread->process->crit_section );
517 /**********************************************************************
518 * TlsGetValue [KERNEL32.532] Gets value in a thread's TLS slot
521 * Success: Value stored in calling thread's TLS slot for index
522 * Failure: 0 and GetLastError returns NO_ERROR
524 LPVOID WINAPI TlsGetValue(
525 DWORD index) /* [in] TLS index to retrieve value for */
527 THDB *thread = THREAD_Current();
530 SetLastError( ERROR_INVALID_PARAMETER );
533 SetLastError( ERROR_SUCCESS );
534 return thread->tls_array[index];
538 /**********************************************************************
539 * TlsSetValue [KERNEL32.533] Stores a value in the thread's TLS slot.
545 BOOL32 WINAPI TlsSetValue(
546 DWORD index, /* [in] TLS index to set value for */
547 LPVOID value) /* [in] Value to be stored */
549 THDB *thread = THREAD_Current();
552 SetLastError( ERROR_INVALID_PARAMETER );
555 thread->tls_array[index] = value;
560 /***********************************************************************
561 * SetThreadContext [KERNEL32.670] Sets context of thread.
567 BOOL32 WINAPI SetThreadContext(
568 HANDLE32 handle, /* [in] Handle to thread with context */
569 CONTEXT *context) /* [out] Address of context structure */
571 FIXME( thread, "not implemented\n" );
575 /***********************************************************************
576 * GetThreadContext [KERNEL32.294] Retrieves context of thread.
582 BOOL32 WINAPI GetThreadContext(
583 HANDLE32 handle, /* [in] Handle to thread with context */
584 CONTEXT *context) /* [out] Address of context structure */
588 FIXME( thread, "returning dummy info\n" );
590 /* make up some plausible values for segment registers */
603 /**********************************************************************
604 * GetThreadPriority [KERNEL32.296] Returns priority for thread.
607 * Success: Thread's priority level.
608 * Failure: THREAD_PRIORITY_ERROR_RETURN
610 INT32 WINAPI GetThreadPriority(
611 HANDLE32 hthread) /* [in] Handle to thread */
613 struct get_thread_info_request req;
614 struct get_thread_info_reply reply;
615 req.handle = HANDLE_GetServerHandle( PROCESS_Current(), hthread,
616 K32OBJ_THREAD, THREAD_QUERY_INFORMATION );
617 CLIENT_SendRequest( REQ_GET_THREAD_INFO, -1, 1, &req, sizeof(req) );
618 if (CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL ))
619 return THREAD_PRIORITY_ERROR_RETURN;
620 return reply.priority;
624 /**********************************************************************
625 * SetThreadPriority [KERNEL32.514] Sets priority for thread.
631 BOOL32 WINAPI SetThreadPriority(
632 HANDLE32 hthread, /* [in] Handle to thread */
633 INT32 priority) /* [in] Thread priority level */
635 struct set_thread_info_request req;
636 req.handle = HANDLE_GetServerHandle( PROCESS_Current(), hthread,
637 K32OBJ_THREAD, THREAD_SET_INFORMATION );
638 if (req.handle == -1) return FALSE;
639 req.priority = priority;
640 req.mask = SET_THREAD_INFO_PRIORITY;
641 CLIENT_SendRequest( REQ_SET_THREAD_INFO, -1, 1, &req, sizeof(req) );
642 return !CLIENT_WaitReply( NULL, NULL, 0 );
646 /**********************************************************************
647 * SetThreadAffinityMask (KERNEL32.669)
649 DWORD WINAPI SetThreadAffinityMask( HANDLE32 hThread, DWORD dwThreadAffinityMask )
651 struct set_thread_info_request req;
652 req.handle = HANDLE_GetServerHandle( PROCESS_Current(), hThread,
653 K32OBJ_THREAD, THREAD_SET_INFORMATION );
654 if (req.handle == -1) return FALSE;
655 req.affinity = dwThreadAffinityMask;
656 req.mask = SET_THREAD_INFO_AFFINITY;
657 CLIENT_SendRequest( REQ_SET_THREAD_INFO, -1, 1, &req, sizeof(req) );
658 if (CLIENT_WaitReply( NULL, NULL, 0 )) return 0;
659 return 1; /* FIXME: should return previous value */
663 /**********************************************************************
664 * TerminateThread [KERNEL32.685] Terminates a thread
670 BOOL32 WINAPI TerminateThread(
671 HANDLE32 handle, /* [in] Handle to thread */
672 DWORD exitcode) /* [in] Exit code for thread */
674 struct terminate_thread_request req;
676 req.handle = HANDLE_GetServerHandle( PROCESS_Current(), handle,
677 K32OBJ_THREAD, THREAD_TERMINATE );
678 req.exit_code = exitcode;
679 CLIENT_SendRequest( REQ_TERMINATE_THREAD, -1, 1, &req, sizeof(req) );
680 return !CLIENT_WaitReply( NULL, NULL, 0 );
684 /**********************************************************************
685 * GetExitCodeThread [KERNEL32.???] Gets termination status of thread.
691 BOOL32 WINAPI GetExitCodeThread(
692 HANDLE32 hthread, /* [in] Handle to thread */
693 LPDWORD exitcode) /* [out] Address to receive termination status */
695 struct get_thread_info_request req;
696 struct get_thread_info_reply reply;
697 req.handle = HANDLE_GetServerHandle( PROCESS_Current(), hthread,
698 K32OBJ_THREAD, THREAD_QUERY_INFORMATION );
699 CLIENT_SendRequest( REQ_GET_THREAD_INFO, -1, 1, &req, sizeof(req) );
700 if (CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL )) return FALSE;
701 if (exitcode) *exitcode = reply.exit_code;
706 /**********************************************************************
707 * ResumeThread [KERNEL32.587] Resumes a thread.
709 * Decrements a thread's suspend count. When count is zero, the
710 * execution of the thread is resumed.
713 * Success: Previous suspend count
714 * Failure: 0xFFFFFFFF
717 DWORD WINAPI ResumeThread(
718 HANDLE32 hthread) /* [in] Identifies thread to restart */
720 struct resume_thread_request req;
721 struct resume_thread_reply reply;
722 req.handle = HANDLE_GetServerHandle( PROCESS_Current(), hthread,
723 K32OBJ_THREAD, THREAD_SUSPEND_RESUME );
724 CLIENT_SendRequest( REQ_RESUME_THREAD, -1, 1, &req, sizeof(req) );
725 if (CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL )) return 0xffffffff;
730 /**********************************************************************
731 * SuspendThread [KERNEL32.681] Suspends a thread.
734 * Success: Previous suspend count
735 * Failure: 0xFFFFFFFF
737 DWORD WINAPI SuspendThread(
738 HANDLE32 hthread) /* [in] Handle to the thread */
740 struct suspend_thread_request req;
741 struct suspend_thread_reply reply;
742 req.handle = HANDLE_GetServerHandle( PROCESS_Current(), hthread,
743 K32OBJ_THREAD, THREAD_SUSPEND_RESUME );
744 CLIENT_SendRequest( REQ_SUSPEND_THREAD, -1, 1, &req, sizeof(req) );
745 if (CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL )) return 0xffffffff;
750 /***********************************************************************
751 * QueueUserAPC (KERNEL32.566)
753 DWORD WINAPI QueueUserAPC( PAPCFUNC func, HANDLE32 hthread, ULONG_PTR data )
755 struct queue_apc_request req;
756 req.handle = HANDLE_GetServerHandle( PROCESS_Current(), hthread,
757 K32OBJ_THREAD, THREAD_SET_CONTEXT );
759 req.param = (void *)data;
760 CLIENT_SendRequest( REQ_QUEUE_APC, -1, 1, &req, sizeof(req) );
761 return !CLIENT_WaitReply( NULL, NULL, 0 );
765 /**********************************************************************
766 * GetThreadTimes [KERNEL32.???] Obtains timing information.
769 * What are the fields where these values are stored?
775 BOOL32 WINAPI GetThreadTimes(
776 HANDLE32 thread, /* [in] Specifies the thread of interest */
777 LPFILETIME creationtime, /* [out] When the thread was created */
778 LPFILETIME exittime, /* [out] When the thread was destroyed */
779 LPFILETIME kerneltime, /* [out] Time thread spent in kernel mode */
780 LPFILETIME usertime) /* [out] Time thread spent in user mode */
782 FIXME(thread,"(0x%08x): stub\n",thread);
783 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
788 /**********************************************************************
789 * AttachThreadInput [KERNEL32.8] Attaches input of 1 thread to other
791 * Attaches the input processing mechanism of one thread to that of
799 * 1. Reset the Key State (currenly per thread key state is not maintained)
801 BOOL32 WINAPI AttachThreadInput(
802 DWORD idAttach, /* [in] Thread to attach */
803 DWORD idAttachTo, /* [in] Thread to attach to */
804 BOOL32 fAttach) /* [in] Attach or detach */
806 MESSAGEQUEUE *pSrcMsgQ = 0, *pTgtMsgQ = 0;
809 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
811 /* A thread cannot attach to itself */
812 if ( idAttach == idAttachTo )
815 /* According to the docs this method should fail if a
816 * "Journal record" hook is installed. (attaches all input queues together)
818 if ( HOOK_IsHooked( WH_JOURNALRECORD ) )
821 /* Retrieve message queues corresponding to the thread id's */
822 pTgtMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetThreadQueue( idAttach ) );
823 pSrcMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetThreadQueue( idAttachTo ) );
825 /* Ensure we have message queues and that Src and Tgt threads
826 * are not system threads.
828 if ( !pSrcMsgQ || !pTgtMsgQ || !pSrcMsgQ->pQData || !pTgtMsgQ->pQData )
831 if (fAttach) /* Attach threads */
833 /* Only attach if currently detached */
834 if ( pTgtMsgQ->pQData != pSrcMsgQ->pQData )
836 /* First release the target threads perQData */
837 PERQDATA_Release( pTgtMsgQ->pQData );
839 /* Share a reference to the source threads perQDATA */
840 PERQDATA_Addref( pSrcMsgQ->pQData );
841 pTgtMsgQ->pQData = pSrcMsgQ->pQData;
844 else /* Detach threads */
846 /* Only detach if currently attached */
847 if ( pTgtMsgQ->pQData == pSrcMsgQ->pQData )
849 /* First release the target threads perQData */
850 PERQDATA_Release( pTgtMsgQ->pQData );
852 /* Give the target thread its own private perQDATA once more */
853 pTgtMsgQ->pQData = PERQDATA_CreateInstance();
857 /* TODO: Reset the Key State */
863 /* Unlock the queues before returning */
865 QUEUE_Unlock( pSrcMsgQ );
867 QUEUE_Unlock( pTgtMsgQ );
872 /**********************************************************************
873 * VWin32_BoostThreadGroup [KERNEL.535]
875 VOID WINAPI VWin32_BoostThreadGroup( DWORD threadId, INT32 boost )
877 FIXME(thread, "(0x%08lx,%d): stub\n", threadId, boost);
880 /**********************************************************************
881 * VWin32_BoostThreadStatic [KERNEL.536]
883 VOID WINAPI VWin32_BoostThreadStatic( DWORD threadId, INT32 boost )
885 FIXME(thread, "(0x%08lx,%d): stub\n", threadId, boost);
888 /**********************************************************************
889 * SetThreadLocale [KERNEL32.671] Sets the calling threads current locale.
896 * Implemented in NT only (3.1 and above according to MS
898 BOOL32 WINAPI SetThreadLocale(
899 LCID lcid) /* [in] Locale identifier */
901 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);