2 * thread.library - threading and synchronisation primitives
4 * Copyright © 2007 Robert Norris
6 * This program is free software; you can redistribute it and/or modify it
7 * under the same terms as AROS itself.
10 #include "thread_intern.h"
12 #include <exec/libraries.h>
13 #include <exec/tasks.h>
14 #include <exec/memory.h>
16 #include <dos/dosextens.h>
17 #include <proto/exec.h>
18 #include <proto/dos.h>
19 #include <proto/thread.h>
22 struct trampoline_data
{
23 struct ThreadBase
*ThreadBase
;
25 ThreadEntryFunction entry
;
30 static void entry_trampoline(void);
32 /*****************************************************************************
35 AROS_LH2(uint32_t, CreateThread
,
38 AROS_LHA(ThreadEntryFunction
, entry
, A0
),
39 AROS_LHA(void *, data
, A1
),
42 struct ThreadBase
*, ThreadBase
, 5, Thread
)
48 entry - pointer to a function to run in the new thread
49 data - pointer to pass in the first in the first argument to function
53 Numeric thread ID, or 0 if the thread could not be started.
58 uint32_t id = CreateThread(entry, data);
60 printf("thread creation failed\n");
62 printf("thread %d created\n", id);
67 CurrentThread(), DetachThread(), WaitThread(), WaitAllThreads()
70 Each thread gets its own instance of arosc.library, so it can safely
71 call functions like printf() without conflicting with other threads.
72 Similarly, each thread gets its own standard I/O streams, though they
73 start attached to the same place as the task that created the thread.
75 *****************************************************************************/
79 struct trampoline_data
*td
;
83 assert(entry
!= NULL
);
85 /* allocate some space for the thread and stuff the trampoline needs */
86 if ((td
= AllocVec(sizeof(struct trampoline_data
), MEMF_PUBLIC
| MEMF_CLEAR
)) == NULL
)
89 td
->ThreadBase
= ThreadBase
;
91 /* let the thread find us */
92 td
->parent
= FindTask(NULL
);
94 /* entry point info for the trampoline */
98 /* create the new process and hand control to the trampoline. it will wait
99 * for us to finish setting up because we have the thread lock */
100 task
= (struct Task
*) CreateNewProcTags(
101 NP_Name
, (IPTR
) "thread.library thread",
102 NP_Entry
, (IPTR
) entry_trampoline
,
103 NP_UserData
, (IPTR
) td
,
104 NP_Input
, (IPTR
) OpenFromLock(DupLockFromFH(Input())),
105 NP_Output
, (IPTR
) OpenFromLock(DupLockFromFH(Output())),
106 NP_Error
, (IPTR
) OpenFromLock(DupLockFromFH(Error())),
109 /* failure, shut it down */
111 FreeMem(td
, sizeof(struct trampoline_data
));
115 /* signal the task to kick it off */
116 Signal(task
, SIGBREAKF_CTRL_C
);
118 /* wait for them to tell us that they're ready */
119 Wait(SIGBREAKF_CTRL_C
);
121 /* get the new id of the thread that they passed back */
124 /* free the trampoline data */
133 static void entry_trampoline(void) {
134 struct Task
*task
= FindTask(NULL
);
135 struct trampoline_data
*td
= task
->tc_UserData
;
136 struct ThreadBase
*ThreadBase
= td
->ThreadBase
;
137 struct Library
*aroscbase
;
138 struct _Thread
*thread
;
140 /* wait for the parent to let us go */
141 Wait(SIGBREAKF_CTRL_C
);
143 /* allocate space for the thread */
144 if ((thread
= AllocVec(sizeof(struct _Thread
), MEMF_PUBLIC
| MEMF_CLEAR
)) == NULL
) {
146 /* in case of failure, set a negative id and tell the parent so they
147 * can inform the caller */
148 Signal(td
->parent
, SIGBREAKF_CTRL_C
);
152 /* give each thread its own C library, so it can reliably printf() etc */
153 if ((aroscbase
= OpenLibrary((CONST_STRPTR
) "arosc.library", 0)) == NULL
) {
155 Signal(td
->parent
, SIGBREAKF_CTRL_C
);
159 /* setup the thread */
160 InitSemaphore(&thread
->lock
);
163 /* make a condition so other threads can wait for us to exit */
164 thread
->exit
= CreateCondition();
165 thread
->exit_mutex
= CreateMutex();
167 ObtainSemaphore(&ThreadBase
->lock
);
170 td
->id
= thread
->id
= ThreadBase
->nextid
++;
172 /* add the thread to the list */
173 ADDTAIL(&ThreadBase
->threads
, thread
);
175 ReleaseSemaphore(&ThreadBase
->lock
);
177 /* inform the parent that we're ready to go */
178 Signal(td
->parent
, SIGBREAKF_CTRL_C
);
180 if (setjmp(thread
->jmp
) == 0) {
181 /* call the actual thread entry */
182 thread
->result
= AROS_UFC1(void *, td
->entry
,
183 AROS_UFCA(void *, td
->data
, A0
));
186 CloseLibrary(aroscbase
);
188 /* its over, update its state */
189 ObtainSemaphore(&thread
->lock
);
191 /* if its detached, then we close it down right here and now */
192 if (thread
->detached
) {
193 /* remove it from the thread list */
194 ObtainSemaphore(&ThreadBase
->lock
);
196 ReleaseSemaphore(&ThreadBase
->lock
);
198 /* and clean it up */
199 DestroyCondition(thread
->exit
);
200 DestroyMutex(thread
->exit_mutex
);
206 /* mark it as done */
209 ReleaseSemaphore(&thread
->lock
);
211 /* tell anyone that cares. we'll be cleaned up in WaitThread() */
212 LockMutex(thread
->exit_mutex
);
213 BroadcastCondition(thread
->exit
);
214 UnlockMutex(thread
->exit_mutex
);