added concrete implementations of putc(), getc(), getchar() and gets()
[tangerine.git] / workbench / libs / thread / createthread.c
blob2aa35d4697b936038d7cbe2f8332750c069fb899
1 /*
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.
8 */
10 #include "thread_intern.h"
12 #include <exec/libraries.h>
13 #include <exec/tasks.h>
14 #include <exec/memory.h>
15 #include <dos/dos.h>
16 #include <dos/dosextens.h>
17 #include <proto/exec.h>
18 #include <proto/dos.h>
19 #include <proto/thread.h>
20 #include <assert.h>
22 struct trampoline_data {
23 struct ThreadBase *ThreadBase;
24 struct Task *parent;
25 ThreadEntryFunction entry;
26 void *data;
27 uint32_t id;
30 static void entry_trampoline(void);
32 /*****************************************************************************
34 NAME */
35 AROS_LH2(uint32_t, CreateThread,
37 /* SYNOPSIS */
38 AROS_LHA(ThreadEntryFunction, entry, A0),
39 AROS_LHA(void *, data, A1),
41 /* LOCATION */
42 struct ThreadBase *, ThreadBase, 5, Thread)
44 /* FUNCTION
45 Creates a new thread.
47 INPUTS
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
50 pointed to by entry
52 RESULT
53 Numeric thread ID, or 0 if the thread could not be started.
55 NOTES
57 EXAMPLE
58 uint32_t id = CreateThread(entry, data);
59 if (id < 0)
60 printf("thread creation failed\n");
61 else
62 printf("thread %d created\n", id);
64 BUGS
66 SEE ALSO
67 CurrentThread(), DetachThread(), WaitThread(), WaitAllThreads()
69 INTERNALS
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 *****************************************************************************/
77 AROS_LIBFUNC_INIT
79 struct trampoline_data *td;
80 struct Task *task;
81 uint32_t id;
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)
87 return 0;
89 td->ThreadBase = ThreadBase;
91 /* let the thread find us */
92 td->parent = FindTask(NULL);
94 /* entry point info for the trampoline */
95 td->entry = entry;
96 td->data = data;
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())),
107 TAG_DONE);
109 /* failure, shut it down */
110 if (task == NULL) {
111 FreeMem(td, sizeof(struct trampoline_data));
112 return 0;
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 */
122 id = td->id;
124 /* free the trampoline data */
125 FreeVec(td);
127 /* done */
128 return id;
130 AROS_LIBFUNC_EXIT
131 } /* CreateThread */
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;
139 void *result;
141 /* wait for the parent to let us go */
142 Wait(SIGBREAKF_CTRL_C);
144 /* allocate space for the thread */
145 if ((thread = AllocVec(sizeof(struct _Thread), MEMF_PUBLIC | MEMF_CLEAR)) == NULL) {
147 /* in case of failure, set a negative id and tell the parent so they
148 * can inform the caller */
149 Signal(td->parent, SIGBREAKF_CTRL_C);
150 return;
153 /* give each thread its own C library, so it can reliably printf() etc */
154 if ((aroscbase = OpenLibrary("arosc.library", 0)) == NULL) {
155 FreeVec(thread);
156 Signal(td->parent, SIGBREAKF_CTRL_C);
157 return;
160 /* setup the thread */
161 InitSemaphore(&thread->lock);
162 thread->task = task;
164 /* make a condition so other threads can wait for us to exit */
165 thread->exit = CreateCondition();
166 thread->exit_mutex = CreateMutex();
168 ObtainSemaphore(&ThreadBase->lock);
170 /* get an id */
171 td->id = thread->id = ThreadBase->nextid++;
173 /* add the thread to the list */
174 ADDTAIL(&ThreadBase->threads, thread);
176 ReleaseSemaphore(&ThreadBase->lock);
178 /* inform the parent that we're ready to go */
179 Signal(td->parent, SIGBREAKF_CTRL_C);
181 /* call the actual thread entry */
182 result = AROS_UFC1(void *, td->entry,
183 AROS_UFCA(void *, td->data, A0));
185 CloseLibrary(aroscbase);
187 /* its over, update its state */
188 ObtainSemaphore(&thread->lock);
190 /* if its detached, then we close it down right here and now */
191 if (thread->detached) {
192 /* remove it from the thread list */
193 ObtainSemaphore(&ThreadBase->lock);
194 REMOVE(thread);
195 ReleaseSemaphore(&ThreadBase->lock);
197 /* and clean it up */
198 DestroyCondition(thread->exit);
199 DestroyMutex(thread->exit_mutex);
200 FreeVec(td);
202 return;
205 /* save the result */
206 thread->result = result;
208 /* mark it as done */
209 thread->task = NULL;
211 ReleaseSemaphore(&thread->lock);
213 /* tell anyone that cares. we'll be cleaned up in WaitThread() */
214 LockMutex(thread->exit_mutex);
215 BroadcastCondition(thread->exit);
216 UnlockMutex(thread->exit_mutex);