For historical reasons,
the collector test program "gctest" is linked as a GUI application,
-but does not open any windows. Its output appears in the file
-"gc.log". It may be started from the file manager. The hour glass
+but does not open any windows. Its output normally appears in the file
+"gctest.exe.log". It may be started from the file manager. The hour glass
cursor may appear as long as it's running. If it is started from the
command line, it will usually run in the background. Wait a few
minutes (a few seconds on a modern machine) before you check the output.
Clark for tracking this down. There's some chance this may be fixed
in 6.1alpha4, since we now separate heap sections with an unused page.)
+[Threads and incremental collection are discussed near the end, below.]
+
Microsoft Tools
---------------
For Microsoft development tools, rename NT_MAKEFILE as
GNU Tools
---------
+The collector should be buildable under Cygwin with either the
+old standard Makefile, or with the "configure;make" machinery.
+(For the latter use --enable-threads=posix for thread support.)
+
+Mingw32 builds are not regularly tested, and may or may not work.
+The following paragraph is probably obsolete:
+
For GNU-win32, use the regular makefile, possibly after uncommenting
the line "include Makefile.DLLs". The latter should be necessary only
if you want to package the collector as a DLL.
in b19. This is probably fixable with a different definition of
DATASTART and DATAEND in gcconfig.h.
-The collector should also be buildable under Cygwin with either the
-old standard Makefile, or with the "configure;make" machinery.
-(For the latter use --enable-threads=posix for thread support.)
-
Borland Tools
-------------
[Rarely tested.]
require the assembler. If you do have the assembler, I recommend
removing the -DUSE_GENERIC.
-Incremental Collection
-----------------------
-There is some support for incremental collection. This is
-currently pretty simple-minded. Pages are protected. Protection
-faults are caught by a handler installed at the bottom of the handler
-stack. This is both slow and interacts poorly with a debugger.
-Whenever possible, I recommend adding a call to
-GC_enable_incremental at the last possible moment, after most
-debugging is complete. Unlike the UNIX versions, no system
-calls are wrapped by the collector itself. It may be necessary
-to wrap ReadFile calls that use a buffer in the heap, so that the
-call does not encounter a protection fault while it's running.
-(As usual, none of this is an issue unless GC_enable_incremental
-is called.)
-
-Note that incremental collection is disabled with -DSMALL_CONFIG.
-
-Threads
--------
-
-James Clark has contributed the necessary code to support win32 threads
-with the collector in a DLL.
-Use NT_THREADS_MAKEFILE (a.k.a gc.mak) instead of NT_MAKEFILE
-to build this version. Note that this requires some files whose names
-are more than 8 + 3 characters long. Thus you should unpack the tar file
-so that long file names are preserved. To build the garbage collector
-test with VC++ from the command line, use
-
-nmake /F ".\gc.mak" CFG="gctest - Win32 Release"
-
-This requires that the subdirectory gctest\Release exist.
-The test program and DLL will reside in the Release directory.
-
-This version relies on the collector residing in a dll.
-
-This version currently supports incremental collection only if it is
-enabled before any additional threads are created.
-
-Since 6.3alpha2, threads are also better supported in static library builds
-with Microsoft tools (use NT_STATIC_THREADS_MAKEFILE) and with the GNU
-tools. In all cases,the collector must be built with GC_WIN32_THREADS
-defined, even if the Cygwin pthreads interface is used.
-(NT_STATIC_THREADS_MAKEFILE does this implicitly. Under Cygwin,
-./configure --enable-threads=posix defines GC_WIN32_THREADS.) Threads must be
-created with GC_CreateThread. This can be accomplished by
-including gc.h and then calling CreateThread, which is redefined
-by gc.h.
-
-For the statically linked versions, it is required that GC_init()
-be called before other GC calls, since there seems to be no implicit way
-to initialize the allocation lock. The easiest way to ensure this in
-portable code is to call GC_INIT() from the main executable (not
-a dynamic library) before calling any other GC_ routines.
-
-We strongly advise against using the TerminateThread() win32 API call,
-especially with the garbage collector. Any use is likely to provoke a
-crash in the GC, since it makes it impossible for the collector to
-correctly track threads.
-
Watcom compiler
---------------
Ivan Demakov (email: ivan@tgrad.nsk.su)
-Win32S
-------
-
-[The following is probably obsolete. The win32s support is still in the
-collector, but I doubt anyone cares, or has tested it recently.]
-
-The collector runs under both win32s and win32, but with different semantics.
-Under win32, all writable pages outside of the heaps and stack are
-scanned for roots. Thus the collector sees pointers in DLL data
-segments. Under win32s, only the main data segment is scanned.
-(The main data segment should always be scanned. Under some
-versions of win32s, other regions may also be scanned.)
-Thus all accessible objects should be accessible from local variables
-or variables in the main data segment. Alternatively, other data
-segments (e.g. in DLLs) may be registered with the collector by
-calling GC_init() and then GC_register_root_section(a), where
-a is the address of some variable inside the data segment. (Duplicate
-registrations are ignored, but not terribly quickly.)
-
-(There are two reasons for this. We didn't want to see many 16:16
-pointers. And the VirtualQuery call has different semantics under
-the two systems, and under different versions of win32s.)
+Incremental Collection
+----------------------
+There is some support for incremental collection. By default, the
+collector chooses between explicit page protection, anf GetWriteWatch-based
+write tracking automatically, depending on the platform.
+
+The former is slow and interacts poorly with a debugger.
+Pages are protected. Protection faults are caught by a handler
+installed at the bottom of the handler
+stack. Whenever possible, I recommend adding a call to
+GC_enable_incremental at the last possible moment, after most
+debugging is complete. No system
+calls are wrapped by the collector itself. It may be necessary
+to wrap ReadFile calls that use a buffer in the heap, so that the
+call does not encounter a protection fault while it's running.
+(As usual, none of this is an issue unless GC_enable_incremental
+is called.)
+
+Note that incremental collection is disabled with -DSMALL_CONFIG.
+
+Threads
+-------
+
+This version of the collector by default handles threads similarly
+to other platforms. James Clark's code which tracks threads attached
+to the collector DLL still exists, but requires that both
+- the collector is built in a DLL with GC_DLL defined, and
+- GC_win32_dll_threads be set to true before GC initialization.
+We generally recommend avoiding this if possible, since it seems to
+be less than 100% reliable.
+
+Use NT_THREADS_MAKEFILE (a.k.a gc.mak) instead of NT_MAKEFILE
+to build a version that supports both kinds of thread tracking.
+To build the garbage collector
+test with VC++ from the command line, use
+
+nmake /F ".\gc.mak" CFG="gctest - Win32 Release"
+
+This requires that the subdirectory gctest\Release exist.
+The test program and DLL will reside in the Release directory.
+
+This version currently supports incremental collection only if it is
+enabled before any additional threads are created.
+
+Since 6.3alpha2, threads are also better supported in static library builds
+with Microsoft tools (use NT_STATIC_THREADS_MAKEFILE) and with the GNU
+tools. In all cases,the collector must be built with GC_WIN32_THREADS
+defined, even if the Cygwin pthreads interface is used.
+(NT_STATIC_THREADS_MAKEFILE does this implicitly. Under Cygwin,
+./configure --enable-threads=posix defines GC_WIN32_THREADS.)
+
+For the normal, non-dll-based thread tracking to work properly,
+threads should be created with GC_CreateThread or GC_beginthreadex,
+and exit normally or call GC_endthreadex or GC_ExitThread. (For
+Cygwin, use standard pthread calls instead.) As in the pthread
+case, including gc.h will redefine CreateThread, _beginthreadex,
+_endthreadex, and ExitThread to call the GC_ versions instead.
+
+GC_INIT should be called from the main executable before other GC calls.
+
+We strongly advise against using the TerminateThread() win32 API call,
+especially with the garbage collector. Any use is likely to provoke a
+crash in the GC, since it makes it impossible for the collector to
+correctly track threads.
# endif
#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1200 /* version 12.0+ (MSVC 6.0+) */
+# ifndef GC_HAVE_NO_BUILTIN_BACKTRACE
+# define GC_HAVE_BUILTIN_BACKTRACE
+# endif
+#endif
+
#if defined(GC_HAVE_BUILTIN_BACKTRACE) && !defined(GC_CAN_SAVE_CALL_STACKS)
# define GC_CAN_SAVE_CALL_STACKS
#endif
# include <windows.h>
/*
- * All threads must be created using GC_CreateThread, so that they will be
- * recorded in the thread table. For backwards compatibility, this is not
- * technically true if the GC is built as a dynamic library, since it can
- * and does then use DllMain to keep track of thread creations. But new code
- * should be built to call GC_CreateThread.
+ * All threads must be created using GC_CreateThread or GC_beginthreadex,
+ * or must explicitly call GC_register_my_thread,
+ * so that they will be recorded in the thread table.
+ * For backwards compatibility, it is possible to build the GC
+ * with GC_DLL defined, and set GC_win32_dll_threads to true.
+ * This implicitly registers all created threads, but appears to be
+ * less robust.
+ *
+ * Currently the collector expects all threads to fall through and
+ * terminate normally, or call GC_endthreadex() or GC_ExitThread,
+ * so that the thread is properly unregistered. (An explicit call
+ * to GC_unregister_my_thread() should also work, but risks unregistering
+ * the thread twice.)
*/
GC_API HANDLE WINAPI GC_CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
# ifndef GC_BUILD
# define WinMain GC_WinMain
-# define CreateThread GC_CreateThread
# endif
# endif /* defined(_WIN32_WCE) */
+# define CreateThread GC_CreateThread
+# define ExitThread GC_ExitThread
+# define _beginthreadex GC_beginthreadex
+# define _endthreadex GC_endthreadex
+
#endif /* defined(GC_WIN32_THREADS) && !cygwin */
/*
# include <pthread.h>
+#else
+
+# undef CreateThread
+# undef ExitThread
+# undef _beginthreadex
+# undef _endthreadex
+
#endif
#if defined(GC_DLL) && !defined(MSWINCE)
}
/* Suspend the given thread, if it's still active. */
-GC_suspend(GC_thread t)
+void GC_suspend(GC_thread t)
{
# ifdef MSWINCE
/* SuspendThread will fail if thread is running kernel code */
static DWORD WINAPI thread_start(LPVOID arg);
+void * GC_win32_start_inner(struct GC_stack_base *sb, LPVOID arg)
+{
+ void * ret;
+ thread_args *args = (thread_args *)arg;
+
+ GC_register_my_thread(sb); /* This waits for an in-progress GC. */
+
+ /* Clear the thread entry even if we exit with an exception. */
+ /* This is probably pointless, since an uncaught exception is */
+ /* supposed to result in the process being killed. */
+#ifndef __GNUC__
+ __try {
+#endif /* __GNUC__ */
+ ret = (void *)args->start (args->param);
+#ifndef __GNUC__
+ } __finally {
+#endif /* __GNUC__ */
+# if defined(THREAD_LOCAL_ALLOC)
+ LOCK();
+ GC_destroy_thread_local(&(me->tlfs));
+ UNLOCK();
+# endif
+ GC_free(args);
+ GC_delete_thread(GetCurrentThreadId());
+#ifndef __GNUC__
+ }
+#endif /* __GNUC__ */
+
+ return ret;
+}
+
+DWORD WINAPI GC_win32_start(LPVOID arg)
+{
+ return (DWORD)GC_call_with_stack_base(GC_win32_start_inner, arg);
+}
+
GC_API HANDLE WINAPI GC_CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
}
}
-void * GC_win32_start_inner(struct GC_stack_base *sb, LPVOID arg)
+void WINAPI GC_ExitThread(DWORD dwExitCode)
{
- void * ret;
- thread_args *args = (thread_args *)arg;
+ GC_unregister_my_thread();
+ ExitThread(dwExitCode);
+}
- GC_register_my_thread(sb); /* This waits for an in-progress GC. */
+uintptr_t GC_beginthreadex(
+ void *security, unsigned stack_size,
+ unsigned ( __stdcall *start_address )( void * ),
+ void *arglist, unsigned initflag, unsigned *thrdaddr)
+{
+ uintptr_t thread_h = -1L;
- /* Clear the thread entry even if we exit with an exception. */
- /* This is probably pointless, since an uncaught exception is */
- /* supposed to result in the process being killed. */
-#ifndef __GNUC__
- __try {
-#endif /* __GNUC__ */
- ret = (void *)args->start (args->param);
-#ifndef __GNUC__
- } __finally {
-#endif /* __GNUC__ */
-# if defined(THREAD_LOCAL_ALLOC)
- LOCK();
- GC_destroy_thread_local(&(me->tlfs));
- UNLOCK();
-# endif
- GC_free(args);
- GC_delete_thread(GetCurrentThreadId());
-#ifndef __GNUC__
- }
-#endif /* __GNUC__ */
+ thread_args *args;
- return ret;
+ if (!parallel_initialized) GC_init_parallel();
+ /* make sure GC is initialized (i.e. main thread is attached,
+ tls initialized) */
+
+ client_has_run = TRUE;
+ if (GC_win32_dll_threads) {
+ return _beginthreadex(security, stack_size, start_address,
+ arglist, initflag, thrdaddr);
+ } else {
+ args = GC_malloc_uncollectable(sizeof(thread_args));
+ /* Handed off to and deallocated by child thread. */
+ if (0 == args) {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return -1L;
+ }
+
+ /* set up thread arguments */
+ args -> start = start_address;
+ args -> param = arglist;
+
+ GC_need_to_lock = TRUE;
+ thread_h = _beginthreadex(security, stack_size, GC_win32_start,
+ args, initflag, thrdaddr);
+
+ return thread_h;
+ }
}
-DWORD WINAPI GC_win32_start(struct GC_stack_base *sb, LPVOID arg)
+void GC_endthreadex(unsigned retval)
{
- return (DWORD)GC_call_with_stack_base(GC_win32_start_inner, arg);
+ GC_unregister_my_thread();
+ _endthreadex(retval);
}
+
#endif /* !CYGWIN32 */
#ifdef MSWINCE