Skip to content

Commit

Permalink
Partial implementation of gc_start/stop_the_world for non-pseudo-atomic
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitryvk committed Apr 3, 2010
1 parent d84f838 commit 379ed76
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 4 deletions.
5 changes: 5 additions & 0 deletions src/runtime/runtime.h
Expand Up @@ -282,4 +282,9 @@ struct runtime_options {
size_t thread_control_stack_size;
};

#if defined(LISP_FEATURE_WIN32)
typedef void (*planted_function_t)();
void plant_call(HANDLE thread, planted_function_t fn);
#endif

#endif /* _SBCL_RUNTIME_H_ */
30 changes: 26 additions & 4 deletions src/runtime/thread.c
Expand Up @@ -44,6 +44,7 @@
#include "interr.h" /* for lose() */
#include "alloc.h"
#include "gc-internal.h"
#include "pseudo-atomic.h"

#ifdef LISP_FEATURE_WIN32
/*
Expand Down Expand Up @@ -619,12 +620,22 @@ os_thread_t create_thread(lispobj initial_function) {
* it's in the middle of allocation) then waits for another SIG_STOP_FOR_GC.
*/

#if defined(LISP_FEATURE_WIN32)
// helper function for Win32
static void sleep_while_gc()
{
struct thread* th = arch_os_get_current_thread();
fprintf(stderr, "In thread %lu, waiting while thread is suspended\n", (DWORD)th->os_thread);
wait_for_thread_state_change(th, STATE_SUSPENDED);
fprintf(stderr, "In thred %lu, waiting done, returning\n", (DWORD)th->os_thread);
}
#endif

/* To avoid deadlocks when gc stops the world all clients of each
* mutex must enable or disable SIG_STOP_FOR_GC for the duration of
* holding the lock, but they must agree on which. */
void gc_stop_the_world()
{
#if !defined(LISP_FEATURE_WIN32)
struct thread *p,*th=arch_os_get_current_thread();
int status, lock_ret;
#ifdef LOCK_CREATE_THREAD
Expand All @@ -649,6 +660,16 @@ void gc_stop_the_world()
if((p!=th) && ((thread_state(p)==STATE_RUNNING))) {
FSHOW_SIGNAL((stderr,"/gc_stop_the_world: suspending thread %lu\n",
p->os_thread));
#if defined(LISP_FEATURE_WIN32)
if (SuspendThread(p->os_thread) == (DWORD)-1)
lose("cannot suspend thread %lu, SuspendThread returned -1, GetLastError() = %lu",
(DWORD)p->os_thread, GetLastError());
if (get_pseudo_atomic_atomic(p))
lose("Sorry, can't yet suspend thread in pseudo-atomic section");
set_thread_state(p, STATE_SUSPENDED);
plant_call(p->os_thread, sleep_while_gc);
ResumeThread(p->os_thread);
#else
/* We already hold all_thread_lock, P can become DEAD but
* cannot exit, ergo it's safe to use pthread_kill. */
status=pthread_kill(p->os_thread,SIG_STOP_FOR_GC);
Expand All @@ -660,6 +681,7 @@ void gc_stop_the_world()
lose("cannot send suspend thread=%lu: %d, %s\n",
p->os_thread,status,strerror(status));
}
#endif
}
}
FSHOW_SIGNAL((stderr,"/gc_stop_the_world:signals sent\n"));
Expand All @@ -675,12 +697,10 @@ void gc_stop_the_world()
}
}
FSHOW_SIGNAL((stderr,"/gc_stop_the_world:end\n"));
#endif
}

void gc_start_the_world()
{
#if !defined(LISP_FEATURE_WIN32)
struct thread *p,*th=arch_os_get_current_thread();
int lock_ret;
/* if a resumed thread creates a new thread before we're done with
Expand All @@ -699,7 +719,10 @@ void gc_start_the_world()
}
FSHOW_SIGNAL((stderr, "/gc_start_the_world: resuming %lu\n",
p->os_thread));
//FIXME Win32: unplant_call_if_uncalled here
fprintf(stderr, "gc_start_the_world setting state to running\n");
set_thread_state(p, STATE_RUNNING);
fprintf(stderr, "gc_start_the_world done setting state to running\n");
}
}
}
Expand All @@ -712,7 +735,6 @@ void gc_start_the_world()
#endif

FSHOW_SIGNAL((stderr,"/gc_start_the_world:end\n"));
#endif
}
#endif

Expand Down
21 changes: 21 additions & 0 deletions src/runtime/x86-assem.S
Expand Up @@ -804,6 +804,27 @@ Lrestore:

SIZE(GNAME(fast_bzero_detect))


.text
.globl GNAME(planted_trampoline)
TYPE(GNAME(planted_trampoline))
.align 2,0x90
GNAME(planted_trampoline):
#now function pointer is on stack
pushl %ebp
movl %esp, %ebp
pushfl
pushal
#now the planted function pointer is now offset at 32 + 4 + 4 (32 bytes from pusha, 4 bytes from pushf, 4 for old ebp)
call *40(%esp)
popal
popfl
#pop the planted function pointer and argument
leal 4(%esp), %esp
popl %ebp
#return to original place
ret
SIZE(GNAME(planted_trampoline))

.text
.align align_16byte,0x90
Expand Down
56 changes: 56 additions & 0 deletions src/runtime/x86-win32-os.c
Expand Up @@ -151,3 +151,59 @@ void
os_flush_icache(os_vm_address_t address, os_vm_size_t length)
{
}

extern void planted_trampoline();

void plant_call(HANDLE thread, planted_function_t fn)
{
CONTEXT context;
if (SuspendThread(thread) == -1)
{
fprintf(stderr, "Unable to suspend thread 0x%x\n", (int)thread);
return;
}
context.ContextFlags = CONTEXT_FULL;
if (GetThreadContext(thread, &context) == 0)
{
fprintf(stderr, "Unable to get thread context for thread 0x%x\n", (int) thread);
ResumeThread(thread);
return;
}

{
//planting
//calling convention is as follow:
// PUSH unsaved registers
// PUSH last arg
// ...
// PUSH first arg
// PUSH return address }
// SET %EIP to first instruction } CALL does this
// POP unsaved registers

// we should save registers (TODO!)

// we have no args, so don't push

// pushing return address (the current EIP, which points to the next instruction)
context.Esp -= 4;
*((int*)((void*)context.Esp - 0)) = context.Eip;
context.Esp -= 4;
*((int*)((void*)context.Esp - 0)) = (int)(void*)fn;
// setting %EIP
context.Eip = (int)(void*)planted_trampoline;
}
if (SetThreadContext(thread, &context) == 0)
{
fprintf(stderr, "Unable to get thread context for thread 0x%x\n", (int) thread);
ResumeThread(thread);
return;
}

if (ResumeThread(thread) == -1)
{
fprintf(stderr, "Unable to resume thread 0x%x\n", (int)thread);
return;
}
fprintf(stderr, "Function planted to thread 0x%x\n", (int)thread);
}

0 comments on commit 379ed76

Please sign in to comment.