mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-14 15:37:55 +00:00
Bug 696162 - Fix jsgcchunk's AllocGCChunk to be more efficient and to avoid potential problems on Mac 10.7. r=igor
--HG-- extra : rebase_source : 13160f0e9d8b09ed31359daf451adff3e68de30d
This commit is contained in:
parent
6068502afa
commit
6f9b37fd43
@ -1,5 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99 ft=cpp:
|
||||
* vim: set ts=4 sw=4 sts=4 et tw=99 ft=cpp:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Copyright (C) 2006-2008 Jason Evans <jasone@FreeBSD.org>.
|
||||
@ -71,13 +71,25 @@
|
||||
#ifdef XP_WIN
|
||||
|
||||
static void *
|
||||
MapPages(void *addr, size_t size)
|
||||
MapPagesWithFlags(void *addr, size_t size, uint32_t flags)
|
||||
{
|
||||
void *p = VirtualAlloc(addr, size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
|
||||
void *p = VirtualAlloc(addr, size, flags, PAGE_READWRITE);
|
||||
JS_ASSERT_IF(p && addr, p == addr);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void *
|
||||
MapPages(void *addr, size_t size)
|
||||
{
|
||||
return MapPagesWithFlags(addr, size, MEM_COMMIT | MEM_RESERVE);
|
||||
}
|
||||
|
||||
static void *
|
||||
MapPagesUncommitted(void *addr, size_t size)
|
||||
{
|
||||
return MapPagesWithFlags(addr, size, MEM_RESERVE);
|
||||
}
|
||||
|
||||
static void
|
||||
UnmapPages(void *addr, size_t size)
|
||||
{
|
||||
@ -274,6 +286,12 @@ UnmapPages(void *addr, size_t size)
|
||||
namespace js {
|
||||
namespace gc {
|
||||
|
||||
static inline size_t
|
||||
ChunkAddrToOffset(void *addr)
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(addr) & ChunkMask;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
FindChunkStart(void *p)
|
||||
{
|
||||
@ -282,58 +300,179 @@ FindChunkStart(void *p)
|
||||
return reinterpret_cast<void *>(addr);
|
||||
}
|
||||
|
||||
#if defined(JS_GC_HAS_MAP_ALIGN)
|
||||
|
||||
void *
|
||||
AllocChunk()
|
||||
{
|
||||
void *p = MapAlignedPages(ChunkSize, ChunkSize);
|
||||
JS_ASSERT(ChunkAddrToOffset(p) == 0);
|
||||
return p;
|
||||
}
|
||||
|
||||
#elif defined(XP_WIN)
|
||||
|
||||
void *
|
||||
AllocChunkSlow()
|
||||
{
|
||||
void *p;
|
||||
|
||||
#ifdef JS_GC_HAS_MAP_ALIGN
|
||||
p = MapAlignedPages(ChunkSize, ChunkSize);
|
||||
if (!p)
|
||||
return NULL;
|
||||
#else
|
||||
/*
|
||||
* Windows requires that there be a 1:1 mapping between VM allocation
|
||||
* and deallocation operations. Therefore, take care here to acquire the
|
||||
* final result via one mapping operation. This means unmapping any
|
||||
* preliminary result that is not correctly aligned.
|
||||
*/
|
||||
p = MapPages(NULL, ChunkSize);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
if (reinterpret_cast<uintptr_t>(p) & ChunkMask) {
|
||||
UnmapPages(p, ChunkSize);
|
||||
do {
|
||||
/*
|
||||
* Over-allocate in order to map a memory region that is definitely
|
||||
* large enough then deallocate and allocate again the correct size,
|
||||
* within the over-sized mapping.
|
||||
*
|
||||
* Since we're going to unmap the whole thing anyway, the first
|
||||
* mapping doesn't have to commit pages.
|
||||
*/
|
||||
p = MapPagesUncommitted(NULL, ChunkSize * 2);
|
||||
if (!p)
|
||||
return NULL;
|
||||
UnmapPages(p, ChunkSize * 2);
|
||||
p = MapPages(FindChunkStart(p), ChunkSize);
|
||||
while (!p) {
|
||||
/*
|
||||
* Over-allocate in order to map a memory region that is
|
||||
* definitely large enough then deallocate and allocate again the
|
||||
* correct size, within the over-sized mapping.
|
||||
*/
|
||||
p = MapPages(NULL, ChunkSize * 2);
|
||||
if (!p)
|
||||
return 0;
|
||||
UnmapPages(p, ChunkSize * 2);
|
||||
p = MapPages(FindChunkStart(p), ChunkSize);
|
||||
|
||||
/*
|
||||
* Failure here indicates a race with another thread, so
|
||||
* try again.
|
||||
*/
|
||||
}
|
||||
}
|
||||
#endif /* !JS_GC_HAS_MAP_ALIGN */
|
||||
/* Failure here indicates a race with another thread, so try again. */
|
||||
} while(!p);
|
||||
|
||||
JS_ASSERT(!(reinterpret_cast<uintptr_t>(p) & ChunkMask));
|
||||
JS_ASSERT(ChunkAddrToOffset(p) == 0);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *
|
||||
AllocChunk()
|
||||
{
|
||||
/*
|
||||
* Like the *nix AllocChunk implementation, this version of AllocChunk has
|
||||
* a fast and a slow path. We always try the fast path first, then fall
|
||||
* back to the slow path if the fast one failed.
|
||||
*
|
||||
* Our implementation for Windows is complicated by the fact that Windows
|
||||
* requires there be a 1:1 mapping between VM allocation and deallocation
|
||||
* operations.
|
||||
*
|
||||
* This restriction means we must acquire the final result via exactly one
|
||||
* mapping operation, so we can't use some of the tricks we play in the
|
||||
* *nix implementation.
|
||||
*/
|
||||
|
||||
/* Fast path; map just one chunk and hope it's aligned. */
|
||||
void *p = MapPages(NULL, ChunkSize);
|
||||
if (!p) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If that chunk was properly aligned, we're all done. */
|
||||
if (ChunkAddrToOffset(p) == 0) {
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fast path didn't work. See if we can map into the next aligned spot
|
||||
* past the address we were given. If not, fall back to the slow but
|
||||
* reliable method.
|
||||
*
|
||||
* Notice that we have to unmap before we remap, due to Windows's
|
||||
* restriction that there be a 1:1 mapping between VM alloc and dealloc
|
||||
* operations.
|
||||
*/
|
||||
UnmapPages(p, ChunkSize);
|
||||
p = MapPages(FindChunkStart(p), ChunkSize);
|
||||
if (p) {
|
||||
JS_ASSERT(ChunkAddrToOffset(p) == 0);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* When all else fails... */
|
||||
return AllocChunkSlow();
|
||||
}
|
||||
|
||||
#else /* not JS_GC_HAS_MAP_ALIGN and not Windows */
|
||||
|
||||
inline static void *
|
||||
AllocChunkSlow()
|
||||
{
|
||||
/*
|
||||
* Map space for two chunks, then unmap around the result so we're left with
|
||||
* space for one chunk.
|
||||
*/
|
||||
|
||||
char *p = reinterpret_cast<char*>(MapPages(NULL, ChunkSize * 2));
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
|
||||
size_t offset = ChunkAddrToOffset(p);
|
||||
if (offset == 0) {
|
||||
/* Trailing space only. */
|
||||
UnmapPages(p + ChunkSize, ChunkSize);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Leading space. */
|
||||
UnmapPages(p, ChunkSize - offset);
|
||||
|
||||
p += ChunkSize - offset;
|
||||
|
||||
/* Trailing space. */
|
||||
UnmapPages(p + ChunkSize, offset);
|
||||
|
||||
JS_ASSERT(ChunkAddrToOffset(p) == 0);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *
|
||||
AllocChunk()
|
||||
{
|
||||
/*
|
||||
* We can take either a fast or a slow path here. The fast path sometimes
|
||||
* fails; when it does, we fall back to the slow path.
|
||||
*
|
||||
* jemalloc uses a heuristic in which we bypass the fast path if, last
|
||||
* time we called AllocChunk() on this thread, the fast path would have
|
||||
* failed. But here in the js engine, we always try the fast path before
|
||||
* falling back to the slow path, because it's not clear that jemalloc's
|
||||
* heuristic is helpful to us.
|
||||
*/
|
||||
|
||||
/* Fast path; just allocate one chunk and hope it's aligned. */
|
||||
char *p = reinterpret_cast<char*>(MapPages(NULL, ChunkSize));
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
size_t offset = ChunkAddrToOffset(p);
|
||||
if (offset == 0) {
|
||||
/* Fast path worked! */
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* We allocated a chunk, but not at the correct alignment. Try to extend
|
||||
* the tail end of the chunk and then unmap the beginning so that we have
|
||||
* an aligned chunk. If that fails, do the slow version of AllocChunk.
|
||||
*/
|
||||
|
||||
if (MapPages(p + ChunkSize, ChunkSize - offset) != NULL) {
|
||||
/* We extended the mapping! Clean up leading space and we're done. */
|
||||
UnmapPages(p, ChunkSize - offset);
|
||||
p += ChunkSize - offset;
|
||||
JS_ASSERT(ChunkAddrToOffset(p) == 0);
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extension failed. Clean up, then revert to the reliable-but-expensive
|
||||
* method.
|
||||
*/
|
||||
UnmapPages(p, ChunkSize);
|
||||
return AllocChunkSlow();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
FreeChunk(void *p)
|
||||
{
|
||||
JS_ASSERT(p);
|
||||
JS_ASSERT(!(reinterpret_cast<uintptr_t>(p) & ChunkMask));
|
||||
JS_ASSERT(ChunkAddrToOffset(p) == 0);
|
||||
UnmapPages(p, ChunkSize);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user