4 * Copyright 1996 Alexandre Julliard
10 #include <sys/socket.h>
12 #include "wine/winbase16.h"
20 #include "selectors.h"
24 #include "stackframe.h"
29 DEFAULT_DEBUG_CHANNEL(thread
)
35 /* Is threading code initialized? */
36 BOOL THREAD_InitDone
= FALSE
;
38 /* THDB of the initial thread */
39 static THDB initial_thdb
;
41 /* Global thread list (FIXME: not thread-safe) */
42 THDB
*THREAD_First
= &initial_thdb
;
44 /***********************************************************************
47 * Return the current thread THDB pointer.
49 THDB
*THREAD_Current(void)
51 if (!THREAD_InitDone
) return NULL
;
52 return (THDB
*)((char *)NtCurrentTeb() - (int)&((THDB
*)0)->teb
);
55 /***********************************************************************
58 BOOL
THREAD_IsWin16( THDB
*thdb
)
60 return !thdb
|| !(thdb
->teb
.flags
& TEBF_WIN32
);
63 /***********************************************************************
66 * Convert a thread id to a THDB, making sure it is valid.
68 THDB
*THREAD_IdToTHDB( DWORD id
)
70 THDB
*thdb
= THREAD_First
;
72 if (!id
) return THREAD_Current();
75 if ((DWORD
)thdb
->server_tid
== id
) return thdb
;
78 /* Allow task handles to be used; convert to main thread */
81 TDB
*pTask
= (TDB
*)GlobalLock16( id
);
82 if (pTask
) return pTask
->thdb
;
84 SetLastError( ERROR_INVALID_PARAMETER
);
89 /***********************************************************************
92 * Initialization of a newly created THDB.
94 static BOOL
THREAD_InitTHDB( THDB
*thdb
, DWORD stack_size
, BOOL alloc_stack16
,
95 LPSECURITY_ATTRIBUTES sa
)
99 /* Allocate the stack */
102 * If stacksize smaller than 1 MB, allocate 1MB
103 * (one program wanted only 10 kB, which is recommendable, but some WINE
104 * functions, noteably in the files subdir, push HUGE structures and
105 * arrays on the stack. They probably shouldn't.)
106 * If stacksize larger than 16 MB, warn the user. (We could shrink the stack
107 * but this could give more or less unexplainable crashes.)
109 if (stack_size
<1024*1024)
110 stack_size
= 1024 * 1024;
111 if (stack_size
>= 16*1024*1024)
112 WARN(thread
,"Thread stack size is %ld MB.\n",stack_size
/1024/1024);
113 thdb
->stack_base
= VirtualAlloc(NULL
,
114 stack_size
+ (alloc_stack16
? 0x10000 : 0),
115 MEM_COMMIT
, PAGE_EXECUTE_READWRITE
);
116 if (!thdb
->stack_base
) goto error
;
117 /* Set a guard page at the bottom of the stack */
118 VirtualProtect( thdb
->stack_base
, 1, PAGE_EXECUTE_READWRITE
| PAGE_GUARD
,
120 thdb
->teb
.stack_top
= (char *)thdb
->stack_base
+ stack_size
;
121 thdb
->teb
.stack_low
= thdb
->stack_base
;
122 thdb
->exit_stack
= thdb
->teb
.stack_top
;
124 /* Allocate the 16-bit stack selector */
128 thdb
->teb
.stack_sel
= SELECTOR_AllocBlock( thdb
->teb
.stack_top
,
129 0x10000, SEGMENT_DATA
,
131 if (!thdb
->teb
.stack_sel
) goto error
;
132 thdb
->cur_stack
= PTR_SEG_OFF_TO_SEGPTR( thdb
->teb
.stack_sel
,
133 0x10000 - sizeof(STACK16FRAME
) );
136 /* Create the thread event */
138 if (!(thdb
->event
= CreateEventA( NULL
, FALSE
, FALSE
, NULL
))) goto error
;
139 thdb
->event
= ConvertToGlobalHandle( thdb
->event
);
143 if (thdb
->event
) CloseHandle( thdb
->event
);
144 if (thdb
->teb
.stack_sel
) SELECTOR_FreeBlock( thdb
->teb
.stack_sel
, 1 );
145 if (thdb
->stack_base
) VirtualFree( thdb
->stack_base
, 0, MEM_RELEASE
);
150 /***********************************************************************
153 * Free data structures associated with a thread.
154 * Must be called from the context of another thread.
156 void CALLBACK
THREAD_FreeTHDB( ULONG_PTR arg
)
158 THDB
*thdb
= (THDB
*)arg
;
159 THDB
**pptr
= &THREAD_First
;
161 TRACE( thread
, "(%p) called\n", thdb
);
162 SERVICE_Delete( thdb
->cleanup
);
164 PROCESS_CallUserSignalProc( USIG_THREAD_EXIT
, 0, 0 );
166 CloseHandle( thdb
->event
);
167 while (*pptr
&& (*pptr
!= thdb
)) pptr
= &(*pptr
)->next
;
168 if (*pptr
) *pptr
= thdb
->next
;
170 /* Free the associated memory */
172 if (thdb
->teb
.stack_sel
) SELECTOR_FreeBlock( thdb
->teb
.stack_sel
, 1 );
173 SELECTOR_FreeBlock( thdb
->teb_sel
, 1 );
174 close( thdb
->socket
);
175 VirtualFree( thdb
->stack_base
, 0, MEM_RELEASE
);
176 HeapFree( SystemHeap
, HEAP_NO_SERIALIZE
, thdb
);
180 /***********************************************************************
181 * THREAD_CreateInitialThread
183 * Create the initial thread.
185 THDB
*THREAD_CreateInitialThread( PDB
*pdb
, int server_fd
)
187 initial_thdb
.process
= pdb
;
188 initial_thdb
.teb
.except
= (void *)-1;
189 initial_thdb
.teb
.self
= &initial_thdb
.teb
;
190 initial_thdb
.teb
.flags
= /* TEBF_WIN32 */ 0;
191 initial_thdb
.teb
.tls_ptr
= initial_thdb
.tls_array
;
192 initial_thdb
.teb
.process
= pdb
;
193 initial_thdb
.exit_code
= 0x103; /* STILL_ACTIVE */
194 initial_thdb
.socket
= server_fd
;
196 /* Allocate the TEB selector (%fs register) */
198 if (!(initial_thdb
.teb_sel
= SELECTOR_AllocBlock( &initial_thdb
.teb
, 0x1000,
199 SEGMENT_DATA
, TRUE
, FALSE
)))
201 MSG("Could not allocate fs register for initial thread\n" );
204 SET_CUR_THREAD( &initial_thdb
);
205 THREAD_InitDone
= TRUE
;
207 /* Now proceed with normal initialization */
209 if (CLIENT_InitThread()) return NULL
;
210 if (!THREAD_InitTHDB( &initial_thdb
, 0, TRUE
, NULL
)) return NULL
;
211 return &initial_thdb
;
215 /***********************************************************************
218 THDB
*THREAD_Create( PDB
*pdb
, DWORD flags
, DWORD stack_size
, BOOL alloc_stack16
,
219 LPSECURITY_ATTRIBUTES sa
, int *server_handle
)
221 struct new_thread_request request
;
222 struct new_thread_reply reply
= { NULL
, -1 };
225 THDB
*thdb
= HeapAlloc( SystemHeap
, HEAP_ZERO_MEMORY
, sizeof(THDB
) );
226 if (!thdb
) return NULL
;
228 thdb
->teb
.except
= (void *)-1;
229 thdb
->teb
.htask16
= pdb
->task
;
230 thdb
->teb
.self
= &thdb
->teb
;
231 thdb
->teb
.flags
= (pdb
->flags
& PDB32_WIN16_PROC
)? 0 : TEBF_WIN32
;
232 thdb
->teb
.tls_ptr
= thdb
->tls_array
;
233 thdb
->teb
.process
= pdb
;
234 thdb
->exit_code
= 0x103; /* STILL_ACTIVE */
238 /* Allocate the TEB selector (%fs register) */
240 thdb
->teb_sel
= SELECTOR_AllocBlock( &thdb
->teb
, 0x1000, SEGMENT_DATA
,
242 if (!thdb
->teb_sel
) goto error
;
244 /* Create the socket pair for server communication */
246 if (socketpair( AF_UNIX
, SOCK_STREAM
, 0, fd
) == -1)
248 SetLastError( ERROR_TOO_MANY_OPEN_FILES
); /* FIXME */
251 thdb
->socket
= fd
[0];
252 fcntl( fd
[0], F_SETFD
, 1 ); /* set close on exec flag */
254 /* Create the thread on the server side */
256 request
.pid
= thdb
->process
->server_pid
;
257 request
.suspend
= ((thdb
->flags
& CREATE_SUSPENDED
) != 0);
258 request
.inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
259 CLIENT_SendRequest( REQ_NEW_THREAD
, fd
[1], 1, &request
, sizeof(request
) );
260 if (CLIENT_WaitSimpleReply( &reply
, sizeof(reply
), NULL
)) goto error
;
261 thdb
->server_tid
= reply
.tid
;
262 *server_handle
= reply
.handle
;
264 /* Do the rest of the initialization */
266 if (!THREAD_InitTHDB( thdb
, stack_size
, alloc_stack16
, sa
)) goto error
;
267 thdb
->next
= THREAD_First
;
270 /* Install cleanup handler */
272 thdb
->cleanup
= SERVICE_AddObject( *server_handle
,
273 THREAD_FreeTHDB
, (ULONG_PTR
)thdb
);
277 if (reply
.handle
!= -1) CloseHandle( reply
.handle
);
278 if (thdb
->teb_sel
) SELECTOR_FreeBlock( thdb
->teb_sel
, 1 );
279 HeapFree( SystemHeap
, 0, thdb
);
280 if (thdb
->socket
!= -1) close( thdb
->socket
);
285 /***********************************************************************
288 * Start execution of a newly created thread. Does not return.
290 static void THREAD_Start(void)
292 THDB
*thdb
= THREAD_Current();
293 LPTHREAD_START_ROUTINE func
= (LPTHREAD_START_ROUTINE
)thdb
->entry_point
;
294 PROCESS_CallUserSignalProc( USIG_THREAD_INIT
, 0, 0 );
296 MODULE_DllThreadAttach( NULL
);
297 ExitThread( func( thdb
->entry_arg
) );
301 /***********************************************************************
302 * CreateThread (KERNEL32.63)
304 HANDLE WINAPI
CreateThread( SECURITY_ATTRIBUTES
*sa
, DWORD stack
,
305 LPTHREAD_START_ROUTINE start
, LPVOID param
,
306 DWORD flags
, LPDWORD id
)
309 THDB
*thread
= THREAD_Create( PROCESS_Current(), flags
, stack
, TRUE
, sa
, &handle
);
310 if (!thread
) return INVALID_HANDLE_VALUE
;
311 thread
->teb
.flags
|= TEBF_WIN32
;
312 thread
->entry_point
= start
;
313 thread
->entry_arg
= param
;
314 thread
->startup
= THREAD_Start
;
315 if (SYSDEPS_SpawnThread( thread
) == -1)
317 CloseHandle( handle
);
318 return INVALID_HANDLE_VALUE
;
320 if (id
) *id
= (DWORD
)thread
->server_tid
;
325 /***********************************************************************
326 * ExitThread [KERNEL32.215] Ends a thread
331 void WINAPI
ExitThread( DWORD code
) /* [in] Exit code for this thread */
333 MODULE_DllThreadDetach( NULL
);
334 TerminateThread( GetCurrentThread(), code
);
338 /***********************************************************************
339 * GetCurrentThread [KERNEL32.200] Gets pseudohandle for current thread
342 * Pseudohandle for the current thread
344 HANDLE WINAPI
GetCurrentThread(void)
346 return CURRENT_THREAD_PSEUDOHANDLE
;
350 /***********************************************************************
351 * GetCurrentThreadId [KERNEL32.201] Returns thread identifier.
354 * Thread identifier of calling thread
356 DWORD WINAPI
GetCurrentThreadId(void)
358 THDB
*thdb
= THREAD_Current();
360 assert( thdb
->server_tid
);
361 return (DWORD
)thdb
->server_tid
;
365 /**********************************************************************
366 * GetLastError [KERNEL.148] [KERNEL32.227] Returns last-error code.
369 * Calling thread's last error code value.
371 DWORD WINAPI
GetLastError(void)
373 THDB
*thread
= THREAD_Current();
374 DWORD ret
= thread
->last_error
;
375 TRACE(thread
,"0x%lx\n",ret
);
380 /**********************************************************************
381 * SetLastError [KERNEL.147] [KERNEL32.497] Sets the last-error code.
386 void WINAPI
SetLastError(
387 DWORD error
) /* [in] Per-thread error code */
389 THDB
*thread
= THREAD_Current();
390 /* This one must work before we have a thread (FIXME) */
392 TRACE(thread
,"%p error=0x%lx\n",thread
,error
);
395 thread
->last_error
= error
;
399 /**********************************************************************
400 * SetLastErrorEx [USER32.485] Sets the last-error code.
405 void WINAPI
SetLastErrorEx(
406 DWORD error
, /* [in] Per-thread error code */
407 DWORD type
) /* [in] Error type */
409 TRACE(thread
, "(0x%08lx, 0x%08lx)\n", error
,type
);
416 /* Fall through for now */
418 FIXME(thread
, "(error=%08lx, type=%08lx): Unhandled type\n", error
,type
);
421 SetLastError( error
);
425 /**********************************************************************
426 * TlsAlloc [KERNEL32.530] Allocates a TLS index.
428 * Allocates a thread local storage index
432 * Failure: 0xFFFFFFFF
434 DWORD WINAPI
TlsAlloc( void )
436 THDB
*thread
= THREAD_Current();
437 DWORD i
, mask
, ret
= 0;
438 DWORD
*bits
= thread
->process
->tls_bits
;
439 EnterCriticalSection( &thread
->process
->crit_section
);
440 if (*bits
== 0xffffffff)
444 if (*bits
== 0xffffffff)
446 LeaveCriticalSection( &thread
->process
->crit_section
);
447 SetLastError( ERROR_NO_MORE_ITEMS
);
451 for (i
= 0, mask
= 1; i
< 32; i
++, mask
<<= 1) if (!(*bits
& mask
)) break;
453 LeaveCriticalSection( &thread
->process
->crit_section
);
458 /**********************************************************************
459 * TlsFree [KERNEL32.531] Releases a TLS index.
461 * Releases a thread local storage index, making it available for reuse
468 DWORD index
) /* [in] TLS Index to free */
471 THDB
*thread
= THREAD_Current();
472 DWORD
*bits
= thread
->process
->tls_bits
;
475 SetLastError( ERROR_INVALID_PARAMETER
);
478 EnterCriticalSection( &thread
->process
->crit_section
);
479 if (index
>= 32) bits
++;
480 mask
= (1 << (index
& 31));
481 if (!(*bits
& mask
)) /* already free? */
483 LeaveCriticalSection( &thread
->process
->crit_section
);
484 SetLastError( ERROR_INVALID_PARAMETER
);
488 thread
->tls_array
[index
] = 0;
489 /* FIXME: should zero all other thread values */
490 LeaveCriticalSection( &thread
->process
->crit_section
);
495 /**********************************************************************
496 * TlsGetValue [KERNEL32.532] Gets value in a thread's TLS slot
499 * Success: Value stored in calling thread's TLS slot for index
500 * Failure: 0 and GetLastError returns NO_ERROR
502 LPVOID WINAPI
TlsGetValue(
503 DWORD index
) /* [in] TLS index to retrieve value for */
505 THDB
*thread
= THREAD_Current();
508 SetLastError( ERROR_INVALID_PARAMETER
);
511 SetLastError( ERROR_SUCCESS
);
512 return thread
->tls_array
[index
];
516 /**********************************************************************
517 * TlsSetValue [KERNEL32.533] Stores a value in the thread's TLS slot.
523 BOOL WINAPI
TlsSetValue(
524 DWORD index
, /* [in] TLS index to set value for */
525 LPVOID value
) /* [in] Value to be stored */
527 THDB
*thread
= THREAD_Current();
530 SetLastError( ERROR_INVALID_PARAMETER
);
533 thread
->tls_array
[index
] = value
;
538 /***********************************************************************
539 * SetThreadContext [KERNEL32.670] Sets context of thread.
545 BOOL WINAPI
SetThreadContext(
546 HANDLE handle
, /* [in] Handle to thread with context */
547 CONTEXT
*context
) /* [out] Address of context structure */
549 FIXME( thread
, "not implemented\n" );
553 /***********************************************************************
554 * GetThreadContext [KERNEL32.294] Retrieves context of thread.
560 BOOL WINAPI
GetThreadContext(
561 HANDLE handle
, /* [in] Handle to thread with context */
562 CONTEXT
*context
) /* [out] Address of context structure */
566 FIXME( thread
, "returning dummy info\n" );
568 /* make up some plausible values for segment registers */
581 /**********************************************************************
582 * GetThreadPriority [KERNEL32.296] Returns priority for thread.
585 * Success: Thread's priority level.
586 * Failure: THREAD_PRIORITY_ERROR_RETURN
588 INT WINAPI
GetThreadPriority(
589 HANDLE hthread
) /* [in] Handle to thread */
591 struct get_thread_info_request req
;
592 struct get_thread_info_reply reply
;
593 req
.handle
= hthread
;
594 CLIENT_SendRequest( REQ_GET_THREAD_INFO
, -1, 1, &req
, sizeof(req
) );
595 if (CLIENT_WaitSimpleReply( &reply
, sizeof(reply
), NULL
))
596 return THREAD_PRIORITY_ERROR_RETURN
;
597 return reply
.priority
;
601 /**********************************************************************
602 * SetThreadPriority [KERNEL32.514] Sets priority for thread.
608 BOOL WINAPI
SetThreadPriority(
609 HANDLE hthread
, /* [in] Handle to thread */
610 INT priority
) /* [in] Thread priority level */
612 struct set_thread_info_request req
;
613 req
.handle
= hthread
;
614 req
.priority
= priority
;
615 req
.mask
= SET_THREAD_INFO_PRIORITY
;
616 CLIENT_SendRequest( REQ_SET_THREAD_INFO
, -1, 1, &req
, sizeof(req
) );
617 return !CLIENT_WaitReply( NULL
, NULL
, 0 );
621 /**********************************************************************
622 * SetThreadAffinityMask (KERNEL32.669)
624 DWORD WINAPI
SetThreadAffinityMask( HANDLE hThread
, DWORD dwThreadAffinityMask
)
626 struct set_thread_info_request req
;
627 req
.handle
= hThread
;
628 req
.affinity
= dwThreadAffinityMask
;
629 req
.mask
= SET_THREAD_INFO_AFFINITY
;
630 CLIENT_SendRequest( REQ_SET_THREAD_INFO
, -1, 1, &req
, sizeof(req
) );
631 if (CLIENT_WaitReply( NULL
, NULL
, 0 )) return 0;
632 return 1; /* FIXME: should return previous value */
636 /**********************************************************************
637 * TerminateThread [KERNEL32.685] Terminates a thread
643 BOOL WINAPI
TerminateThread(
644 HANDLE handle
, /* [in] Handle to thread */
645 DWORD exitcode
) /* [in] Exit code for thread */
647 struct terminate_thread_request req
;
649 req
.exit_code
= exitcode
;
650 CLIENT_SendRequest( REQ_TERMINATE_THREAD
, -1, 1, &req
, sizeof(req
) );
651 return !CLIENT_WaitReply( NULL
, NULL
, 0 );
655 /**********************************************************************
656 * GetExitCodeThread [KERNEL32.???] Gets termination status of thread.
662 BOOL WINAPI
GetExitCodeThread(
663 HANDLE hthread
, /* [in] Handle to thread */
664 LPDWORD exitcode
) /* [out] Address to receive termination status */
666 struct get_thread_info_request req
;
667 struct get_thread_info_reply reply
;
668 req
.handle
= hthread
;
669 CLIENT_SendRequest( REQ_GET_THREAD_INFO
, -1, 1, &req
, sizeof(req
) );
670 if (CLIENT_WaitSimpleReply( &reply
, sizeof(reply
), NULL
)) return FALSE
;
671 if (exitcode
) *exitcode
= reply
.exit_code
;
676 /**********************************************************************
677 * ResumeThread [KERNEL32.587] Resumes a thread.
679 * Decrements a thread's suspend count. When count is zero, the
680 * execution of the thread is resumed.
683 * Success: Previous suspend count
684 * Failure: 0xFFFFFFFF
687 DWORD WINAPI
ResumeThread(
688 HANDLE hthread
) /* [in] Identifies thread to restart */
690 struct resume_thread_request req
;
691 struct resume_thread_reply reply
;
692 req
.handle
= hthread
;
693 CLIENT_SendRequest( REQ_RESUME_THREAD
, -1, 1, &req
, sizeof(req
) );
694 if (CLIENT_WaitSimpleReply( &reply
, sizeof(reply
), NULL
)) return 0xffffffff;
699 /**********************************************************************
700 * SuspendThread [KERNEL32.681] Suspends a thread.
703 * Success: Previous suspend count
704 * Failure: 0xFFFFFFFF
706 DWORD WINAPI
SuspendThread(
707 HANDLE hthread
) /* [in] Handle to the thread */
709 struct suspend_thread_request req
;
710 struct suspend_thread_reply reply
;
711 req
.handle
= hthread
;
712 CLIENT_SendRequest( REQ_SUSPEND_THREAD
, -1, 1, &req
, sizeof(req
) );
713 if (CLIENT_WaitSimpleReply( &reply
, sizeof(reply
), NULL
)) return 0xffffffff;
718 /***********************************************************************
719 * QueueUserAPC (KERNEL32.566)
721 DWORD WINAPI
QueueUserAPC( PAPCFUNC func
, HANDLE hthread
, ULONG_PTR data
)
723 struct queue_apc_request req
;
724 req
.handle
= hthread
;
726 req
.param
= (void *)data
;
727 CLIENT_SendRequest( REQ_QUEUE_APC
, -1, 1, &req
, sizeof(req
) );
728 return !CLIENT_WaitReply( NULL
, NULL
, 0 );
732 /**********************************************************************
733 * GetThreadTimes [KERNEL32.???] Obtains timing information.
736 * What are the fields where these values are stored?
742 BOOL WINAPI
GetThreadTimes(
743 HANDLE thread
, /* [in] Specifies the thread of interest */
744 LPFILETIME creationtime
, /* [out] When the thread was created */
745 LPFILETIME exittime
, /* [out] When the thread was destroyed */
746 LPFILETIME kerneltime
, /* [out] Time thread spent in kernel mode */
747 LPFILETIME usertime
) /* [out] Time thread spent in user mode */
749 FIXME(thread
,"(0x%08x): stub\n",thread
);
750 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
755 /**********************************************************************
756 * AttachThreadInput [KERNEL32.8] Attaches input of 1 thread to other
758 * Attaches the input processing mechanism of one thread to that of
766 * 1. Reset the Key State (currenly per thread key state is not maintained)
768 BOOL WINAPI
AttachThreadInput(
769 DWORD idAttach
, /* [in] Thread to attach */
770 DWORD idAttachTo
, /* [in] Thread to attach to */
771 BOOL fAttach
) /* [in] Attach or detach */
773 MESSAGEQUEUE
*pSrcMsgQ
= 0, *pTgtMsgQ
= 0;
776 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
778 /* A thread cannot attach to itself */
779 if ( idAttach
== idAttachTo
)
782 /* According to the docs this method should fail if a
783 * "Journal record" hook is installed. (attaches all input queues together)
785 if ( HOOK_IsHooked( WH_JOURNALRECORD
) )
788 /* Retrieve message queues corresponding to the thread id's */
789 pTgtMsgQ
= (MESSAGEQUEUE
*)QUEUE_Lock( GetThreadQueue16( idAttach
) );
790 pSrcMsgQ
= (MESSAGEQUEUE
*)QUEUE_Lock( GetThreadQueue16( idAttachTo
) );
792 /* Ensure we have message queues and that Src and Tgt threads
793 * are not system threads.
795 if ( !pSrcMsgQ
|| !pTgtMsgQ
|| !pSrcMsgQ
->pQData
|| !pTgtMsgQ
->pQData
)
798 if (fAttach
) /* Attach threads */
800 /* Only attach if currently detached */
801 if ( pTgtMsgQ
->pQData
!= pSrcMsgQ
->pQData
)
803 /* First release the target threads perQData */
804 PERQDATA_Release( pTgtMsgQ
->pQData
);
806 /* Share a reference to the source threads perQDATA */
807 PERQDATA_Addref( pSrcMsgQ
->pQData
);
808 pTgtMsgQ
->pQData
= pSrcMsgQ
->pQData
;
811 else /* Detach threads */
813 /* Only detach if currently attached */
814 if ( pTgtMsgQ
->pQData
== pSrcMsgQ
->pQData
)
816 /* First release the target threads perQData */
817 PERQDATA_Release( pTgtMsgQ
->pQData
);
819 /* Give the target thread its own private perQDATA once more */
820 pTgtMsgQ
->pQData
= PERQDATA_CreateInstance();
824 /* TODO: Reset the Key State */
826 bRet
= 1; /* Success */
830 /* Unlock the queues before returning */
832 QUEUE_Unlock( pSrcMsgQ
);
834 QUEUE_Unlock( pTgtMsgQ
);
839 /**********************************************************************
840 * VWin32_BoostThreadGroup [KERNEL.535]
842 VOID WINAPI
VWin32_BoostThreadGroup( DWORD threadId
, INT boost
)
844 FIXME(thread
, "(0x%08lx,%d): stub\n", threadId
, boost
);
847 /**********************************************************************
848 * VWin32_BoostThreadStatic [KERNEL.536]
850 VOID WINAPI
VWin32_BoostThreadStatic( DWORD threadId
, INT boost
)
852 FIXME(thread
, "(0x%08lx,%d): stub\n", threadId
, boost
);
855 /**********************************************************************
856 * SetThreadLocale [KERNEL32.671] Sets the calling threads current locale.
863 * Implemented in NT only (3.1 and above according to MS
865 BOOL WINAPI
SetThreadLocale(
866 LCID lcid
) /* [in] Locale identifier */
868 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);