-2009-12-07 Ivan Maidanski <ivmai@mail.ru> (with input from Andy Wingo)
+2009-12-18 Ivan Maidanski <ivmai@mail.ru> (with input from
+ Jean-Claude Beaudoin)
+
+ * include/gc.h (GC_unregister_my_thread): Fix a typo; update the
+ comment.
+ * pthread_support.c (GC_delete_thread): Allow to delete the main
+ thread (don't call GC_INTERNAL_FREE for it); update the comment.
+ * win32_threads.c (GC_delete_thread): Ditto.
+ * pthread_support.c (GC_unregister_my_thread): Add an assertion
+ for FINISHED flag is unset.
+ * tests/test.c (check_heap_stats): Test the main thread
+ unregistering (only if THREADS).
+ * win32_threads.c (GC_register_my_thread_inner): Set flags to
+ DETACHED (only if GC_PTHREADS).
+ * win32_threads.c (GC_unregister_my_thread): Add FIXME (for
+ GC_wait_for_gc_completion).
+ * win32_threads.c (GC_pthread_start_inner): Clear flags detached
+ state if needed; set pthread_id and flags while holding the lock.
+
+2009-12-17 Ivan Maidanski <ivmai@mail.ru> (with input from Andy Wingo)
* include/private/gc_priv.h (SIG_SUSPEND): Don't define for
OpenBSD and Darwin.
-2009-12-07 Ivan Maidanski <ivmai@mail.ru>
+2009-12-17 Ivan Maidanski <ivmai@mail.ru>
* include/gc.h: Recognize _M_X64 (as an alias for _AMD64_).
/* libraries. */
GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *);
-/* Unregister the current thread. Only an explicity registered thread */
+/* Unregister the current thread. Only an explicitly registered thread */
/* (i.e. for which GC_register_my_thread() returns GC_SUCCESS) is */
-/* allowed (and required) to call this function. The thread may no */
-/* longer allocate garbage collected memory or manipulate pointers to */
-/* the garbage collected heap after making this call. */
-/* Specifically, if it wants to return or otherwise communicate a */
-/* pointer to the garbage-collected heap to another thread, it must */
+/* allowed (and required) to call this function. (As a special */
+/* exception, it is also allowed to once unregister the main thread.) */
+/* The thread may no longer allocate garbage collected memory or */
+/* manipulate pointers to the garbage collected heap after making this */
+/* call. Specifically, if it wants to return or otherwise communicate */
+/* a pointer to the garbage-collected heap to another thread, it must */
/* do this before calling GC_unregister_my_thread, most probably */
/* by saving it in a global data structure. Must not be called inside */
/* a GC callback function (except for GC_call_with_stack_base() one). */
/* Delete a thread from GC_threads. We assume it is there. */
/* (The code intentionally traps if it wasn't.) */
+/* It is safe to delete the main thread. */
STATIC void GC_delete_thread(pthread_t id)
{
int hv = NUMERIC_THREAD_ID(id) % THREAD_TABLE_SZ;
} else {
prev -> next = p -> next;
}
-# ifdef GC_DARWIN_THREADS
+ if (p != &first_thread) {
+# ifdef GC_DARWIN_THREADS
mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread);
-# endif
- GC_INTERNAL_FREE(p);
+# endif
+ GC_INTERNAL_FREE(p);
+ }
}
/* If a thread has been joined, but we have not yet */
/* complete before we remove this thread. */
GC_wait_for_gc_completion(FALSE);
me = GC_lookup_thread(pthread_self());
+ GC_ASSERT(!(me -> flags & FINISHED));
# if defined(THREAD_LOCAL_ALLOC)
GC_destroy_thread_local(&(me->tlfs));
# endif
(void)GC_printf("Unexpected heap growth - collector may be broken\n");
FAIL;
}
+# ifdef THREADS
+ GC_unregister_my_thread(); /* just to check it works (for main) */
+# endif
(void)GC_printf("Collector appears to work\n");
}
# ifdef GC_PTHREADS
/* me can be NULL -> segfault */
me -> pthread_id = pthread_self();
+ me -> flags = DETACHED; /* cleared in GC_pthread_start_inner if needed */
# endif
# ifndef MSWINCE
/* GetCurrentThread() returns a pseudohandle (a const value). */
}
/* Delete a thread from GC_threads. We assume it is there. */
-/* (The code intentionally traps if it wasn't.) */
-/* Assumes we hold the allocation lock unless */
-/* GC_win32_dll_threads is set. */
-/* If GC_win32_dll_threads is set it should be called from the */
-/* thread being deleted. */
+/* (The code intentionally traps if it wasn't.) Assumes we */
+/* hold the allocation lock unless GC_win32_dll_threads is set. */
+/* If GC_win32_dll_threads is set then it should be called from */
+/* the thread being deleted. It is also safe to delete the */
+/* main thread (unless GC_win32_dll_threads). */
STATIC void GC_delete_thread(DWORD id)
{
if (GC_win32_dll_threads) {
} else {
prev -> tm.next = p -> tm.next;
}
- GC_INTERNAL_FREE(p);
+ if (p != &first_thread) {
+ GC_INTERNAL_FREE(p);
+ }
}
}
GC_API int GC_CALL GC_unregister_my_thread(void)
{
DWORD t = GetCurrentThreadId();
-
+ /* FIXME: is GC_wait_for_gc_completion(FALSE) needed here? */
if (GC_win32_dll_threads) {
# if defined(THREAD_LOCAL_ALLOC)
/* Can't happen: see GC_use_DllMain(). */
/* we don't need to hold the allocation lock during pthread_create. */
me = GC_register_my_thread_inner(sb, thread_id);
SET_PTHREAD_MAP_CACHE(pthread_id, thread_id);
+ me -> pthread_id = pthread_id;
+ if (!si->detached) me -> flags &= ~DETACHED;
UNLOCK();
start = si -> start_routine;
start_arg = si -> arg;
- if (si-> detached) me -> flags |= DETACHED;
- me -> pthread_id = pthread_id;
GC_free(si); /* was allocated uncollectable */