2 Copyright (C) 2014 Szilard Biro
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
24 #include <dos/dostags.h>
25 #include <clib/alib_protos.h>
26 #include <proto/exec.h>
27 #include <proto/dos.h>
28 #include <proto/timer.h>
30 #include <aros/symbolsets.h>
32 #include <constructor.h>
33 #define StackSwapArgs PPCStackSwapArgs
34 #define NewStackSwap NewPPCStackSwap
46 #define FALLBACKSIGNAL SIGBREAKB_CTRL_E
48 #define PTHREAD_INVALID_ID ((pthread_t)-1)
60 void (*destructor
)(void *);
67 void (*routine
)(void *);
74 void *(*start
)(void *);
76 struct MsgPort
*msgport
;
78 struct Process
*process
;
85 TLSKey tls
[PTHREAD_KEYS_MAX
];
86 struct MinList cleanup
;
89 #define PTHREAD_FIRST_THREAD_ID (1) /* First thread id will be 1 so that it is different than default value of pthread_t */
91 static ThreadInfo threads
[PTHREAD_THREADS_MAX
];
92 //static volatile pthread_t nextid = 0;
93 static struct SignalSemaphore thread_sem
;
99 static int SemaphoreIsInvalid(struct SignalSemaphore
*sem
)
101 return (!sem
|| sem
->ss_Link
.ln_Type
!= NT_SIGNALSEM
|| sem
->ss_WaitQueue
.mlh_Tail
!= NULL
);
104 static ThreadInfo
*GetThreadInfo(pthread_t thread
)
106 ThreadInfo
*inf
= NULL
;
108 //if (thread < nextid)
109 // TODO: more robust error handling?
110 if (thread
< PTHREAD_THREADS_MAX
)
111 inf
= &threads
[thread
];
116 static pthread_t
GetThreadId(struct Task
*task
)
120 ObtainSemaphore(&thread_sem
);
122 for (i
= PTHREAD_FIRST_THREAD_ID
; i
< PTHREAD_THREADS_MAX
; i
++)
124 if ((struct Task
*)threads
[i
].process
== task
)
128 ReleaseSemaphore(&thread_sem
);
130 if (i
>= PTHREAD_THREADS_MAX
)
131 i
= PTHREAD_INVALID_ID
;
137 // Thread specific data functions
140 int pthread_key_create(pthread_key_t
*key
, void (*destructor
)(void *))
147 D(bug("%s(%p, %p)\n", __FUNCTION__
, key
, destructor
));
152 thread
= pthread_self();
153 inf
= GetThreadInfo(thread
);
155 for (i
= 0; i
< PTHREAD_KEYS_MAX
; i
++)
157 if (inf
->tls
[i
].used
== FALSE
)
161 if (i
>= PTHREAD_KEYS_MAX
)
166 tls
->destructor
= destructor
;
173 int pthread_key_delete(pthread_key_t key
)
179 D(bug("%s(%u)\n", __FUNCTION__
, key
));
181 thread
= pthread_self();
182 inf
= GetThreadInfo(thread
);
183 tls
= &inf
->tls
[key
];
185 if (tls
->used
== FALSE
)
189 tls
->destructor(tls
->value
);
192 tls
->destructor
= NULL
;
197 int pthread_setspecific(pthread_key_t key
, const void *value
)
203 D(bug("%s(%u)\n", __FUNCTION__
, key
));
205 thread
= pthread_self();
206 inf
= GetThreadInfo(thread
);
207 tls
= &inf
->tls
[key
];
209 if (tls
->used
== FALSE
)
212 tls
->value
= (void *)value
;
217 void *pthread_getspecific(pthread_key_t key
)
224 D(bug("%s(%u)\n", __FUNCTION__
, key
));
226 thread
= pthread_self();
227 inf
= GetThreadInfo(thread
);
228 tls
= &inf
->tls
[key
];
230 if (tls
->used
== TRUE
)
237 // Mutex attribute functions
240 int pthread_mutexattr_init(pthread_mutexattr_t
*attr
)
242 D(bug("%s(%p)\n", __FUNCTION__
, attr
));
247 memset(attr
, 0, sizeof(pthread_mutexattr_t
));
252 int pthread_mutexattr_settype(pthread_mutexattr_t
*attr
, int kind
)
254 D(bug("%s(%p)\n", __FUNCTION__
, attr
));
268 int pthread_mutex_init(pthread_mutex_t
*mutex
, const pthread_mutexattr_t
*attr
)
270 D(bug("%s(%p, %p)\n", __FUNCTION__
, mutex
, attr
));
275 InitSemaphore(&mutex
->semaphore
);
280 int pthread_mutex_destroy(pthread_mutex_t
*mutex
)
282 //D(bug("%s(%p)\n", __FUNCTION__, mutex));
287 memset(mutex
, 0, sizeof(pthread_mutex_t
));
292 int pthread_mutex_lock(pthread_mutex_t
*mutex
)
294 //D(bug("%s(%p)\n", __FUNCTION__, mutex));
299 // initialize static mutexes
300 if (SemaphoreIsInvalid(&mutex
->semaphore
))
301 pthread_mutex_init(mutex
, NULL
);
303 ObtainSemaphore(&mutex
->semaphore
);
308 int pthread_mutex_trylock(pthread_mutex_t
*mutex
)
312 //D(bug("%s(%p)\n", __FUNCTION__, mutex));
317 // initialize static mutexes
318 if (SemaphoreIsInvalid(&mutex
->semaphore
))
319 pthread_mutex_init(mutex
, NULL
);
321 ret
= AttemptSemaphore(&mutex
->semaphore
);
323 return (ret
== TRUE
) ? 0 : EBUSY
;
326 int pthread_mutex_unlock(pthread_mutex_t
*mutex
)
328 //D(bug("%s(%p)\n", __FUNCTION__, mutex));
333 // initialize static mutexes
334 if (SemaphoreIsInvalid(&mutex
->semaphore
))
335 pthread_mutex_init(mutex
, NULL
);
337 ReleaseSemaphore(&mutex
->semaphore
);
343 // Condition variable functions
346 int pthread_cond_init(pthread_cond_t
*cond
, const pthread_condattr_t
*attr
)
348 D(bug("%s(%p, %p)\n", __FUNCTION__
, cond
, attr
));
353 InitSemaphore(&cond
->semaphore
);
354 NewList((struct List
*)&cond
->waiters
);
360 int pthread_cond_destroy(pthread_cond_t
*cond
)
362 D(bug("%s(%p)\n", __FUNCTION__
, cond
));
367 if (cond
->waiting
> 0)
370 memset(cond
, 0, sizeof(pthread_cond_t
));
375 int pthread_cond_timedwait(pthread_cond_t
*cond
, pthread_mutex_t
*mutex
, const struct timespec
*abstime
)
381 struct MsgPort
*timermp
= NULL
;
382 struct timerequest
*timerio
= NULL
;
383 struct Device
*TimerBase
= NULL
;
385 //D(bug("%s(%p, %p, %p)\n", __FUNCTION__, cond, mutex, abstime));
387 if (cond
== NULL
|| mutex
== NULL
)
390 // initialize static conditions
391 if (SemaphoreIsInvalid(&cond
->semaphore
))
392 pthread_cond_init(cond
, NULL
);
396 if (!(timermp
= CreateMsgPort()))
399 if (!(timerio
= (struct timerequest
*)CreateIORequest(timermp
, sizeof(struct timerequest
))))
401 DeleteMsgPort(timermp
);
405 if (OpenDevice(TIMERNAME
, UNIT_MICROHZ
, &timerio
->tr_node
, 0) != 0)
407 DeleteMsgPort(timermp
);
408 DeleteIORequest((struct IORequest
*)timerio
);
412 TimerBase
= timerio
->tr_node
.io_Device
;
417 struct timeval starttime
;
419 gettimeofday(&starttime
, NULL
);
420 timerio
->tr_node
.io_Command
= TR_ADDREQUEST
;
421 timerio
->tr_time
.tv_secs
= abstime
->tv_sec
;
422 timerio
->tr_time
.tv_micro
= abstime
->tv_nsec
/ 1000;
423 SubTime(&timerio
->tr_time
, &starttime
);
424 timermask
= 1 << timermp
->mp_SigBit
;
426 SendIO((struct IORequest
*)timerio
);
429 waiter
.task
= FindTask(NULL
);
430 signal
= AllocSignal(-1);
433 signal
= FALLBACKSIGNAL
;
434 SetSignal(1 << FALLBACKSIGNAL
, 0);
436 waiter
.sigmask
= 1 << signal
;
437 sigs
|= waiter
.sigmask
;
438 ObtainSemaphore(&cond
->semaphore
);
439 AddTail((struct List
*)&cond
->waiters
, (struct Node
*)&waiter
);
441 ReleaseSemaphore(&cond
->semaphore
);
443 pthread_mutex_unlock(mutex
);
445 pthread_mutex_lock(mutex
);
447 ObtainSemaphore(&cond
->semaphore
);
448 Remove((struct Node
*)&waiter
);
450 ReleaseSemaphore(&cond
->semaphore
);
452 if (signal
!= FALLBACKSIGNAL
)
457 if (!CheckIO((struct IORequest
*)timerio
))
459 AbortIO((struct IORequest
*)timerio
);
460 WaitIO((struct IORequest
*)timerio
);
462 CloseDevice((struct IORequest
*)timerio
);
463 DeleteIORequest((struct IORequest
*)timerio
);
464 DeleteMsgPort(timermp
);
466 if (sigs
& timermask
)
473 int pthread_cond_wait(pthread_cond_t
*cond
, pthread_mutex_t
*mutex
)
475 //D(bug("%s(%p)\n", __FUNCTION__, cond));
477 return pthread_cond_timedwait(cond
, mutex
, NULL
);
480 static int _pthread_cond_broadcast(pthread_cond_t
*cond
, BOOL onlyfirst
)
484 //D(bug("%s(%p, %d)\n", __FUNCTION__, cond, onlyfirst));
489 // initialize static conditions
490 if (SemaphoreIsInvalid(&cond
->semaphore
))
491 pthread_cond_init(cond
, NULL
);
493 ObtainSemaphore(&cond
->semaphore
);
494 if (cond
->waiting
> 0)
496 ForeachNode(&cond
->waiters
, waiter
)
498 Signal(waiter
->task
, waiter
->sigmask
);
499 if (onlyfirst
) break;
502 ReleaseSemaphore(&cond
->semaphore
);
507 int pthread_cond_signal(pthread_cond_t
*cond
)
509 //D(bug("%s(%p)\n", __FUNCTION__, cond));
511 return _pthread_cond_broadcast(cond
, TRUE
);
514 int pthread_cond_broadcast(pthread_cond_t
*cond
)
516 //D(bug("%s(%p)\n", __FUNCTION__, cond));
518 return _pthread_cond_broadcast(cond
, FALSE
);
522 // Thread attribute functions
525 int pthread_attr_init(pthread_attr_t
*attr
)
527 struct Task
*task
= FindTask(NULL
);
529 D(bug("%s(%p)\n", __FUNCTION__
, attr
));
534 memset(attr
, 0, sizeof(pthread_attr_t
));
535 // inherit the priority and stack size of the parent thread
536 attr
->param
.sched_priority
= task
->tc_Node
.ln_Pri
;
537 attr
->stacksize
= (UBYTE
*)task
->tc_SPUpper
- (UBYTE
*)task
->tc_SPLower
;
542 int pthread_attr_destroy(pthread_attr_t
*attr
)
544 D(bug("%s(%p)\n", __FUNCTION__
, attr
));
549 memset(attr
, 0, sizeof(pthread_attr_t
));
554 int pthread_attr_getstack(const pthread_attr_t
*attr
, void **stackaddr
, size_t *stacksize
)
556 D(bug("%s(%p, %p)\n", __FUNCTION__
, attr
, stackaddr
));
561 if (stackaddr
!= NULL
)
562 *stackaddr
= attr
->stackaddr
;
564 if (stacksize
!= NULL
)
565 *stacksize
= attr
->stacksize
;
570 int pthread_attr_setstack(pthread_attr_t
*attr
, void *stackaddr
, size_t stacksize
)
572 D(bug("%s(%p, %p)\n", __FUNCTION__
, attr
, stackaddr
));
574 if (attr
== NULL
|| (stackaddr
!= NULL
&& stacksize
== 0))
577 attr
->stackaddr
= stackaddr
;
578 attr
->stacksize
= stacksize
;
583 int pthread_attr_getstacksize(const pthread_attr_t
*attr
, size_t *stacksize
)
585 D(bug("%s(%p, %p)\n", __FUNCTION__
, attr
, stacksize
));
587 return pthread_attr_getstack(attr
, NULL
, stacksize
);
590 int pthread_attr_setstacksize(pthread_attr_t
*attr
, size_t stacksize
)
592 D(bug("%s(%p, %u)\n", __FUNCTION__
, attr
, stacksize
));
594 return pthread_attr_setstack(attr
, NULL
, stacksize
);
597 int pthread_attr_getschedparam(const pthread_attr_t
*attr
, struct sched_param
*param
)
599 D(bug("%s(%p, %p)\n", __FUNCTION__
, attr
, param
));
601 if (attr
== NULL
|| param
== NULL
)
604 *param
= attr
->param
;
609 int pthread_attr_setschedparam(pthread_attr_t
*attr
, const struct sched_param
*param
)
611 D(bug("%s(%p, %p)\n", __FUNCTION__
, attr
, param
));
613 if (attr
== NULL
|| param
== NULL
)
616 attr
->param
= *param
;
625 static void StarterFunc(void)
629 D(bug("%s()\n", __FUNCTION__
));
631 inf
= (ThreadInfo
*)FindTask(NULL
)->tc_UserData
;
633 //inf->process->pr_Task.tc_Node.ln_Name[inf->oldlen];
635 // we have to set the priority here to avoid race conditions
636 SetTaskPri((struct Task
*)inf
->process
, inf
->attr
.param
.sched_priority
);
638 if (!setjmp(inf
->jmp
))
640 if (inf
->attr
.stackaddr
!= NULL
&& inf
->attr
.stacksize
> 0)
642 struct StackSwapArgs swapargs
;
643 struct StackSwapStruct stack
;
645 swapargs
.Args
[0] = (IPTR
)inf
->arg
;
646 stack
.stk_Lower
= inf
->attr
.stackaddr
;
647 stack
.stk_Upper
= (APTR
)((IPTR
)stack
.stk_Lower
+ inf
->attr
.stacksize
);
648 stack
.stk_Pointer
= stack
.stk_Upper
;
650 inf
->ret
= (void *)NewStackSwap(&stack
, inf
->start
, &swapargs
);
654 inf
->ret
= inf
->start(inf
->arg
);
662 int pthread_create(pthread_t
*thread
, const pthread_attr_t
*attr
, void *(*start
)(void *), void *arg
)
670 D(bug("%s(%p, %p, %p, %p)\n", __FUNCTION__
, thread
, attr
, start
, arg
));
672 if (thread
== NULL
|| start
== NULL
)
675 ObtainSemaphore(&thread_sem
);
677 //threadnew = nextid++; //__sync_add_and_fetch(&nextid, 1);
678 threadnew
= GetThreadId(NULL
);
679 if (threadnew
== PTHREAD_INVALID_ID
)
681 ReleaseSemaphore(&thread_sem
);
685 inf
= GetThreadInfo(threadnew
);
686 memset(inf
, 0, sizeof(ThreadInfo
));
691 pthread_attr_init(&inf
->attr
);
693 NewList((struct List
*)&inf
->cleanup
);
695 // let's trick CreateNewProc into allocating a larger buffer for the name
696 snprintf(buf
, sizeof(buf
), "pthread thread #%d", threadnew
);
697 oldlen
= strlen(buf
);
698 memset(name
, ' ', sizeof(name
));
699 memcpy(name
, buf
, oldlen
);
700 name
[sizeof(name
) - 1] = '\0';
702 inf
->msgport
= CreateMsgPort();
705 ReleaseSemaphore(&thread_sem
);
709 inf
->msg
.mn_Node
.ln_Type
= NT_MESSAGE
;
710 inf
->msg
.mn_ReplyPort
= inf
->msgport
;
711 inf
->msg
.mn_Length
= sizeof(inf
->msg
);
713 inf
->process
= CreateNewProcTags(NP_Entry
, StarterFunc
,
715 NP_CodeType
, CODETYPE_PPC
,
716 (inf
->attr
.stackaddr
== NULL
&& inf
->attr
.stacksize
> 0) ? NP_PPCStackSize
: TAG_IGNORE
, inf
->attr
.stacksize
,
718 (inf
->attr
.stackaddr
== NULL
&& inf
->attr
.stacksize
> 0) ? NP_StackSize
: TAG_IGNORE
, inf
->attr
.stacksize
,
724 ReleaseSemaphore(&thread_sem
);
728 DeleteMsgPort(inf
->msgport
);
738 int pthread_detach(pthread_t thread
)
740 D(bug("%s(%u) not implemented\n", __FUNCTION__
, thread
));
745 int pthread_join(pthread_t thread
, void **value_ptr
)
749 D(bug("%s(%u, %p)\n", __FUNCTION__
, thread
, value_ptr
));
751 inf
= GetThreadInfo(thread
);
756 //while (!GetMsg(inf->msgport))
757 WaitPort(inf
->msgport
);
759 DeleteMsgPort(inf
->msgport
);
760 //inf->msgport = NULL;
763 *value_ptr
= inf
->ret
;
765 ObtainSemaphore(&thread_sem
);
766 memset(inf
, 0, sizeof(ThreadInfo
));
767 ReleaseSemaphore(&thread_sem
);
772 int pthread_equal(pthread_t t1
, pthread_t t2
)
774 D(bug("%s(%u, %u)\n", __FUNCTION__
, t1
, t2
));
779 pthread_t
pthread_self(void)
784 D(bug("%s()\n", __FUNCTION__
));
786 task
= FindTask(NULL
);
787 thread
= GetThreadId(task
);
789 // add non-pthread processes to our list, so we can handle the main thread
790 if (thread
== PTHREAD_INVALID_ID
)
794 ObtainSemaphore(&thread_sem
);
795 thread
= GetThreadId(NULL
);
796 //thread = nextid++; //__sync_add_and_fetch(&nextid, 1);
797 inf
= GetThreadInfo(thread
);
798 memset(inf
, 0, sizeof(ThreadInfo
));
799 NewList((struct List
*)&inf
->cleanup
);
800 inf
->process
= (struct Process
*)task
;
801 ReleaseSemaphore(&thread_sem
);
807 int pthread_cancel(pthread_t thread
)
809 D(bug("%s(%u) not implemented\n", __FUNCTION__
, thread
));
811 // TODO: should I do a pthread_join here?
815 void pthread_exit(void *value_ptr
)
818 CleanupHandler
*handler
;
820 D(bug("%s(%p)\n", __FUNCTION__
, value_ptr
));
822 inf
= GetThreadInfo(pthread_self());
823 inf
->ret
= value_ptr
;
825 while ((handler
= (CleanupHandler
*)RemTail((struct List
*)&inf
->cleanup
)))
826 if (handler
->routine
)
827 handler
->routine(handler
->arg
);
829 longjmp(inf
->jmp
, 1);
832 #if defined __mc68000__
833 /* No CAS instruction on m68k */
834 static int __m68k_sync_val_compare_and_swap(int *v
, int o
, int n
)
846 #undef __sync_val_compare_and_swap
847 #define __sync_val_compare_and_swap(v, o, n) __m68k_sync_val_compare_and_swap(v, o, n)
850 int pthread_once(pthread_once_t
*once_control
, void (*init_routine
)(void))
852 if (once_control
== NULL
|| init_routine
== NULL
)
855 if (__sync_val_compare_and_swap(&once_control
->started
, FALSE
, TRUE
))
857 if (!once_control
->done
)
860 once_control
->done
= TRUE
;
868 // Scheduling functions
871 int pthread_setschedparam(pthread_t thread
, int policy
, const struct sched_param
*param
)
875 D(bug("%s(%u, %d, %p)\n", __FUNCTION__
, thread
, policy
, param
));
880 inf
= GetThreadInfo(thread
);
881 SetTaskPri((struct Task
*)inf
->process
, param
->sched_priority
);
889 int pthread_setname_np(pthread_t thread
, const char *name
)
894 D(bug("%s(%u, %s)\n", __FUNCTION__
, thread
, name
));
899 inf
= GetThreadInfo(thread
);
900 currentName
= inf
->process
->pr_Task
.tc_Node
.ln_Name
;
902 if (strlen(name
) + 1 > NAMELEN
)
905 strncpy(currentName
, name
, NAMELEN
);
910 int pthread_getname_np(pthread_t thread
, char *name
, size_t len
)
915 D(bug("%s(%u, %p, %u)\n", __FUNCTION__
, thread
, name
, len
));
917 if (name
== NULL
|| len
== 0)
920 inf
= GetThreadInfo(thread
);
921 currentName
= inf
->process
->pr_Task
.tc_Node
.ln_Name
;
923 if (strlen(currentName
) + 1 > len
)
925 // TODO: partially copy the name?
926 strncpy(name
, currentName
, len
);
932 // Cancellation cleanup
935 // theads can't be cancelled, but they can still call pthread_exit, which
936 // will execute these clean-up handlers
937 void pthread_cleanup_push(void (*routine
)(void *), void *arg
)
941 CleanupHandler
*handler
;
943 D(bug("%s(%p, %p)\n", __FUNCTION__
, routine
, arg
));
945 handler
= calloc(1, sizeof(CleanupHandler
));
947 if (routine
== NULL
|| handler
== NULL
)
950 thread
= pthread_self();
951 inf
= GetThreadInfo(thread
);
953 AddTail((struct List
*)&inf
->cleanup
, (struct Node
*)handler
);
956 void pthread_cleanup_pop(int execute
)
960 CleanupHandler
*handler
;
962 D(bug("%s(%d)\n", __FUNCTION__
, execute
));
964 thread
= pthread_self();
965 inf
= GetThreadInfo(thread
);
966 handler
= (CleanupHandler
*)RemTail((struct List
*)&inf
->cleanup
);
968 if (handler
&& handler
->routine
&& execute
)
969 handler
->routine(handler
->arg
);
978 int pthread_kill(pthread_t thread
, int sig
)
984 D(bug("%s(%u, %d)\n", __FUNCTION__
, thread
, sig
));
987 inf
= GetThreadInfo(thread
);
988 et
= GetETask((struct Task
*)inf
->process
);
993 return kill((pid_t
)et
->et_UniqueID
, sig
);
1000 // Constructors, destructors
1003 static int _Init_Func(void)
1005 D(bug("%s()\n", __FUNCTION__
));
1007 memset(&threads
, 0, sizeof(threads
));
1008 InitSemaphore(&thread_sem
);
1013 static void _Exit_Func(void)
1019 D(bug("%s()\n", __FUNCTION__
));
1021 // wait for the threads?
1023 for (i
= 0; i
< PTHREAD_THREADS_MAX
; i
++)
1024 pthread_join(i
, NULL
);
1029 ADD2INIT(_Init_Func
, 0);
1030 ADD2EXIT(_Exit_Func
, 0);
1032 static CONSTRUCTOR_P(_Init_Func
, 100)
1034 return !_Init_Func();
1037 static DESTRUCTOR_P(_Exit_Func
, 100)