f6a85c868d5cce1438a6e268ecb58b850b9daff1
[platform/upstream/freerdp.git] / winpr / libwinpr / thread / thread.c
1 /**
2  * WinPR: Windows Portable Runtime
3  * Process Thread Functions
4  *
5  * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <assert.h>
25
26 #include <winpr/handle.h>
27
28 #include <winpr/thread.h>
29
30 /**
31  * api-ms-win-core-processthreads-l1-1-1.dll
32  *
33  * CreateRemoteThread
34  * CreateRemoteThreadEx
35  * CreateThread
36  * DeleteProcThreadAttributeList
37  * ExitThread
38  * FlushInstructionCache
39  * FlushProcessWriteBuffers
40  * GetCurrentThread
41  * GetCurrentThreadId
42  * GetCurrentThreadStackLimits
43  * GetExitCodeThread
44  * GetPriorityClass
45  * GetStartupInfoW
46  * GetThreadContext
47  * GetThreadId
48  * GetThreadIdealProcessorEx
49  * GetThreadPriority
50  * GetThreadPriorityBoost
51  * GetThreadTimes
52  * InitializeProcThreadAttributeList
53  * OpenThread
54  * OpenThreadToken
55  * QueryProcessAffinityUpdateMode
56  * QueueUserAPC
57  * ResumeThread
58  * SetPriorityClass
59  * SetThreadContext
60  * SetThreadPriority
61  * SetThreadPriorityBoost
62  * SetThreadStackGuarantee
63  * SetThreadToken
64  * SuspendThread
65  * SwitchToThread
66  * TerminateThread
67  * UpdateProcThreadAttribute
68  */
69
70 #ifndef _WIN32
71
72 #include <winpr/crt.h>
73 #include <winpr/platform.h>
74
75 #ifdef HAVE_UNISTD_H
76 #include <unistd.h>
77 #endif
78
79 #ifdef HAVE_EVENTFD_H
80 #include <sys/eventfd.h>
81 #endif
82
83 #include <winpr/debug.h>
84
85 #include <errno.h>
86 #include <fcntl.h>
87
88 #include <winpr/collections.h>
89
90 #include "thread.h"
91
92 #include "../handle/handle.h"
93 #include "../log.h"
94 #define TAG WINPR_TAG("thread")
95
96 static wListDictionary* thread_list = NULL;
97
98 static BOOL ThreadCloseHandle(HANDLE handle);
99 static void cleanup_handle(void* obj);
100
101 static BOOL ThreadIsHandled(HANDLE handle)
102 {
103         WINPR_THREAD* pThread = (WINPR_THREAD*) handle;
104
105         if (!pThread || (pThread->Type != HANDLE_TYPE_THREAD))
106         {
107                 SetLastError(ERROR_INVALID_HANDLE);
108                 return FALSE;
109         }
110
111         return TRUE;
112 }
113
114 static int ThreadGetFd(HANDLE handle)
115 {
116         WINPR_THREAD* pThread = (WINPR_THREAD*) handle;
117
118         if (!ThreadIsHandled(handle))
119                 return -1;
120
121         return pThread->pipe_fd[0];
122 }
123
124 static DWORD ThreadCleanupHandle(HANDLE handle)
125 {
126         WINPR_THREAD* thread = (WINPR_THREAD*) handle;
127
128         if (!ThreadIsHandled(handle))
129                 return WAIT_FAILED;
130
131         pthread_mutex_lock(&thread->mutex);
132
133         if (!thread->joined)
134         {
135                 int status;
136                 status = pthread_join(thread->thread, NULL);
137
138                 if (status != 0)
139                 {
140                         WLog_ERR(TAG, "pthread_join failure: [%d] %s",
141                                         status, strerror(status));
142                         pthread_mutex_unlock(&thread->mutex);
143                         return WAIT_FAILED;
144                 }
145                 else
146                         thread->joined = TRUE;
147         }
148
149         pthread_mutex_unlock(&thread->mutex);
150
151         return WAIT_OBJECT_0;
152 }
153
154 static HANDLE_OPS ops = {
155                 ThreadIsHandled,
156                 ThreadCloseHandle,
157                 ThreadGetFd,
158                 ThreadCleanupHandle
159 };
160
161
162 static void dump_thread(WINPR_THREAD* thread)
163 {
164 #if defined(WITH_DEBUG_THREADS)
165         void* stack = winpr_backtrace(20);
166         char** msg;
167         size_t used, i;
168         WLog_DBG(TAG, "Called from:");
169         msg = winpr_backtrace_symbols(stack, &used);
170
171         for (i = 0; i < used; i++)
172                 WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
173
174         free(msg);
175         winpr_backtrace_free(stack);
176         WLog_DBG(TAG, "Thread handle created still not closed!");
177         msg = winpr_backtrace_symbols(thread->create_stack, &used);
178
179         for (i = 0; i < used; i++)
180                 WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
181
182         free(msg);
183
184         if (thread->started)
185         {
186                 WLog_DBG(TAG, "Thread still running!");
187         }
188         else if (!thread->exit_stack)
189         {
190                 WLog_DBG(TAG, "Thread suspended.");
191         }
192         else
193         {
194                 WLog_DBG(TAG, "Thread exited at:");
195                 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
196
197                 for (i = 0; i < used; i++)
198                         WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
199
200                 free(msg);
201         }
202 #endif
203 }
204
205 /**
206  * TODO: implement thread suspend/resume using pthreads
207  * http://stackoverflow.com/questions/3140867/suspend-pthreads-without-using-condition
208  */
209 static BOOL set_event(WINPR_THREAD *thread)
210 {
211         int length;
212         BOOL status = FALSE;
213 #ifdef HAVE_EVENTFD_H
214         eventfd_t val = 1;
215
216         do
217         {
218                 length = eventfd_write(thread->pipe_fd[0], val);
219         }
220         while ((length < 0) && (errno == EINTR));
221
222         status = (length == 0) ? TRUE : FALSE;
223 #else
224         if (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0)
225         {
226                 length = write(thread->pipe_fd[1], "-", 1);
227
228                 if (length == 1)
229                         status = TRUE;
230         }
231         else
232         {
233                 status = TRUE;
234         }
235 #endif
236         return status;
237 }
238
239 static BOOL reset_event(WINPR_THREAD *thread)
240 {
241         int length;
242         BOOL status = FALSE;
243 #ifdef HAVE_EVENTFD_H
244         eventfd_t value;
245
246         do
247         {
248                 length = eventfd_read(thread->pipe_fd[0], &value);
249         }
250         while ((length < 0) && (errno == EINTR));
251
252         if ((length > 0) && (!status))
253                 status = TRUE;
254
255 #else
256         length = read(thread->pipe_fd[0], &length, 1);
257
258         if ((length == 1) && (!status))
259                 status = TRUE;
260
261 #endif
262         return status;
263 }
264
265 static BOOL thread_compare(void* a, void* b)
266 {
267         pthread_t* p1 = a;
268         pthread_t* p2 = b;
269         BOOL rc = pthread_equal(*p1, *p2);
270         return rc;
271 }
272
273 /* Thread launcher function responsible for registering
274  * cleanup handlers and calling pthread_exit, if not done
275  * in thread function. */
276 static void* thread_launcher(void* arg)
277 {
278         DWORD res = -1;
279         void* rc = NULL;
280         WINPR_THREAD* thread = (WINPR_THREAD*) arg;
281
282         if (!thread)
283         {
284                 WLog_ERR(TAG, "Called with invalid argument %p", arg);
285                 goto exit;
286         }
287         else
288         {
289                 void *(*fkt)(void*) = (void*) thread->lpStartAddress;
290
291                 if (!fkt)
292                 {
293                         WLog_ERR(TAG, "Thread function argument is %p", fkt);
294                         goto exit;
295                 }
296
297                 /* pthread_create(3) doesn't guaranty the thread ID to be set
298                  * before to invoke the start_routine() even if this seems to be
299                  * actually done.*/
300                 thread->thread = pthread_self(); 
301
302                 /* also done by winpr_StartThread(). This ensures thread_list
303                  * was updated before the thread could exit */
304                 if (!ListDictionary_Add(thread_list, &thread->thread, thread))
305                 {
306                     WLog_ERR(TAG, "Thread function argument is %p", fkt);
307                     goto exit;
308                 }
309
310                 SetEvent(thread->hLaunchedEvent);
311
312                 rc = fkt(thread->lpParameter);
313         }
314
315 exit:
316
317         if (thread)
318         {
319                 if (!thread->exited)
320                         thread->dwExitCode = (DWORD)(size_t)rc;
321
322                 set_event(thread);
323
324                 res = thread->dwExitCode;
325                 if (thread->detached || !thread->started)
326                         cleanup_handle(thread);
327         }
328         pthread_exit((void*) (size_t) res);
329         return rc;
330 }
331
332 static BOOL winpr_StartThread(WINPR_THREAD *thread)
333 {
334         pthread_attr_t attr;
335         pthread_attr_init(&attr);
336         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
337
338         if (thread->dwStackSize > 0)
339                 pthread_attr_setstacksize(&attr, (size_t) thread->dwStackSize);
340
341         thread->started = TRUE;
342         reset_event(thread);
343
344         if (pthread_create(&thread->thread, &attr, thread_launcher, thread))
345                 goto error;
346
347         if (WaitForSingleObject(thread->hLaunchedEvent, INFINITE) != WAIT_OBJECT_0)
348         {
349                 WLog_ERR(TAG, "failed to launch the thread");
350                 goto error;
351         }
352         
353         pthread_attr_destroy(&attr);
354         dump_thread(thread);
355         return TRUE;
356
357 error:
358         pthread_attr_destroy(&attr);
359         return FALSE;
360 }
361
362 HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize,
363                                         LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
364 {
365         HANDLE handle;
366         WINPR_THREAD* thread;
367
368         thread = (WINPR_THREAD*) calloc(1, sizeof(WINPR_THREAD));
369
370         if (!thread)
371                 return NULL;
372
373         thread->hLaunchedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
374         if (!thread->hLaunchedEvent)
375         {
376                 goto error_event;
377         }
378
379         thread->dwStackSize = dwStackSize;
380         thread->lpParameter = lpParameter;
381         thread->lpStartAddress = lpStartAddress;
382         thread->lpThreadAttributes = lpThreadAttributes;
383         thread->ops = &ops;
384
385 #if defined(WITH_DEBUG_THREADS)
386         thread->create_stack = winpr_backtrace(20);
387         dump_thread(thread);
388 #endif
389         thread->pipe_fd[0] = -1;
390         thread->pipe_fd[1] = -1;
391         
392 #ifdef HAVE_EVENTFD_H
393         thread->pipe_fd[0] = eventfd(0, EFD_NONBLOCK);
394
395         if (thread->pipe_fd[0] < 0)
396         {
397                 WLog_ERR(TAG, "failed to create thread pipe fd 0");
398                 goto error_pipefd0;
399         }
400 #else
401         if (pipe(thread->pipe_fd) < 0)
402         {
403                 WLog_ERR(TAG, "failed to create thread pipe");
404                 goto error_pipefd0;
405         }
406         
407         {
408                 int flags = fcntl(thread->pipe_fd[0], F_GETFL);
409                 fcntl(thread->pipe_fd[0], F_SETFL, flags | O_NONBLOCK);
410         }
411 #endif
412         
413         if(pthread_mutex_init(&thread->mutex, 0) != 0)
414         {
415                 WLog_ERR(TAG, "failed to initialize thread mutex");
416                 goto error_mutex;
417         }
418
419         WINPR_HANDLE_SET_TYPE(thread, HANDLE_TYPE_THREAD);
420         handle = (HANDLE) thread;
421
422         if (!thread_list)
423         {
424                 thread_list = ListDictionary_New(TRUE);
425                 if (!thread_list)
426                 {
427                         WLog_ERR(TAG, "Couldn't create global thread list");
428                         goto error_thread_list;
429                 }
430                 thread_list->objectKey.fnObjectEquals = thread_compare;
431         }
432
433         if (!(dwCreationFlags & CREATE_SUSPENDED))
434         {
435                 if (!winpr_StartThread(thread))
436                         goto error_thread_list;
437         }
438         else
439         {
440                 if (!set_event(thread))
441                         goto error_thread_list;
442         }
443
444         return handle;
445
446 error_thread_list:
447         pthread_mutex_destroy(&thread->mutex);
448 error_mutex:
449         if (thread->pipe_fd[1] >= 0)
450                 close(thread->pipe_fd[1]);
451         if (thread->pipe_fd[0] >= 0)
452                 close(thread->pipe_fd[0]);
453 error_pipefd0:
454 error_event:
455         free(thread);
456         return NULL;
457 }
458
459 void cleanup_handle(void *obj)
460 {
461         WINPR_THREAD* thread = (WINPR_THREAD*) obj;
462         int rc = pthread_mutex_destroy(&thread->mutex);
463
464         if (rc)
465                 WLog_ERR(TAG, "failed to destroy mutex [%d] %s (%d)",
466                                 rc, strerror(errno), errno);
467
468         if (thread->hLaunchedEvent)
469                 CloseHandle(thread->hLaunchedEvent);
470
471         if (thread->pipe_fd[0])
472                 close(thread->pipe_fd[0]);
473
474         if (thread->pipe_fd[1])
475                 close(thread->pipe_fd[1]);
476
477         if (thread_list && ListDictionary_Contains(thread_list, &thread->thread))
478                 ListDictionary_Remove(thread_list, &thread->thread);
479
480 #if defined(WITH_DEBUG_THREADS)
481
482         if (thread->create_stack)
483                 winpr_backtrace_free(thread->create_stack);
484
485         if (thread->exit_stack)
486                 winpr_backtrace_free(thread->exit_stack);
487
488 #endif
489         free(thread);
490 }
491
492 BOOL ThreadCloseHandle(HANDLE handle)
493 {
494         WINPR_THREAD* thread = (WINPR_THREAD*) handle;
495
496         if (!thread_list)
497         {
498                 WLog_ERR(TAG, "Thread list does not exist, check call!");
499                 dump_thread(thread);
500         }
501         else if (!ListDictionary_Contains(thread_list, &thread->thread))
502         {
503                 WLog_ERR(TAG, "Thread list does not contain this thread! check call!");
504                 dump_thread(thread);
505         }
506         else
507         {
508                 ListDictionary_Lock(thread_list);
509                 dump_thread(thread);
510
511                 if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0))
512                 {
513                         WLog_ERR(TAG, "Thread running, setting to detached state!");
514                         thread->detached = TRUE;
515                         pthread_detach(thread->thread);
516                 }
517                 else
518                 {
519                         cleanup_handle(thread);
520                 }
521
522                 ListDictionary_Unlock(thread_list);
523
524                 if (ListDictionary_Count(thread_list) < 1)
525                 {
526                         ListDictionary_Free(thread_list);
527                         thread_list = NULL;
528                 }
529         }
530
531         return TRUE;
532 }
533
534 HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize,
535                                                   LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
536 {
537         WLog_ERR(TAG, "not implemented");
538         return NULL;
539 }
540
541 VOID ExitThread(DWORD dwExitCode)
542 {
543         DWORD rc;
544         pthread_t tid = pthread_self();
545
546         if (!thread_list)
547         {
548                 WLog_ERR(TAG, "function called without existing thread list!");
549 #if defined(WITH_DEBUG_THREADS)
550                 DumpThreadHandles();
551 #endif
552                 pthread_exit(0);
553         }
554         else if (!ListDictionary_Contains(thread_list, &tid))
555         {
556                 WLog_ERR(TAG, "function called, but no matching entry in thread list!");
557 #if defined(WITH_DEBUG_THREADS)
558                 DumpThreadHandles();
559 #endif
560                 pthread_exit(0);
561         }
562         else
563         {
564                 WINPR_THREAD* thread;
565
566                 ListDictionary_Lock(thread_list);
567                 thread = ListDictionary_GetItemValue(thread_list, &tid);
568
569                 assert(thread);
570                 thread->exited = TRUE;
571                 thread->dwExitCode = dwExitCode;
572 #if defined(WITH_DEBUG_THREADS)
573                 thread->exit_stack = winpr_backtrace(20);
574 #endif
575                 ListDictionary_Unlock(thread_list);
576                 set_event(thread);
577
578                 rc = thread->dwExitCode;
579                 if (thread->detached || !thread->started)
580                         cleanup_handle(thread);
581
582                 pthread_exit((void*) (size_t) rc);
583         }
584 }
585
586 BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode)
587 {
588         ULONG Type;
589         PVOID Object;
590         WINPR_THREAD* thread;
591
592         if (!winpr_Handle_GetInfo(hThread, &Type, &Object))
593                 return FALSE;
594
595         thread = (WINPR_THREAD*) Object;
596         *lpExitCode = thread->dwExitCode;
597         return TRUE;
598 }
599
600 HANDLE _GetCurrentThread(VOID)
601 {
602         HANDLE hdl = NULL;
603         pthread_t tid = pthread_self();
604
605         if (!thread_list)
606         {
607                 WLog_ERR(TAG, "function called without existing thread list!");
608 #if defined(WITH_DEBUG_THREADS)
609                 DumpThreadHandles();
610 #endif
611         }
612         else if (!ListDictionary_Contains(thread_list, &tid))
613         {
614                 WLog_ERR(TAG, "function called, but no matching entry in thread list!");
615 #if defined(WITH_DEBUG_THREADS)
616                 DumpThreadHandles();
617 #endif
618         }
619         else
620         {
621                 hdl = ListDictionary_GetItemValue(thread_list, &tid);
622         }
623
624         return hdl;
625 }
626
627 DWORD GetCurrentThreadId(VOID)
628 {
629         pthread_t tid;
630         tid = pthread_self();
631
632         /* Since pthread_t can be 64-bits on some systems, take just the    */
633         /* lower 32-bits of it for the thread ID returned by this function. */
634         tid = (long)tid & 0xffffffff;
635         return (DWORD) tid;
636 }
637
638 DWORD ResumeThread(HANDLE hThread)
639 {
640         ULONG Type;
641         PVOID Object;
642         WINPR_THREAD *thread;
643
644         if (!winpr_Handle_GetInfo(hThread, &Type, &Object))
645                 return 0;
646
647         thread = (WINPR_THREAD*) Object;
648         pthread_mutex_lock(&thread->mutex);
649
650         if (!thread->started)
651                 winpr_StartThread(thread);
652         else
653                 WLog_WARN(TAG, "Thread already started!");
654
655         pthread_mutex_unlock(&thread->mutex);
656         return 0;
657 }
658
659 DWORD SuspendThread(HANDLE hThread)
660 {
661         WLog_ERR(TAG, "Function not implemented!");
662         return 0;
663 }
664
665 BOOL SwitchToThread(VOID)
666 {
667         return TRUE;
668 }
669
670 BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode)
671 {
672         ULONG Type;
673         PVOID Object;
674         WINPR_THREAD* thread;
675
676         if (!winpr_Handle_GetInfo(hThread, &Type, &Object))
677                 return 0;
678
679         thread = (WINPR_THREAD*) Object;
680         thread->exited = TRUE;
681         thread->dwExitCode = dwExitCode;
682         pthread_mutex_lock(&thread->mutex);
683 #ifndef ANDROID
684         pthread_cancel(thread->thread);
685 #else
686         WLog_ERR(TAG, "Function not supported on this platform!");
687 #endif
688         pthread_mutex_unlock(&thread->mutex);
689         set_event(thread);
690         return TRUE;
691 }
692
693 #if defined(WITH_DEBUG_THREADS)
694 VOID DumpThreadHandles(void)
695 {
696         char** msg;
697         size_t used, i;
698         void* stack = winpr_backtrace(20);
699         WLog_DBG(TAG, "---------------- Called from ----------------------------");
700         msg = winpr_backtrace_symbols(stack, &used);
701
702         for (i = 0; i < used; i++)
703         {
704                 WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
705         }
706
707         free(msg);
708         winpr_backtrace_free(stack);
709         WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------");
710
711         if (!thread_list)
712         {
713                 WLog_DBG(TAG, "All threads properly shut down and disposed of.");
714         }
715         else
716         {
717                 ULONG_PTR *keys = NULL;
718                 ListDictionary_Lock(thread_list);
719                 int x, count = ListDictionary_GetKeys(thread_list, &keys);
720                 WLog_DBG(TAG, "Dumping %d elements", count);
721
722                 for (x = 0; x < count; x++)
723                 {
724                         WINPR_THREAD* thread = ListDictionary_GetItemValue(thread_list, (void*) keys[x]);
725                         WLog_DBG(TAG, "Thread [%d] handle created still not closed!", x);
726                         msg = winpr_backtrace_symbols(thread->create_stack, &used);
727
728                         for (i = 0; i < used; i++)
729                         {
730                                 WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
731                         }
732
733                         free(msg);
734
735                         if (thread->started)
736                         {
737                                 WLog_DBG(TAG, "Thread [%d] still running!",     x);
738                         }
739                         else
740                         {
741                                 WLog_DBG(TAG, "Thread [%d] exited at:", x);
742                                 msg = winpr_backtrace_symbols(thread->exit_stack, &used);
743
744                                 for (i=0; i<used; i++)
745                                         WLog_DBG(TAG, "[%d]: %s", i, msg[i]);
746
747                                 free(msg);
748                         }
749                 }
750
751                 free(keys);
752
753                 ListDictionary_Unlock(thread_list);
754         }
755
756         WLog_DBG(TAG, "---------------- End Dumping thread handles -------------");
757 }
758 #endif
759 #endif
760