root/MMgc/GCLargeAlloc.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Free
  2. ClearMarks
  3. Finalize
  4. ConservativeGetMark
  5. GetUsageInfo

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2004-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "MMgc.h"

namespace MMgc
{
        GCLargeAlloc::GCLargeAlloc(GC* gc) : m_gc(gc)
        {
                m_blocks = NULL;
                m_startedFinalize = false;
#ifdef MMGC_MEMORY_PROFILER
                m_totalAskSize = 0;
#endif
        }

#if defined DEBUG || defined MMGC_MEMORY_PROFILER
        void* GCLargeAlloc::Alloc(size_t originalSize, size_t requestSize, int flags)
#else
        void* GCLargeAlloc::Alloc(size_t requestSize, int flags)
#endif
        {
                GCHeap::CheckForAllocSizeOverflow(requestSize, sizeof(LargeBlock)+GCHeap::kBlockSize);

                int blocks = (int)((requestSize+sizeof(LargeBlock)+GCHeap::kBlockSize-1) / GCHeap::kBlockSize);
                uint32_t computedSize = blocks*GCHeap::kBlockSize - sizeof(LargeBlock);
                
                // Allocation must be signalled before we allocate because no GC work must be allowed to
                // come between an allocation and an initialization - if it does, we may crash, as 
                // GCFinalizedObject subclasses may not have a valid vtable, but the GC depends on them
                // having it.  In principle we could signal allocation late but only set the object
                // flags after signaling, but we might still cause trouble for the profiler, which also
                // depends on non-interruptibility.

                m_gc->SignalAllocWork(computedSize);
                
                LargeBlock *block = (LargeBlock*) m_gc->AllocBlock(blocks, GC::kGCLargeAllocPageFirst, 
                                                                                                                   (flags&GC::kZero) != 0, (flags&GC::kCanFail) != 0);
                void *item = NULL;

                if (block)
                {
                        block->flags = ((flags&GC::kFinalize) != 0) ? kFinalizeFlag : 0;
                        block->flags |= ((flags&GC::kContainsPointers) != 0) ? kContainsPointers : 0;
                        block->flags |= ((flags&GC::kRCObject) != 0) ? kRCObject : 0;
                        block->gc = this->m_gc;
                        block->next = m_blocks;
                        block->size = computedSize;
                        m_blocks = block;
                        
                        item = (void*)(block+1);

                        if(m_gc->collecting && !m_startedFinalize)
                                block->flags |= kMarkFlag;

#ifdef _DEBUG
                        (void)originalSize;
                        if (flags & GC::kZero)
                        {
                                // AllocBlock should take care of this
                                for(int i=0, n=(int)(requestSize/sizeof(int)); i<n; i++) {
                                        if(((int*)item)[i] != 0)
                                                GCAssert(false);
                                }
                        }
#endif

#ifdef MMGC_HOOKS
                        GCHeap* heap = GCHeap::GetGCHeap();
                        if(heap->HooksEnabled()) {
                                size_t userSize = block->size - DebugSize();
#ifdef MMGC_MEMORY_PROFILER
                                m_totalAskSize += originalSize;
                                heap->AllocHook(GetUserPointer(item), originalSize, userSize);
#else
                                heap->AllocHook(GetUserPointer(item), 0, userSize);
#endif
                        }
#endif
                }
                return item;
        }

        
        void GCLargeAlloc::Free(const void *item)
        {
                GCAssertMsg(!m_startedFinalize, "GCLargeAlloc::Free is not allowed during finalization; caller must guard against this.");

                LargeBlock *b = GetLargeBlock(item);

#ifdef MMGC_HOOKS
                GCHeap* heap = GCHeap::GetGCHeap();
                if(heap->HooksEnabled())
                {
                        const void* p = GetUserPointer(item);
                        size_t userSize = GC::Size(p);
                        heap->FreeHook(p, userSize, 0xca);
#ifdef MMGC_MEMORY_PROFILER
                        if(heap->GetProfiler())
                                m_totalAskSize -= heap->GetProfiler()->GetAskSize(p);
#endif
                        heap->FinalizeHook(p, userSize);
                }
#endif

                if(b->flags & kHasWeakRef)
                        b->gc->ClearWeakRef(GetUserPointer(item));

                LargeBlock **prev = &m_blocks;
                while(*prev)
                {
                        if(b == *prev)
                        {
                                *prev = Next(b);
                                m_gc->FreeBlock(b, b->GetNumBlocks());
                                return;
                        }
                        prev = (LargeBlock**)(&(*prev)->next);
                }
                GCAssertMsg(false, "Bad free!");
        }

