Fixed CHECK_STRING display.
[wine/multimedia.git] / scheduler / thread.c
blobd89b77f4aa7f5063a41dab4c9c8d7d7fa16584a5
1 /*
2 * Win32 threads
4 * Copyright 1996 Alexandre Julliard
5 */
7 #include <assert.h>
8 #include <signal.h>
9 #include <unistd.h>
10 #include "thread.h"
11 #include "process.h"
12 #include "task.h"
13 #include "module.h"
14 #include "user.h"
15 #include "winerror.h"
16 #include "heap.h"
17 #include "selectors.h"
18 #include "miscemu.h"
19 #include "winnt.h"
20 #include "server.h"
21 #include "stackframe.h"
22 #include "debug.h"
24 #ifndef __i386__
25 THDB *pCurrentThread;
26 #endif
28 static void THREAD_Destroy( K32OBJ *obj );
30 const K32OBJ_OPS THREAD_Ops =
32 THREAD_Destroy /* destroy */
35 /* Is threading code initialized? */
36 BOOL32 THREAD_InitDone = FALSE;
38 /**********************************************************************
39 * THREAD_GetPtr
41 * Return a pointer to a thread object. The object count must be decremented
42 * when no longer used.
44 THDB *THREAD_GetPtr( HANDLE32 handle, DWORD access, int *server_handle )
46 return (THDB *)HANDLE_GetObjPtr( PROCESS_Current(), handle,
47 K32OBJ_THREAD, access, server_handle );
51 /***********************************************************************
52 * THREAD_Current
54 * Return the current thread THDB pointer.
56 THDB *THREAD_Current(void)
58 if (!THREAD_InitDone) return NULL;
59 return (THDB *)((char *)NtCurrentTeb() - (int)&((THDB *)0)->teb);
62 /***********************************************************************
63 * THREAD_IsWin16
65 BOOL32 THREAD_IsWin16( THDB *thdb )
67 if (!thdb || !thdb->process)
68 return TRUE;
69 else
71 TDB* pTask = (TDB*)GlobalLock16( thdb->process->task );
72 return !pTask || pTask->thdb == thdb;
76 /***********************************************************************
77 * THREAD_IdToTHDB
79 * Convert a thread id to a THDB, making sure it is valid.
81 THDB *THREAD_IdToTHDB( DWORD id )
83 THDB *thdb;
85 if (!id) return THREAD_Current();
86 thdb = THREAD_ID_TO_THDB( id );
87 if (!K32OBJ_IsValid( &thdb->header, K32OBJ_THREAD ))
89 /* Allow task handles to be used; convert to main thread */
90 if ( IsTask( id ) )
92 TDB *pTask = (TDB *)GlobalLock16( id );
93 if (pTask) return pTask->thdb;
96 SetLastError( ERROR_INVALID_PARAMETER );
97 return NULL;
99 return thdb;
103 /***********************************************************************
104 * THREAD_AddQueue
106 * Add a thread to a queue.
108 void THREAD_AddQueue( THREAD_QUEUE *queue, THDB *thread )
110 THREAD_ENTRY *entry = HeapAlloc( SystemHeap, HEAP_NO_SERIALIZE,
111 sizeof(*entry) );
112 assert(entry);
113 SYSTEM_LOCK();
114 entry->thread = thread;
115 if (*queue)
117 entry->next = (*queue)->next;
118 (*queue)->next = entry;
120 else entry->next = entry;
121 *queue = entry;
122 SYSTEM_UNLOCK();
125 /***********************************************************************
126 * THREAD_RemoveQueue
128 * Remove a thread from a queue.
130 void THREAD_RemoveQueue( THREAD_QUEUE *queue, THDB *thread )
132 THREAD_ENTRY *entry = *queue;
133 SYSTEM_LOCK();
134 if (entry->next == entry) /* Only one element in the queue */
136 assert( entry->thread == thread );
137 *queue = NULL;
139 else
141 THREAD_ENTRY *next;
142 while (entry->next->thread != thread)
144 entry = entry->next;
145 assert( entry != *queue ); /* Have we come all the way around? */
147 if ((next = entry->next) == *queue) *queue = entry;
148 entry->next = entry->next->next;
149 entry = next; /* This is the one we want to free */
151 HeapFree( SystemHeap, 0, entry );
152 SYSTEM_UNLOCK();
156 /***********************************************************************
157 * THREAD_Create
159 THDB *THREAD_Create( PDB32 *pdb, DWORD stack_size, BOOL32 alloc_stack16,
160 int *server_thandle, int *server_phandle,
161 LPTHREAD_START_ROUTINE start_addr, LPVOID param )
163 DWORD old_prot;
164 WORD cs, ds;
166 THDB *thdb = HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(THDB) );
167 if (!thdb) return NULL;
168 thdb->header.type = K32OBJ_THREAD;
169 thdb->header.refcount = 1;
170 thdb->process = pdb;
171 thdb->teb.except = (void *)-1;
172 thdb->teb.htask16 = 0; /* FIXME */
173 thdb->teb.self = &thdb->teb;
174 thdb->teb.flags = (pdb->flags & PDB32_WIN16_PROC)? 0 : TEBF_WIN32;
175 thdb->teb.tls_ptr = thdb->tls_array;
176 thdb->teb.process = pdb;
177 thdb->wait_list = &thdb->wait_struct;
178 thdb->exit_code = 0x103; /* STILL_ACTIVE */
179 thdb->entry_point = start_addr;
180 thdb->entry_arg = param;
181 thdb->socket = -1;
183 /* Allocate the stack */
185 /* FIXME:
186 * If stacksize smaller than 1 MB, allocate 1MB
187 * (one program wanted only 10 kB, which is recommendable, but some WINE
188 * functions, noteably in the files subdir, push HUGE structures and
189 * arrays on the stack. They probably shouldn't.)
190 * If stacksize larger than 16 MB, warn the user. (We could shrink the stack
191 * but this could give more or less unexplainable crashes.)
193 if (stack_size<1024*1024)
194 stack_size = 1024 * 1024;
195 if (stack_size >= 16*1024*1024)
196 WARN(thread,"Thread stack size is %ld MB.\n",stack_size/1024/1024);
197 thdb->stack_base = VirtualAlloc(NULL,
198 stack_size + (alloc_stack16 ? 0x10000 : 0),
199 MEM_COMMIT, PAGE_EXECUTE_READWRITE );
200 if (!thdb->stack_base) goto error;
201 /* Set a guard page at the bottom of the stack */
202 VirtualProtect( thdb->stack_base, 1, PAGE_EXECUTE_READWRITE | PAGE_GUARD,
203 &old_prot );
204 thdb->teb.stack_top = (char *)thdb->stack_base + stack_size;
205 thdb->teb.stack_low = thdb->stack_base;
206 thdb->exit_stack = thdb->teb.stack_top;
208 /* Allocate the TEB selector (%fs register) */
210 thdb->teb_sel = SELECTOR_AllocBlock( &thdb->teb, 0x1000, SEGMENT_DATA,
211 TRUE, FALSE );
212 if (!thdb->teb_sel) goto error;
214 /* Allocate the 16-bit stack selector */
216 if (alloc_stack16)
218 thdb->teb.stack_sel = SELECTOR_AllocBlock( thdb->teb.stack_top,
219 0x10000, SEGMENT_DATA,
220 FALSE, FALSE );
221 if (!thdb->teb.stack_sel) goto error;
222 thdb->cur_stack = PTR_SEG_OFF_TO_SEGPTR( thdb->teb.stack_sel,
223 0x10000 - sizeof(STACK16FRAME) );
226 /* Create the thread socket */
228 if (CLIENT_NewThread( thdb, server_thandle, server_phandle )) goto error;
230 /* Add thread to process's list of threads */
232 THREAD_AddQueue( &pdb->thread_list, thdb );
234 /* Create the thread event */
236 if (!(thdb->event = CreateEvent32A( NULL, FALSE, FALSE, NULL ))) goto error;
237 thdb->event = ConvertToGlobalHandle( thdb->event );
239 /* Initialize the thread context */
241 GET_CS(cs);
242 GET_DS(ds);
243 thdb->pcontext = &thdb->context;
244 thdb->context.SegCs = cs;
245 thdb->context.SegDs = ds;
246 thdb->context.SegEs = ds;
247 thdb->context.SegGs = ds;
248 thdb->context.SegSs = ds;
249 thdb->context.SegFs = thdb->teb_sel;
250 thdb->context.Eip = (DWORD)start_addr;
251 thdb->context.Esp = (DWORD)thdb->teb.stack_top;
252 PE_InitTls( thdb );
253 return thdb;
255 error:
256 if (thdb->socket != -1) close( thdb->socket );
257 if (thdb->event) CloseHandle( thdb->event );
258 if (thdb->teb.stack_sel) SELECTOR_FreeBlock( thdb->teb.stack_sel, 1 );
259 if (thdb->teb_sel) SELECTOR_FreeBlock( thdb->teb_sel, 1 );
260 if (thdb->stack_base) VirtualFree( thdb->stack_base, 0, MEM_RELEASE );
261 HeapFree( SystemHeap, 0, thdb );
262 return NULL;
266 /***********************************************************************
267 * THREAD_Destroy
269 static void THREAD_Destroy( K32OBJ *ptr )
271 THDB *thdb = (THDB *)ptr;
273 assert( ptr->type == K32OBJ_THREAD );
274 ptr->type = K32OBJ_UNKNOWN;
276 /* Free the associated memory */
278 #ifdef __i386__
280 /* Check if we are deleting the current thread */
281 WORD fs;
282 GET_FS( fs );
283 if (fs == thdb->teb_sel)
285 GET_DS( fs );
286 SET_FS( fs );
289 #endif
290 CloseHandle( thdb->event );
291 close( thdb->socket );
292 SELECTOR_FreeBlock( thdb->teb_sel, 1 );
293 if (thdb->teb.stack_sel) SELECTOR_FreeBlock( thdb->teb.stack_sel, 1 );
294 HeapFree( SystemHeap, 0, thdb );
299 /***********************************************************************
300 * THREAD_Start
302 * Start execution of a newly created thread. Does not return.
304 void THREAD_Start( THDB *thdb )
306 LPTHREAD_START_ROUTINE func = (LPTHREAD_START_ROUTINE)thdb->entry_point;
307 assert( THREAD_Current() == thdb );
308 CLIENT_InitThread();
309 MODULE_InitializeDLLs( thdb->process, 0, DLL_THREAD_ATTACH, NULL );
310 ExitThread( func( thdb->entry_arg ) );
314 /***********************************************************************
315 * CreateThread (KERNEL32.63)
317 HANDLE32 WINAPI CreateThread( SECURITY_ATTRIBUTES *sa, DWORD stack,
318 LPTHREAD_START_ROUTINE start, LPVOID param,
319 DWORD flags, LPDWORD id )
321 int server_handle = -1;
322 HANDLE32 handle = INVALID_HANDLE_VALUE32;
323 BOOL32 inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
325 THDB *thread = THREAD_Create( PROCESS_Current(), stack,
326 TRUE, &server_handle, NULL, start, param );
327 if (!thread) return INVALID_HANDLE_VALUE32;
328 handle = HANDLE_Alloc( PROCESS_Current(), &thread->header,
329 THREAD_ALL_ACCESS, inherit, server_handle );
330 if (handle == INVALID_HANDLE_VALUE32) goto error;
331 if (SYSDEPS_SpawnThread( thread ) == -1) goto error;
332 if (id) *id = THDB_TO_THREAD_ID( thread );
333 return handle;
335 error:
336 if (handle != INVALID_HANDLE_VALUE32) CloseHandle( handle );
337 K32OBJ_DecCount( &thread->header );
338 return INVALID_HANDLE_VALUE32;
342 /***********************************************************************
343 * ExitThread [KERNEL32.215] Ends a thread
345 * RETURNS
346 * None
348 void WINAPI ExitThread(
349 DWORD code) /* [in] Exit code for this thread */
351 THDB *thdb = THREAD_Current();
352 LONG count;
354 /* Remove thread from process's list */
355 THREAD_RemoveQueue( &thdb->process->thread_list, thdb );
357 MODULE_InitializeDLLs( thdb->process, 0, DLL_THREAD_DETACH, NULL );
359 SYSTEM_LOCK();
360 thdb->exit_code = code;
362 /* FIXME: should free the stack somehow */
363 #if 0
364 /* FIXME: We cannot do this; once the current thread is destroyed,
365 synchronization primitives do not work properly. */
366 K32OBJ_DecCount( &thdb->header );
367 #endif
368 /* Completely unlock the system lock just in case */
369 count = SYSTEM_LOCK_COUNT();
370 while (count--) SYSTEM_UNLOCK();
371 SYSDEPS_ExitThread();
375 /***********************************************************************
376 * GetCurrentThread [KERNEL32.200] Gets pseudohandle for current thread
378 * RETURNS
379 * Pseudohandle for the current thread
381 HANDLE32 WINAPI GetCurrentThread(void)
383 return CURRENT_THREAD_PSEUDOHANDLE;
387 /***********************************************************************
388 * GetCurrentThreadId [KERNEL32.201] Returns thread identifier.
390 * RETURNS
391 * Thread identifier of calling thread
393 DWORD WINAPI GetCurrentThreadId(void)
395 return THDB_TO_THREAD_ID( THREAD_Current() );
399 /**********************************************************************
400 * GetLastError [KERNEL.148] [KERNEL32.227] Returns last-error code.
402 * RETURNS
403 * Calling thread's last error code value.
405 DWORD WINAPI GetLastError(void)
407 THDB *thread = THREAD_Current();
408 DWORD ret = thread->last_error;
409 TRACE(thread,"0x%lx\n",ret);
410 return ret;
414 /**********************************************************************
415 * SetLastError [KERNEL.147] [KERNEL32.497] Sets the last-error code.
417 * RETURNS
418 * None.
420 void WINAPI SetLastError(
421 DWORD error) /* [in] Per-thread error code */
423 THDB *thread = THREAD_Current();
424 /* This one must work before we have a thread (FIXME) */
426 TRACE(thread,"%p error=0x%lx\n",thread,error);
428 if (thread)
429 thread->last_error = error;
433 /**********************************************************************
434 * SetLastErrorEx [USER32.485] Sets the last-error code.
436 * RETURNS
437 * None.
439 void WINAPI SetLastErrorEx(
440 DWORD error, /* [in] Per-thread error code */
441 DWORD type) /* [in] Error type */
443 TRACE(thread, "(0x%08lx, 0x%08lx)\n", error,type);
444 switch(type) {
445 case 0:
446 break;
447 case SLE_ERROR:
448 case SLE_MINORERROR:
449 case SLE_WARNING:
450 /* Fall through for now */
451 default:
452 FIXME(thread, "(error=%08lx, type=%08lx): Unhandled type\n", error,type);
453 break;
455 SetLastError( error );
459 /**********************************************************************
460 * THREAD_TlsAlloc
462 DWORD THREAD_TlsAlloc(THDB *thread)
464 DWORD i, mask, ret = 0;
465 DWORD *bits = thread->process->tls_bits;
466 EnterCriticalSection( &thread->process->crit_section );
467 if (*bits == 0xffffffff)
469 bits++;
470 ret = 32;
471 if (*bits == 0xffffffff)
473 LeaveCriticalSection( &thread->process->crit_section );
474 SetLastError( ERROR_NO_MORE_ITEMS );
475 return 0xffffffff;
478 for (i = 0, mask = 1; i < 32; i++, mask <<= 1) if (!(*bits & mask)) break;
479 *bits |= mask;
480 LeaveCriticalSection( &thread->process->crit_section );
481 return ret + i;
485 /**********************************************************************
486 * TlsAlloc [KERNEL32.530] Allocates a TLS index.
488 * Allocates a thread local storage index
490 * RETURNS
491 * Success: TLS Index
492 * Failure: 0xFFFFFFFF
494 DWORD WINAPI TlsAlloc(void)
496 return THREAD_TlsAlloc(THREAD_Current());
500 /**********************************************************************
501 * TlsFree [KERNEL32.531] Releases a TLS index.
503 * Releases a thread local storage index, making it available for reuse
505 * RETURNS
506 * Success: TRUE
507 * Failure: FALSE
509 BOOL32 WINAPI TlsFree(
510 DWORD index) /* [in] TLS Index to free */
512 DWORD mask;
513 THDB *thread = THREAD_Current();
514 DWORD *bits = thread->process->tls_bits;
515 if (index >= 64)
517 SetLastError( ERROR_INVALID_PARAMETER );
518 return FALSE;
520 EnterCriticalSection( &thread->process->crit_section );
521 if (index >= 32) bits++;
522 mask = (1 << (index & 31));
523 if (!(*bits & mask)) /* already free? */
525 LeaveCriticalSection( &thread->process->crit_section );
526 SetLastError( ERROR_INVALID_PARAMETER );
527 return FALSE;
529 *bits &= ~mask;
530 thread->tls_array[index] = 0;
531 /* FIXME: should zero all other thread values */
532 LeaveCriticalSection( &thread->process->crit_section );
533 return TRUE;
537 /**********************************************************************
538 * TlsGetValue [KERNEL32.532] Gets value in a thread's TLS slot
540 * RETURNS
541 * Success: Value stored in calling thread's TLS slot for index
542 * Failure: 0 and GetLastError returns NO_ERROR
544 LPVOID WINAPI TlsGetValue(
545 DWORD index) /* [in] TLS index to retrieve value for */
547 THDB *thread = THREAD_Current();
548 if (index >= 64)
550 SetLastError( ERROR_INVALID_PARAMETER );
551 return NULL;
553 SetLastError( ERROR_SUCCESS );
554 return thread->tls_array[index];
558 /**********************************************************************
559 * TlsSetValue [KERNEL32.533] Stores a value in the thread's TLS slot.
561 * RETURNS
562 * Success: TRUE
563 * Failure: FALSE
565 BOOL32 WINAPI TlsSetValue(
566 DWORD index, /* [in] TLS index to set value for */
567 LPVOID value) /* [in] Value to be stored */
569 THDB *thread = THREAD_Current();
570 if (index >= 64)
572 SetLastError( ERROR_INVALID_PARAMETER );
573 return FALSE;
575 thread->tls_array[index] = value;
576 return TRUE;
580 /***********************************************************************
581 * SetThreadContext [KERNEL32.670] Sets context of thread.
583 * RETURNS
584 * Success: TRUE
585 * Failure: FALSE
587 BOOL32 WINAPI SetThreadContext(
588 HANDLE32 handle, /* [in] Handle to thread with context */
589 CONTEXT *context) /* [out] Address of context structure */
591 THDB *thread = THREAD_GetPtr( handle, THREAD_GET_CONTEXT, NULL );
592 if (!thread) return FALSE;
593 *context = thread->context;
594 K32OBJ_DecCount( &thread->header );
595 return TRUE;
598 /***********************************************************************
599 * GetThreadContext [KERNEL32.294] Retrieves context of thread.
601 * RETURNS
602 * Success: TRUE
603 * Failure: FALSE
605 BOOL32 WINAPI GetThreadContext(
606 HANDLE32 handle, /* [in] Handle to thread with context */
607 CONTEXT *context) /* [out] Address of context structure */
609 THDB *thread = THREAD_GetPtr( handle, THREAD_GET_CONTEXT, NULL );
610 if (!thread) return FALSE;
611 *context = thread->context;
612 K32OBJ_DecCount( &thread->header );
613 return TRUE;
617 /**********************************************************************
618 * GetThreadPriority [KERNEL32.296] Returns priority for thread.
620 * RETURNS
621 * Success: Thread's priority level.
622 * Failure: THREAD_PRIORITY_ERROR_RETURN
624 INT32 WINAPI GetThreadPriority(
625 HANDLE32 hthread) /* [in] Handle to thread */
627 THDB *thread;
628 INT32 ret;
630 if (!(thread = THREAD_GetPtr( hthread, THREAD_QUERY_INFORMATION, NULL )))
631 return THREAD_PRIORITY_ERROR_RETURN;
632 ret = thread->delta_priority;
633 K32OBJ_DecCount( &thread->header );
634 return ret;
638 /**********************************************************************
639 * SetThreadPriority [KERNEL32.514] Sets priority for thread.
641 * RETURNS
642 * Success: TRUE
643 * Failure: FALSE
645 BOOL32 WINAPI SetThreadPriority(
646 HANDLE32 hthread, /* [in] Handle to thread */
647 INT32 priority) /* [in] Thread priority level */
649 THDB *thread;
651 if (!(thread = THREAD_GetPtr( hthread, THREAD_SET_INFORMATION, NULL )))
652 return FALSE;
653 thread->delta_priority = priority;
654 K32OBJ_DecCount( &thread->header );
655 return TRUE;
659 /**********************************************************************
660 * TerminateThread [KERNEL32.685] Terminates a thread
662 * RETURNS
663 * Success: TRUE
664 * Failure: FALSE
666 BOOL32 WINAPI TerminateThread(
667 HANDLE32 handle, /* [in] Handle to thread */
668 DWORD exitcode) /* [in] Exit code for thread */
670 struct terminate_thread_request req;
672 req.handle = HANDLE_GetServerHandle( PROCESS_Current(), handle,
673 K32OBJ_THREAD, THREAD_TERMINATE );
674 req.exit_code = exitcode;
675 CLIENT_SendRequest( REQ_TERMINATE_THREAD, -1, 1, &req, sizeof(req) );
676 return !CLIENT_WaitReply( NULL, NULL, 0 );
680 /**********************************************************************
681 * GetExitCodeThread [KERNEL32.???] Gets termination status of thread.
683 * RETURNS
684 * Success: TRUE
685 * Failure: FALSE
687 BOOL32 WINAPI GetExitCodeThread(
688 HANDLE32 hthread, /* [in] Handle to thread */
689 LPDWORD exitcode) /* [out] Address to receive termination status */
691 struct get_thread_info_reply reply;
692 int handle = HANDLE_GetServerHandle( PROCESS_Current(), hthread,
693 K32OBJ_THREAD, THREAD_QUERY_INFORMATION );
694 CLIENT_SendRequest( REQ_GET_THREAD_INFO, -1, 1, &handle, sizeof(handle) );
695 if (CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL )) return FALSE;
696 if (exitcode) *exitcode = reply.exit_code;
697 return TRUE;
701 /**********************************************************************
702 * ResumeThread [KERNEL32.587] Resumes a thread.
704 * Decrements a thread's suspend count. When count is zero, the
705 * execution of the thread is resumed.
707 * RETURNS
708 * Success: Previous suspend count
709 * Failure: 0xFFFFFFFF
710 * Already running: 0
712 DWORD WINAPI ResumeThread(
713 HANDLE32 hthread) /* [in] Indentifies thread to restart */
715 THDB *thread;
716 DWORD oldcount;
718 SYSTEM_LOCK();
719 if (!(thread = THREAD_GetPtr( hthread, THREAD_QUERY_INFORMATION, NULL )))
721 SYSTEM_UNLOCK();
722 WARN(thread, "Invalid thread handle\n");
723 return 0xFFFFFFFF;
725 if ((oldcount = thread->suspend_count) != 0)
727 if (!--thread->suspend_count)
729 if (kill(thread->unix_pid, SIGCONT))
731 WARN(thread, "Unable to CONTinue pid: %04x\n",
732 thread->unix_pid);
733 oldcount = 0xFFFFFFFF;
737 K32OBJ_DecCount(&thread->header);
738 SYSTEM_UNLOCK();
739 return oldcount;
743 /**********************************************************************
744 * SuspendThread [KERNEL32.681] Suspends a thread.
746 * RETURNS
747 * Success: Previous suspend count
748 * Failure: 0xFFFFFFFF
750 DWORD WINAPI SuspendThread(
751 HANDLE32 hthread) /* [in] Handle to the thread */
753 THDB *thread;
754 DWORD oldcount;
756 SYSTEM_LOCK();
757 if (!(thread = THREAD_GetPtr( hthread, THREAD_QUERY_INFORMATION, NULL )))
759 SYSTEM_UNLOCK();
760 WARN(thread, "Invalid thread handle\n");
761 return 0xFFFFFFFF;
764 if (!(oldcount = thread->suspend_count))
766 if (thread->unix_pid == getpid())
767 WARN(thread, "Attempting to suspend myself\n" );
768 else
770 if (kill(thread->unix_pid, SIGSTOP))
772 WARN(thread, "Unable to STOP pid: %04x\n",
773 thread->unix_pid);
774 oldcount = 0xFFFFFFFF;
776 else thread->suspend_count++;
779 else thread->suspend_count++;
780 K32OBJ_DecCount( &thread->header );
781 SYSTEM_UNLOCK();
782 return oldcount;
787 /**********************************************************************
788 * GetThreadTimes [KERNEL32.???] Obtains timing information.
790 * NOTES
791 * What are the fields where these values are stored?
793 * RETURNS
794 * Success: TRUE
795 * Failure: FALSE
797 BOOL32 WINAPI GetThreadTimes(
798 HANDLE32 thread, /* [in] Specifies the thread of interest */
799 LPFILETIME creationtime, /* [out] When the thread was created */
800 LPFILETIME exittime, /* [out] When the thread was destroyed */
801 LPFILETIME kerneltime, /* [out] Time thread spent in kernel mode */
802 LPFILETIME usertime) /* [out] Time thread spent in user mode */
804 FIXME(thread,"(0x%08x): stub\n",thread);
805 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
806 return FALSE;
810 /**********************************************************************
811 * AttachThreadInput [KERNEL32.8] Attaches input of 1 thread to other
813 * Attaches the input processing mechanism of one thread to that of
814 * another thread.
816 * RETURNS
817 * Success: TRUE
818 * Failure: FALSE
820 BOOL32 WINAPI AttachThreadInput(
821 DWORD idAttach, /* [in] Thread to attach */
822 DWORD idAttachTo, /* [in] Thread to attach to */
823 BOOL32 fAttach) /* [in] Attach or detach */
825 BOOL32 ret;
827 FIXME(thread, "(0x%08lx,0x%08lx,%d): stub\n",idAttach,idAttachTo,fAttach);
828 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
829 if (fAttach) {
830 /* Attach threads */
831 ret = FALSE;
833 else {
834 /* Detach threads */
835 ret = FALSE;
837 return ret;
840 /**********************************************************************
841 * VWin32_BoostThreadGroup [KERNEL.535]
843 VOID WINAPI VWin32_BoostThreadGroup( DWORD threadId, INT32 boost )
845 FIXME(thread, "(0x%08lx,%d): stub\n", threadId, boost);
848 /**********************************************************************
849 * VWin32_BoostThreadStatic [KERNEL.536]
851 VOID WINAPI VWin32_BoostThreadStatic( DWORD threadId, INT32 boost )
853 FIXME(thread, "(0x%08lx,%d): stub\n", threadId, boost);