2 * threads-pthread.c: System-specific thread support
5 * Dick Porter (dick@ximian.com)
7 * (C) 2001 Ximian, Inc.
18 #include <mono/metadata/object.h>
19 #include <mono/metadata/threads.h>
22 * Implementation of timed thread joining from the P1003.1d/D14 (July 1999)
23 * draft spec, figure B-6.
28 pthread_mutex_t join_mutex;
29 pthread_cond_t exit_cond;
30 void *(*start_routine)(void *arg);
36 static pthread_key_t timed_thread_key;
37 static pthread_once_t timed_thread_once = PTHREAD_ONCE_INIT;
39 static GHashTable *threads=NULL;
40 static MonoObject *main_thread;
42 static void timed_thread_init()
44 pthread_key_create(&timed_thread_key, NULL);
47 static void timed_thread_exit(void *status)
52 if((specific = pthread_getspecific(timed_thread_key)) == NULL) {
53 /* Handle cases which won't happen with correct usage.
58 thread=(ThreadInfo *)specific;
60 pthread_mutex_lock(&thread->join_mutex);
62 /* Tell a joiner that we're exiting.
64 thread->status = status;
65 thread->exiting = TRUE;
67 pthread_cond_signal(&thread->exit_cond);
68 pthread_mutex_unlock(&thread->join_mutex);
70 /* Call pthread_exit() to call destructors and really exit the
76 /* Routine to establish thread specific data value and run the actual
77 * thread start routine which was supplied to timed_thread_create()
79 static void *timed_thread_start_routine(void *args)
81 ThreadInfo *thread = (ThreadInfo *)args;
83 pthread_once(&timed_thread_once, timed_thread_init);
84 pthread_setspecific(timed_thread_key, (void *)thread);
85 timed_thread_exit((thread->start_routine)(thread->arg));
87 /* pthread_create routine has to return something to keep gcc
93 /* Allocate a thread which can be used with timed_thread_join().
95 static int timed_thread_create(ThreadInfo **threadp,
96 const pthread_attr_t *attr,
97 void *(*start_routine)(void *), void *arg)
102 thread=(ThreadInfo *)g_new0(ThreadInfo, 1);
103 pthread_mutex_init(&thread->join_mutex, NULL);
104 pthread_cond_init(&thread->exit_cond, NULL);
105 thread->start_routine = start_routine;
107 thread->status = NULL;
108 thread->exiting = FALSE;
110 if((result = pthread_create(&thread->id, attr,
111 timed_thread_start_routine,
112 (void *)thread)) != 0) {
117 pthread_detach(thread->id);
122 static int timed_thread_join(ThreadInfo *thread, struct timespec *timeout,
127 pthread_mutex_lock(&thread->join_mutex);
130 /* Wait until the thread announces that it's exiting, or until
133 while(result == 0 && !thread->exiting) {
134 if(timeout == NULL) {
135 result = pthread_cond_wait(&thread->exit_cond,
136 &thread->join_mutex);
138 result = pthread_cond_timedwait(&thread->exit_cond,
144 pthread_mutex_unlock(&thread->join_mutex);
145 if(result == 0 && thread->exiting) {
147 *status = thread->status;
153 pthread_t ves_icall_System_Threading_Thread_Start_internal(MonoObject *this,
156 MonoClassField *field;
157 void *(*start_func)(void *);
162 g_message("Trying to start a new thread: this (%p) start (%p)",
166 field=mono_class_get_field_from_name(mono_defaults.delegate_class, "method_ptr");
167 start_func= *(gpointer *)(((char *)start) + field->offset);
169 if(start_func==NULL) {
170 g_warning("Can't locate start method!");
171 /* Not sure if 0 can't be a valid pthread_t. Calling
172 * pthread_self() on the main thread seems to always
177 ret=timed_thread_create(&thread, NULL, start_func, NULL);
179 g_warning("pthread_create error: %s", strerror(ret));
184 g_message("Started thread ID %ld", thread->id);
187 /* Store tid for cleanup later */
189 threads=g_hash_table_new(g_int_hash, g_int_equal);
194 /* FIXME: need some locking around here */
195 g_hash_table_insert(threads, &thread->id, thread);
201 gint32 ves_icall_System_Threading_Thread_Sleep_internal(gint32 ms)
203 struct timespec req, rem;
208 g_message("Sleeping for %d ms", ms);
213 req.tv_sec=divvy.quot;
214 req.tv_nsec=divvy.rem*1000;
216 ret=nanosleep(&req, &rem);
218 /* Sleep interrupted with rem time remaining */
219 gint32 rems=rem.tv_sec*1000 + rem.tv_nsec/1000;
222 g_message("Returning %dms early", rems);
234 void ves_icall_System_Threading_Thread_Schedule_internal(void)
236 /* Give up the timeslice. pthread_yield() is the standard
237 * function but glibc seems to insist it's a GNU
238 * extension. However, all it does at the moment is
239 * sched_yield() anyway, and sched_yield() is a POSIX standard
246 MonoObject *ves_icall_System_Threading_Thread_CurrentThread_internal(void)
249 ThreadInfo *thread_info;
251 /* Find the current thread id */
254 /* Look it up in the threads hash */
256 thread_info=g_hash_table_lookup(threads, &tid);
258 /* No threads running yet! */
262 /* Return the object associated with it */
263 if(thread_info==NULL) {
264 /* If we can't find our own thread ID, assume it's the
269 return(thread_info->object);
273 static void delete_thread(ThreadInfo *thread)
275 g_assert(threads!=NULL);
276 g_assert(thread!=NULL);
278 g_hash_table_remove(threads, &thread->id);
282 gboolean ves_icall_System_Threading_Thread_Join_internal(MonoObject *this,
283 int ms, pthread_t tid)
290 g_message("Joining with thread %p id %ld, waiting for %dms", this,
296 /* .net doesnt spot this and proceeds to
297 * deadlock. This will have to be commented out if we
298 * want to be bug-compatible :-(
300 g_warning("Can't join my own thread!");
305 thread=g_hash_table_lookup(threads, &tid);
307 /* No threads running yet! */
312 g_warning("Can't find thread id %ld", tid);
317 /* block until thread exits */
318 ret=timed_thread_join(thread, NULL, NULL);
321 delete_thread(thread);
324 g_warning("Join error: %s", strerror(ret));
328 /* timeout in ms milliseconds */
329 struct timespec timeout;
336 timeout.tv_sec=now+divvy.quot;
337 timeout.tv_nsec=divvy.rem*1000;
339 ret=timed_thread_join(thread, &timeout, NULL);
341 delete_thread(thread);
345 g_warning("Timed join error: %s",
353 static void join_all_threads(gpointer key, gpointer value, gpointer user)
355 ThreadInfo *thread_info=(ThreadInfo *)value;
358 g_message("[%ld]", thread_info->id);
360 timed_thread_join(thread_info, NULL, NULL);
363 static gboolean free_all_threadinfo(gpointer key, gpointer value, gpointer user)
370 void mono_thread_init(void)
372 MonoClass *thread_class;
374 /* Build a System.Threading.Thread object instance to return
375 * for the main line's Thread.CurrentThread property.
377 thread_class=mono_class_from_name(mono_defaults.corlib, "System.Threading", "Thread");
379 /* I wonder what happens if someone tries to destroy this
380 * object? In theory, I guess the whole program should act as
381 * though exit() were called :-)
383 main_thread=mono_new_object(thread_class);
387 void mono_thread_cleanup(void)
389 /* join each thread that's still running */
391 g_message("Joining each running thread...");
396 g_message("No threads");
401 g_hash_table_foreach(threads, join_all_threads, NULL);
403 g_hash_table_foreach_remove(threads, free_all_threadinfo, NULL);
404 g_hash_table_destroy(threads);