Permalink
Browse files

Guard memory allocations for JIT memory with spinlock

This fixes a crash issue where the JIT was running independent from the
GC and the GC was deallocating JIT memory at the same time. We don't
want to make the whole JIT generation GC dependent, since that causes
performance issues, so we guard all memory allocations here with a
spinlock.

The crash would be exposed with these backtraces where things were
modified concurrently:

Thread 6 (process 70553):
 #0  rubinius::jit::FreeRangeHeader::AddToFreeList () at /Users/dirkjan/Code/rubinius/vm/llvm/jit_memory_manager.hpp:151
 #1  0x000000010989f037 in rubinius::jit::MemoryRangeHeader::TrimAllocationToSize (this=0x10f7ec688, FreeList=0x10f7ec688, NewSize=5064) at vm/llvm/jit_memory_manager.cpp:211
 #2  0x000000010989bb75 in rubinius::jit::RubiniusRequestJITMemoryManager::endFunctionBody (this=<value temporarily unavailable, due to optimizations>, F=<value temporarily unavailable, due to optimizations>, FunctionStart=<value temporarily unavailable, due to optimizations>, FunctionEnd=0x13c8 <Address 0x13c8 out of bounds>) at jit_memory_manager.hpp:317
 #3  0x0000000109b4f852 in (anonymous namespace)::JITEmitter::finishFunction ()
 #4  0x0000000109946106 in (anonymous namespace)::Emitter<llvm::JITCodeEmitter>::runOnMachineFunction ()
 #5  0x0000000109bbbc30 in llvm::MachineFunctionPass::runOnFunction ()
 #6  0x0000000109f1beb2 in llvm::FPPassManager::runOnFunction ()
 #7  0x0000000109f1b9f9 in llvm::FunctionPassManagerImpl::run ()
 #8  0x0000000109f1b8a1 in llvm::FunctionPassManager::run ()
 #9  0x0000000109b461ab in llvm::JIT::runJITOnFunctionUnlocked ()
 #10 0x0000000109b46148 in llvm::JIT::runJITOnFunction ()
 #11 0x0000000109898fcc in rubinius::jit::Compiler::generate_function (this=0x10d485d38, indy=true) at vm/llvm/jit_compiler.cpp:118
 #12 0x00000001098ada93 in rubinius::BackgroundCompilerThread::perform (this=0x7fce81633240) at vm/llvm/state.cpp:345
 #13 0x00000001098ad4ef in rubinius::utilities::thread::Thread::delete_on_exit () at /Users/dirkjan/Code/rubinius/vm/util/thread.hpp:79
 #14 0x00000001098ad4ef in rubinius::utilities::thread::Thread::trampoline (arg=0x7fce81633240) at thread.hpp:211
 #15 0x00007fff8e73c7a2 in _pthread_start ()
 #16 0x00007fff8e7291e1 in thread_start ()

Thread 5 (process 70553):
 #0  0x00007fff952b5386 in __semwait_signal ()
 #1  0x00007fff8e7c6800 in nanosleep ()
 #2  0x00007fff8e7c668a in sleep ()
 #3  0x000000010969c9dd in rubinius::segv_handler (sig=11) at vm/environment.cpp:211
 #4  <signal handler called>
 #5  rubinius::jit::FreeRangeHeader::AddToFreeList () at /Users/dirkjan/Code/rubinius/vm/llvm/jit_memory_manager.hpp:151
 #6  0x000000010989ee53 in rubinius::jit::MemoryRangeHeader::FreeBlock (this=0x10f7c88f0, FreeList=<value temporarily unavailable, due to optimizations>) at jit_memory_manager.hpp:155
 #7  0x00000001098ac3e7 in rubinius::LLVMState::remove (this=<value temporarily unavailable, due to optimizations>, func=<value temporarily unavailable, due to optimizations>) at jit_memory_manager.hpp:426
 #8  0x000000010983dde9 in rubinius::CodeManager::sweep (this=0x7fce8180a2d8) at vm/gc/code_manager.cpp:107
 #9  0x0000000109750e7e in rubinius::ObjectMemory::mark () at /Users/dirkjan/Code/rubinius/vm/objectmemory.hpp:634
 #10 0x0000000109750e7e in rubinius::ObjectMemory::collect_mature_finish (this=0x7fce8180a200, state=0x10c94fec8, data=0x7fce8528b220) at vm/objectmemory.cpp:636
 #11 0x0000000109843d8a in rubinius::State::memory () at /Users/dirkjan/Code/rubinius/vm/state.hpp:171
 #12 0x0000000109843d8a in rubinius::ImmixMarker::perform (this=0x7fce8163a720, state=0x10c94fec8) at vm/gc/immix_marker.cpp:172
 #13 0x0000000109843b71 in rubinius::immix_marker_tramp (state=0x10f7ec688) at vm/gc/immix_marker.cpp:18
 #14 0x00000001098094c0 in rubinius::Thread::in_new_thread (ptr=0x7fce86a23e70) at vm/builtin/thread.cpp:250
 #15 0x00007fff8e73c7a2 in _pthread_start ()
 #16 0x00007fff8e7291e1 in thread_start ()
  • Loading branch information...
