Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / libs / thread / createthread.c
blobcecd0ff0f9020fa0f829f0d6c54d9a35ddc21beb
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;
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);
149 return;
152 /* give each thread its own C library, so it can reliably printf() etc */
153 if ((aroscbase = OpenLibrary((CONST_STRPTR) "arosc.library", 0)) == NULL) {
154 FreeVec(thread);
155 Signal(td->parent, SIGBREAKF_CTRL_C);
156 return;
159 /* setup the thread */
160 InitSemaphore(&thread->lock);
161 thread->task = task;
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);
169 /* get an id */
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);
195 REMOVE(thread);
196 ReleaseSemaphore(&ThreadBase->lock);
198 /* and clean it up */
199 DestroyCondition(thread->exit);
200 DestroyMutex(thread->exit_mutex);
201 FreeVec(td);
203 return;
206 /* mark it as done */
207 thread->task = NULL;
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);