        void GCLargeAlloc::ClearMarks()
        {
                LargeBlock *block = m_blocks;
                while (block) {
                        block->flags &= ~(kMarkFlag|kQueuedFlag);
                        block = Next(block);
                }
        }

        void GCLargeAlloc::Finalize()
        {
                m_startedFinalize = true;
                LargeBlock **prev = &m_blocks;
                while (*prev) {                 
                        LargeBlock *b = *prev;
                        if ((b->flags & kMarkFlag) == 0) {
                                GCAssert((b->flags & kQueuedFlag) == 0);
                                GC* gc = b->gc;
                                
                                // Large blocks may be allocated by finalizers for large blocks, creating contention
                                // for the block list.  Yet the block list must be live, since eg GetUsageInfo may be
                                // called by the finalizers (or their callees).
                                //
                                // Unlink the block from the list early to avoid contention.
                                
                                *prev = Next(b);
                                b->next = NULL;

                                void *item = b+1;
                                if (NeedsFinalize(b)) {
                                        GCFinalizedObject *obj = (GCFinalizedObject *) item;
                                        obj = (GCFinalizedObject *) GetUserPointer(obj);
                                        obj->~GCFinalizedObject();
#if defined(_DEBUG)
                                        if((b->flags & kRCObject) != 0) {
                                                gc->RCObjectZeroCheck((RCObject*)obj);
                                        }
#endif
                                }
                                if(b->flags & kHasWeakRef) {
                                        gc->ClearWeakRef(GetUserPointer(item));
                                }
                                
#ifdef MMGC_HOOKS
                                if(m_gc->heap->HooksEnabled())
                                {
                                #ifdef MMGC_MEMORY_PROFILER
                                        if(GCHeap::GetGCHeap()->GetProfiler())
                                                m_totalAskSize -= GCHeap::GetGCHeap()->GetProfiler()->GetAskSize(GetUserPointer(item));
                                #endif

                                        m_gc->heap->FinalizeHook(GetUserPointer(item), b->size - DebugSize());
                                }
#endif
                                
                                // The block is not empty until now, so now add it.
                                gc->AddToLargeEmptyBlockList(b);
                                continue;
                        }
                        // clear marks
                        b->flags &= ~(kMarkFlag|kQueuedFlag);
                        prev = (LargeBlock**)(&b->next);
                }
                m_startedFinalize = false;
        }

        GCLargeAlloc::~GCLargeAlloc()
        {
                GCAssert(!m_blocks);
        }

#ifdef _DEBUG
        /* static */
        bool GCLargeAlloc::ConservativeGetMark(const void *item, bool bogusPointerReturnValue)
        {
                if(((uintptr_t) item & 0xfff) == sizeof(LargeBlock))
                {
                        return GetMark(item);
                }
                return bogusPointerReturnValue;
        }
#endif
        
        void GCLargeAlloc::GetUsageInfo(size_t& totalAskSize, size_t& totalAllocated)
        {
                totalAskSize = 0;
                totalAllocated = 0;

                LargeBlock *block = m_blocks;
                while (block) {
                        totalAllocated += block->size;
                        block = Next(block);
                }               
        
#ifdef MMGC_MEMORY_PROFILER
                totalAskSize += m_totalAskSize;
#endif
        }
}

/* [<][>][^][v][top][bottom][index][help] */