1 parent 41eb610 commit 1bde00017b91fba6767ffa3e04027fdf6faa815d @dbussink dbussink committed Jul 8, 2013
Showing with 16 additions and 1 deletion.
  1. +15 −0 vm/llvm/jit_memory_manager.hpp
  2. +1 −1 vm/llvm/state.cpp
@@ -57,6 +57,8 @@ SOFTWARE.
#include <llvm/Support/Allocator.h>
#include <llvm/ExecutionEngine/JITMemoryManager.h>
+#include "util/thread.hpp"
+
namespace rubinius {
namespace jit {
@@ -195,6 +197,7 @@ namespace jit {
// When emitting code into a memory block, this is the block.
MemoryRangeHeader *CurBlock;
+ utilities::thread::SpinLock lock_;
bool PoisonMemory;
public:
@@ -234,6 +237,7 @@ namespace jit {
/// startFunctionBody - When a function starts, allocate a block of free
/// executable memory, returning a pointer to it and its actual size.
uint8_t *startFunctionBody(const Function *F, uintptr_t &ActualSize) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
FreeRangeHeader* candidateBlock = FreeMemoryList;
FreeRangeHeader* head = FreeMemoryList;
@@ -307,6 +311,7 @@ namespace jit {
/// in the range [FunctionStart,FunctionEnd).
void endFunctionBody(const Function *F, uint8_t *FunctionStart,
uint8_t *FunctionEnd) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
assert(FunctionEnd > FunctionStart);
assert(FunctionStart == (uint8_t *)(CurBlock+1) &&
"Mismatched function start/end!");
@@ -320,6 +325,8 @@ namespace jit {
/// allocateSpace - Allocate a memory block of the given size. This method
/// cannot be called between calls to startFunctionBody and endFunctionBody.
uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
+
CurBlock = FreeMemoryList;
FreeMemoryList = FreeMemoryList->AllocateBlock();
@@ -338,17 +345,20 @@ namespace jit {
/// allocateStub - Allocate memory for a function stub.
uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize,
unsigned Alignment) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
return (uint8_t*)StubAllocator.Allocate(StubSize, Alignment);
}
/// allocateGlobal - Allocate memory for a global.
uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
return (uint8_t*)DataAllocator.Allocate(Size, Alignment);
}
/// allocateCodeSection - Allocate memory for a code section.
uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
unsigned SectionID) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
// Grow the required block size to account for the block header
Size += sizeof(*CurBlock);
@@ -389,19 +399,22 @@ namespace jit {
/// allocateDataSection - Allocate memory for a data section.
uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
unsigned SectionID) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
return (uint8_t*)DataAllocator.Allocate(Size, Alignment);
}
/// startExceptionTable - Use startFunctionBody to allocate memory for the
/// function's exception table.
uint8_t* startExceptionTable(const Function* F, uintptr_t &ActualSize) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
return startFunctionBody(F, ActualSize);
}
/// endExceptionTable - The exception table of F is now allocated,
/// and takes the memory in the range [TableStart,TableEnd).
void endExceptionTable(const Function *F, uint8_t *TableStart,
uint8_t *TableEnd, uint8_t* FrameRegister) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
assert(TableEnd > TableStart);
assert(TableStart == (uint8_t *)(CurBlock+1) &&
"Mismatched table start/end!");
@@ -429,6 +442,7 @@ namespace jit {
/// deallocateFunctionBody - Deallocate all memory for the specified
/// function body.
void deallocateFunctionBody(void *Body) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
if(Body) {
deallocateBlock(Body);
}
@@ -437,6 +451,7 @@ namespace jit {
/// deallocateExceptionTable - Deallocate memory for the specified
/// exception table.
void deallocateExceptionTable(void *ET) {
+ utilities::thread::SpinLock::LockGuard guard(lock_);
if (ET) deallocateBlock(ET);
}
View
@@ -663,7 +663,7 @@ namespace rubinius {
void LLVMState::remove(void* func) {
shared_.stats.jitted_methods.dec();
- memory_->deallocateBlock(func);
+ memory_->deallocateFunctionBody(func);
}
const static int cInlineMaxDepth = 2;

0 comments on commit 1bde000

Please sign in to comment.