STATIC word GC_used_heap_size_after_full = 0;
+extern GC_on_collection_event_proc GC_on_collection_event;
+
/* GC_copyright symbol is externally visible. */
char * const GC_copyright[] =
{"Copyright 1988,1989 Hans-J. Boehm and Alan J. Demers ",
}
}
GC_notify_full_gc();
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_COLLECTION_BEGIN, NULL);
+
# ifndef SMALL_CONFIG
if (GC_print_stats) {
GET_TIME(start_time);
# define COMMA_IF_USE_MUNMAP(x) /* empty */
#endif
+static void start_world()
+{
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_STARTWORLD_BEGIN, NULL);
+
+ START_WORLD();
+
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_STARTWORLD_END, NULL);
+}
+
/*
* Assumes lock is held. We stop the world and mark from all roots.
* If stop_func() ever returns TRUE, we may fail and return FALSE.
GET_TIME(start_time);
# endif
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_STOPWORLD_BEGIN, NULL);
+
STOP_WORLD();
+
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_STOPWORLD_END, NULL);
+
# ifdef THREAD_LOCAL_ALLOC
GC_world_stopped = TRUE;
# endif
GC_clear_a_few_frames();
GC_noop6(0,0,0,0,0,0);
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_MARK_BEGIN, NULL);
+
GC_initiate_gc();
for (i = 0;;i++) {
if ((*stop_func)()) {
# ifdef THREAD_LOCAL_ALLOC
GC_world_stopped = FALSE;
# endif
- START_WORLD();
+
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_MARK_END, NULL);
+
+ start_world();
return(FALSE);
}
if (GC_mark_some(GC_approx_sp())) break;
}
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_MARK_END, NULL);
+
GC_gc_no++;
GC_DBGLOG_PRINTF("GC #%lu freed %ld bytes, heap %lu KiB"
IF_USE_MUNMAP(" (+ %lu KiB unmapped)") "\n",
# ifdef THREAD_LOCAL_ALLOC
GC_world_stopped = FALSE;
# endif
- START_WORLD();
+ start_world();
# ifndef SMALL_CONFIG
if (GC_PRINT_STATS_FLAG) {
unsigned long time_diff;
MS_TIME_DIFF(done_time,finalize_time));
}
# endif
+
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_COLLECTION_END, NULL);
}
/* If stop_func == 0 then GC_default_stop_func is used instead. */
#endif /* !GC_NO_THREADS_DISCOVERY */
+extern GC_on_collection_event_proc GC_on_collection_event;
+
/* Caller holds allocation lock. */
GC_INNER void GC_stop_world(void)
{
kern_result = thread_suspend(p->stop_info.mach_thread);
if (kern_result != KERN_SUCCESS)
ABORT("thread_suspend failed");
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)p->stop_info.mach_thread);
}
}
}
if ((p->flags & FINISHED) == 0 && !p->thread_blocked &&
p->stop_info.mach_thread != my_thread)
GC_thread_resume(p->stop_info.mach_thread);
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)p->stop_info.mach_thread);
}
}
/* Both the supplied setter and the getter */
/* acquire the GC lock (to avoid data races). */
+typedef enum
+{
+ GC_EVENT_COLLECTION_BEGIN,
+ GC_EVENT_STOPWORLD_BEGIN,
+ GC_EVENT_STOPWORLD_END,
+ GC_EVENT_MARK_BEGIN,
+ GC_EVENT_MARK_END,
+ GC_EVENT_STARTWORLD_BEGIN,
+ GC_EVENT_STARTWORLD_END,
+ GC_EVENT_COLLECTION_END,
+ GC_EVENT_THREAD_SUSPENDED,
+ GC_EVENT_THREAD_UNSUSPENDED
+} GCEventKind;
+
+typedef void (GC_CALLBACK * GC_on_collection_event_proc)(GCEventKind, void*);
+ /* Invoked to indicate progress through the */
+ /* collection process. */
+ /* Called with the world stopped (and the */
+ /* allocation lock held). May be 0. */
+GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc);
+GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void);
+
GC_API GC_ATTR_DEPRECATED int GC_find_leak;
/* Do not actually garbage collect, but simply */
/* report inaccessible memory that was not */
return fn;
}
+GC_on_collection_event_proc GC_on_collection_event = NULL;
+
+GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc fn)
+{
+ /* fn may be 0 (means no event notifier). */
+ DCL_LOCK_STATE;
+ LOCK();
+ GC_on_collection_event = fn;
+ UNLOCK();
+}
+
+GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void)
+{
+ GC_on_collection_event_proc fn;
+ DCL_LOCK_STATE;
+ LOCK();
+ fn = GC_on_collection_event;
+ UNLOCK();
+ return fn;
+}
+
GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc fn)
{
/* fn may be 0 (means no finalizer notifier). */
{
int n_live_threads = 0;
int i;
-
+ int thread_id;
# ifndef NACL
GC_thread p;
# ifndef GC_OPENBSD_UTHREADS
if (pthread_stackseg_np(p->id, &stack))
ABORT("pthread_stackseg_np failed");
p -> stop_info.stack_ptr = (ptr_t)stack.ss_sp - stack.ss_size;
+
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_THREAD_SUSPENDED, (void *)p->id);
}
# else
# ifndef PLATFORM_ANDROID
- result = pthread_kill(p -> id, GC_sig_suspend);
+ thread_id = p -> id;
+ result = pthread_kill(thread_id, GC_sig_suspend);
# else
- result = android_thread_kill(p -> kernel_id, GC_sig_suspend);
+ thread_id = p -> kernel_id;
+ result = android_thread_kill(thread_id, GC_sig_suspend);
# endif
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_THREAD_SUSPENDED, (void *)threadid);
switch(result) {
case ESRCH:
/* Not really there anymore. Possible? */
ABORT("pthread_resume_np failed");
# else
# ifndef PLATFORM_ANDROID
- result = pthread_kill(p -> id, GC_sig_thr_restart);
+ thread_id = p -> id;
+ result = pthread_kill(thread_id, GC_sig_thr_restart);
# else
- result = android_thread_kill(p -> kernel_id,
- GC_sig_thr_restart);
+ thread_id = p -> kernel_id;
+ result = android_thread_kill(thread_id, GC_sig_thr_restart);
# endif
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)thread_id);
+
switch(result) {
case ESRCH:
/* Not really there anymore. Possible? */
/* TRUE only if GC_stop_world() acquired GC_write_cs. */
#endif
+extern GC_on_collection_event_proc GC_on_collection_event;
+
GC_INNER void GC_stop_world(void)
{
DWORD thread_id = GetCurrentThreadId();
if (t -> stack_base != 0 && t -> thread_blocked_sp == NULL
&& t -> id != thread_id) {
GC_suspend((GC_thread)t);
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_THREAD_SUSPENDED, (void *)THREAD_HANDLE(t));
}
}
} else
if (t -> stack_base != 0 && t -> thread_blocked_sp == NULL
&& !KNOWN_FINISHED(t) && t -> id != thread_id) {
GC_suspend(t);
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_THREAD_SUSPENDED, (void *)THREAD_HANDLE(t));
}
}
}
if (ResumeThread(THREAD_HANDLE(t)) == (DWORD)-1)
ABORT("ResumeThread failed");
t -> suspended = FALSE;
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)THREAD_HANDLE(t));
}
}
} else {
ABORT("ResumeThread failed");
UNPROTECT_THREAD(t);
t -> suspended = FALSE;
+ if (GC_on_collection_event)
+ GC_on_collection_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)THREAD_HANDLE(t));
}
}
}