+2007-05-23 Hans Boehm <Hans.Boehm@hp.com> (Really mostly Romano Paolo Tenca)
+
+ * gc_dlopen.c, thread_local_alloc.c, threadlibs.c, win32_threads.c,
+ tests/test.c: Accomodate GC_WIN32_PTHREADS.
+ * include/gc.h: Don't include windows.h for GC_WIN32_PTHREADS.
+ * include/gc_config_macros.h: Define both PTHREADS and
+ GC_WIN32_THREADS.
+ * include/private/gc_locks.h: Nonstandard definitions of
+ NUMERIC_THREAD_ID for GC_WIN32_PTHREADS.
+ * doc/README.win32, Makefile.direct: Include documentation
+ for GC_WIN32_PTHREADS.
+ * Makefile.direct: Remove some anachronisms in the documentation.
+
2007-05-23 Hans Boehm <Hans.Boehm@hp.com>
* Makefile.am: Move includes to bottom. Add better library
# This causes the collector to assume that all inaccessible
# objects should have been explicitly deallocated, and reports exceptions.
# Finalization and the test program are not usable in this mode.
-# -DGC_SOLARIS_THREADS enables support for Solaris (thr_) threads.
+#
+# IMPORTANT: Any of the _THREADS options must normally also be defined in
+# the client before including gc.h. This redefines thread primitives to
+# invoke the GC_ versions instead. Alternatively, linker-based symbol
+# interception can be used on a few platforms.
+# -DGC_THREADS should set the appropriate one of the below macros,
+# except -DGC_WIN32_PTHREADS, which must be set explicitly.
+# -DGC_SOLARIS_PTHREADS enables support for Solaris pthreads.
# (Clients should also define GC_SOLARIS_THREADS and then include
# gc.h before performing thr_ or dl* or GC_ operations.)
# Must also define -D_REENTRANT.
-# -DGC_SOLARIS_PTHREADS enables support for Solaris pthreads.
-# (Internally this define GC_SOLARIS_THREADS as well.)
# -DGC_IRIX_THREADS enables support for Irix pthreads. See README.irix.
# -DGC_HPUX_THREADS enables support for HP/UX 11 pthreads.
# Also requires -D_REENTRANT or -D_POSIX_C_SOURCE=199506L. See README.hp.
-# -DGC_LINUX_THREADS enables support for Xavier Leroy's Linux threads.
-# see README.linux. -D_REENTRANT may also be required.
+# -DGC_LINUX_THREADS enables support for Xavier Leroy's Linux threads
+# or NPTL threads. See README.linux. -D_REENTRANT may also be required.
# -DGC_OSF1_THREADS enables support for Tru64 pthreads.
# -DGC_FREEBSD_THREADS enables support for FreeBSD pthreads.
# Appeared to run into some underlying thread problems.
# -DGC_DARWIN_THREADS enables support for Mac OS X pthreads.
# -DGC_AIX_THREADS enables support for IBM AIX threads.
# -DGC_DGUX386_THREADS enables support for DB/UX on I386 threads.
-# See README.DGUX386.
+# See README.DGUX386. (Probably has not been tested recently.)
# -DGC_WIN32_THREADS enables support for win32 threads. That makes sense
# for this Makefile only under Cygwin.
-# -DGC_THREADS should set the appropriate one of the above macros.
-# It assumes pthreads for Solaris.
+# -DGC_WIN32_PTHREADS enables support for Ming32 pthreads. This cannot be
+# enabled automatically by GC_THREADS, which would assume Win32 native
+# threads.
+# -DPTW32_STATIC_LIB causes the static version of the Mingw pthreads library
+# to be used. Requires -DGC_WIN32_PTHREADS.
+#
# -DALL_INTERIOR_POINTERS allows all pointers to the interior
# of objects to be recognized. (See gc_priv.h for consequences.)
# Alternatively, GC_all_interior_pointers can be set at process
case, including gc.h will redefine CreateThread, _beginthreadex,
_endthreadex, and ExitThread to call the GC_ versions instead.
+Note that, as usual, GC_CreateThread tends to introduce resource leaks
+that are avoided by GC_beginthreadex. There is currently no equivalent of
+_beginthread, and it should not be used.
+
GC_INIT should be called from the main executable before other GC calls.
We strongly advise against using the TerminateThread() win32 API call,
crash in the GC, since it makes it impossible for the collector to
correctly track threads.
+To build the collector for Mingw32 Pthreads, use Makefile.direct and
+explicitly set GC_WIN32_PTHREADS. Use -DPTW32_STATIC_LIB for the static
+threads library. Note that the DEBUG_WIN32_PTHREADS support in
+win32_threads.c is currently broken and looking for someone to debug it.
+(This information and the port came from Romano Paolo Tenca).
+
#include "private/gc_priv.h"
-# if (defined(GC_PTHREADS) && !defined(GC_DARWIN_THREADS)) \
+# if (defined(GC_PTHREADS) && !defined(GC_DARWIN_THREADS)) && !defined(GC_WIN32_PTHREADS)\
|| defined(GC_SOLARIS_THREADS)
# if defined(dlopen) && !defined(GC_USE_LD_WRAP)
(int (*callback)(const char *, void *, size_t));
-#if defined(GC_WIN32_THREADS) && !defined(__CYGWIN32__) && !defined(__CYGWIN__)
+#if defined(GC_WIN32_THREADS) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) \
+ && !defined(GC_PTHREADS)
# include <windows.h>
/*
# define GC_PTHREADS
# endif
+#if defined(GC_WIN32_PTHREADS)
+# define GC_WIN32_THREADS
+# define GC_PTHREADS
+#endif
+
#if defined(GC_THREADS) && !defined(GC_PTHREADS)
# if defined(__linux__)
# define GC_LINUX_THREADS
/* the mapping to integers does not need to result in different */
/* integers for each thread, though that should be true as much */
/* as possible. */
-# if 1 /* Refine to exclude platforms on which pthread_t is struct */
-# define NUMERIC_THREAD_ID(id) ((unsigned long)(id))
-# define THREAD_EQUAL(id1, id2) ((id1) == (id2))
-# define NUMERIC_THREAD_ID_UNIQUE
+ /* Refine to exclude platforms on which pthread_t is struct */
+# if !defined(GC_WIN32_PTHREADS)
+# define NUMERIC_THREAD_ID(id) ((unsigned long)(id))
+# define THREAD_EQUAL(id1, id2) ((id1) == (id2))
+# define NUMERIC_THREAD_ID_UNIQUE
# else
- /* Generic definitions that always work, but will result in */
- /* poor performance and weak assertion checking. */
-# define NUMERIC_THREAD_ID(id) 1l
-# define THREAD_EQUAL(id1, id2) pthread_equal(id1, id2)
-# undef NUMERIC_THREAD_ID_UNIQUE
+# if defined(GC_WIN32_PTHREADS)
+# define NUMERIC_THREAD_ID(id) ((unsigned long)(id.p))
+# define THREAD_EQUAL(id1, id2) pthread_equal(id1, id2)
+# undef NUMERIC_THREAD_ID_UNIQUE
+# else
+ /* Generic definitions that always work, but will result in */
+ /* poor performance and weak assertion checking. */
+# define NUMERIC_THREAD_ID(id) 1l
+# define THREAD_EQUAL(id1, id2) pthread_equal(id1, id2)
+# undef NUMERIC_THREAD_ID_UNIQUE
+# endif
# endif
# define NO_THREAD (-1l)
/* != NUMERIC_THREAD_ID(pthread_self()) for any thread */
*/
#ifdef THREADS
-# if defined(GC_WIN32_THREADS) && !defined(CYGWIN32)
+# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
DWORD __stdcall tiny_reverse_test(void * arg)
# else
void * tiny_reverse_test(void * arg)
}
# endif
-#if defined(GC_WIN32_THREADS) && !defined(CYGWIN32)
+#if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS)
DWORD __stdcall thr_run_one_test(void *arg)
{
pthread_t th2;
pthread_attr_t attr;
int code;
-
# ifdef GC_IRIX_THREADS
/* Force a larger stack to be preallocated */
/* Since the initial cant always grow later. */
(void)GC_printf("pthread_default_stacksize_np failed.\n");
}
# endif /* GC_HPUX_THREADS */
+# ifdef PTW32_STATIC_LIB
+ pthread_win32_process_attach_np ();
+ pthread_win32_thread_attach_np ();
+# endif
GC_INIT();
pthread_attr_init(&attr);
(void)fflush(stdout);
pthread_attr_destroy(&attr);
GC_printf("Completed %d collections\n", GC_gc_no);
+# ifdef PTW32_STATIC_LIB
+ pthread_win32_thread_detach_np ();
+ pthread_win32_process_detach_np ();
+# endif
return(0);
}
#endif /* GC_PTHREADS */
# endif
}
-#if defined(GC_ASSERTIONS) && defined(GC_PTHREADS) && !defined(CYGWIN32)
+#if defined(GC_ASSERTIONS) && defined(GC_PTHREADS) && !defined(CYGWIN32) \
+ && !defined(GC_WIN32_PTHREADS)
# include <pthread.h>
extern char * GC_lookup_thread(pthread_t id);
#endif
# if defined(GC_WIN32_THREADS) && defined(CYGWIN32)
printf("-lpthread\n");
# endif
+# if defined(GC_WIN32_PTHREADS)
+# ifdef PTW32_STATIC_LIB
+ /* assume suffix s for static version of the win32 pthread library */
+ printf("-lpthreadGC2s -lws2_32\n");
+# else
+ printf("-lpthreadGC2\n");
+# endif
+# endif
# if defined(GC_OSF1_THREADS)
printf("-pthread -lrt"); /* DOB: must be -pthread, not -lpthread */
# endif
unsigned long GC_lock_holder = NO_THREAD;
#endif
-#ifdef CYGWIN32
+#ifdef GC_PTHREADS
# include <errno.h>
/* GC_DLL should not normally be defined, especially since we often do turn on */
# undef dlopen
# define DEBUG_CYGWIN_THREADS 0
+# define DEBUG_WIN32_PTHREADS 0
void * GC_pthread_start(void * arg);
void GC_thread_exit_proc(void *arg);
/* !in_use ==> stack_base == 0 */
GC_bool suspended;
-# ifdef CYGWIN32
+# ifdef GC_PTHREADS
void *status; /* hold exit value until join in case it's a pointer */
pthread_t pthread_id;
short flags; /* Protected by GC lock. */
GC_ASSERT(!GC_win32_dll_threads);
result = (struct GC_Thread_Rep *)
GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL);
-# ifdef CYGWIN32
+# ifdef GC_PTHREADS
+ /* result can be NULL -> segfault */
GC_ASSERT(result -> flags == 0);
# endif
}
/* result -> id = id; Done by caller. */
result -> next = GC_threads[hv];
GC_threads[hv] = result;
-# ifdef CYGWIN32
+# ifdef GC_PTHREADS
GC_ASSERT(result -> flags == 0 /* && result -> thread_blocked == 0 */);
# endif
return(result);
GC_ASSERT(I_HOLD_LOCK());
me = GC_new_thread(thread_id);
}
-# ifdef CYGWIN32
- me -> pthread_id = pthread_self();
+# ifdef GC_PTHREADS
+ /* me can be NULL -> segfault */
+ me -> pthread_id = pthread_self();
# endif
+
if (!DuplicateHandle(GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),
# ifdef CYGWIN32
gc_id -> pthread_id = 0;
# endif /* CYGWIN32 */
+# ifdef GC_WIN32_PTHREADS
+ gc_id -> pthread_id.p = NULL;
+# endif /* GC_WIN32_PTHREADS */
AO_store_release(&(gc_id->in_use), FALSE);
} else {
/* Cast away volatile qualifier, since we have lock. */
}
-#ifdef CYGWIN32
+#ifdef GC_PTHREADS
/* A quick-and-dirty cache of the mapping between pthread_t */
/* and win32 thread id. */
for (i = 0;
i <= my_max &&
(!AO_load_acquire(&(dll_thread_table[i].in_use))
- || dll_thread_table[i].pthread_id != id);
+ || THREAD_EQUAL(dll_thread_table[i].pthread_id, id));
/* Must still be in_use, since nobody else can store our thread_id. */
i++);
if (i > my_max) return 0;
}
}
-#endif /* CYGWIN32 */
+#endif /* GC_PTHREADS */
void GC_push_thread_structures(void)
{
/* Unlike the other threads implementations, the thread table here */
/* contains no pointers to the collectable heap. Thus we have */
/* no private structures we need to preserve. */
-# ifdef CYGWIN32
+# ifdef GC_PTHREADS
{ int i; /* pthreads may keep a pointer in the thread exit value */
LONG my_max = GC_get_max_thread_index();
if (GetExitCodeThread(t -> handle, &exitCode) &&
exitCode != STILL_ACTIVE) {
t -> stack_base = 0; /* prevent stack from being pushed */
-# ifndef CYGWIN32
+# ifndef GC_PTHREADS
/* this breaks pthread_join on Cygwin, which is guaranteed to */
/* only see user pthreads */
AO_store(&(t -> in_use), FALSE);
}
/* Defined in misc.c */
-#ifndef CYGWIN32
+#ifndef GC_PTHREADS
extern CRITICAL_SECTION GC_write_cs;
#endif
GC_ASSERT(I_HOLD_LOCK());
GC_please_stop = TRUE;
-# ifndef CYGWIN32
+# ifndef GC_PTHREADS
EnterCriticalSection(&GC_write_cs);
# endif
if (GC_win32_dll_threads) {
}
}
}
-# ifndef CYGWIN32
+# ifndef GC_PTHREADS
LeaveCriticalSection(&GC_write_cs);
# endif
}
# if DEBUG_CYGWIN_THREADS
GC_printf("Pushing thread from %p to %p for %d from %d\n",
sp, thread -> stack_base, thread -> id, me);
+# endif
+# if DEBUG_WIN32_PTHREADS
+ GC_printf("Pushing thread from %p to %p for 0x%x from 0x%x\n",
+ sp, thread -> stack_base, thread -> id, me);
# endif
GC_push_all_stack(sp, thread->stack_base);
} else {
if (*lo < start) *lo = start;
}
-#if !defined(CYGWIN32)
+#ifndef GC_PTHREADS
/* We have no DllMain to take care of new threads. Thus we */
/* must properly intercept thread creation. */
_endthreadex(retval);
}
-#endif /* !CYGWIN32 */
+#endif /* !GC_PTHREADS */
#ifdef MSWINCE
GC_register_my_thread(&sb);
}
-#ifdef CYGWIN32
+#ifdef GC_PTHREADS
struct start_info {
void *(*start_routine)(void *);
GC_printf("thread 0x%x(0x%x) is joining thread 0x%x.\n",
(int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);
# endif
+# if DEBUG_WIN32_PTHREADS
+ GC_printf("thread 0x%x(0x%x) is joining thread 0x%x.\n",
+ (pthread_self()).p, GetCurrentThreadId(), pthread_id.p);
+# endif
if (!parallel_initialized) GC_init_parallel();
/* Thread being joined might not have registered itself yet. */
GC_printf("thread 0x%x(0x%x) completed join with thread 0x%x.\n",
(int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);
# endif
+# if DEBUG_WIN32_PTHREADS
+ GC_printf("thread 0x%x(0x%x) completed join with thread 0x%x.\n",
+ (pthread_self()).p, GetCurrentThreadId(), pthread_id.p);
+# endif
return result;
}
# if DEBUG_CYGWIN_THREADS
GC_printf("About to create a thread from 0x%x(0x%x)\n",
(int)pthread_self(), GetCurrentThreadId);
+# endif
+# if DEBUG_WIN32_PTHREADS
+ GC_printf("About to create a thread from 0x%x(0x%x)\n",
+ (pthread_self()).p, GetCurrentThreadId());
# endif
GC_need_to_lock = TRUE;
result = pthread_create(new_thread, attr, GC_pthread_start, si);
GC_printf("thread 0x%x(0x%x) starting...\n",(int)pthread_id,
thread_id);
# endif
+# if DEBUG_WIN32_PTHREADS
+ GC_printf("thread 0x%x(0x%x) starting...\n",(int) pthread_id.p,
+ thread_id);
+# endif
GC_ASSERT(!GC_win32_dll_threads);
/* If a GC occurs before the thread is registered, that GC will */
GC_printf("thread 0x%x(0x%x) returned from start routine.\n",
(int)pthread_self(),GetCurrentThreadId());
# endif
+# if DEBUG_WIN32_PTHREADS
+ GC_printf("thread 0x%x(0x%x) returned from start routine.\n",
+ (int)(pthread_self()).p, GetCurrentThreadId());
+# endif
return(result);
}
GC_printf("thread 0x%x(0x%x) called pthread_exit().\n",
(int)pthread_self(),GetCurrentThreadId());
# endif
+# if DEBUG_WIN32_PTHREADS
+ GC_printf("thread 0x%x(0x%x) called pthread_exit().\n",
+ (int)(pthread_self()).p,GetCurrentThreadId());
+# endif
LOCK();
# if defined(THREAD_LOCAL_ALLOC)
UNLOCK();
}
+#ifndef GC_WIN32_PTHREADS
+/* win32 pthread does not support sigmask */
/* nothing required here... */
int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) {
if (!parallel_initialized) GC_init_parallel();
return pthread_sigmask(how, set, oset);
}
+#endif
int GC_pthread_detach(pthread_t thread)
{
return result;
}
-#else /* !CYGWIN32 */
+#else /* !GC_PTHREADS */
/*
* We avoid acquiring locks here, since this doesn't seem to be preemptable.
return TRUE;
}
#endif /* GC_DLL */
-#endif /* !CYGWIN32 */
+#endif /* !GC_PTHREADS */
# endif /* !MSWINCE */