Ejemplo n.º 1
0
// Check if the new region is big enough to store a new free block.
// if not do not split, but free list now points to next node.
void allocateBlock(uint32_t size, uint32_t spaceLeft, 
	uint32_t minFreeRegionSize, uint32_t sizeFreeRegion,  
	void* freeBlock, void* currentFreeBlock, 
	struct freeBlockLinks *currentBlockLinks) 
{
	if (spaceLeft > minFreeRegionSize) {
		if (sufficientSize(size)) {
			freeList = splitFreeBlock(freeBlock, spaceLeft, 
				size, currentFreeBlock, currentBlockLinks);
		} else {
			minFreeRegionSize -= BYTES_TO_WORDS(sizeof(freeBlockFooter));
			freeList = splitFreeBlock(freeBlock, spaceLeft, 
				minFreeRegionSize, currentFreeBlock, currentBlockLinks);
		}

		numberFreeBlocks++;
	} else { 
		// No split.
		// If successor is mmap footer, then set its flag to 0.
		totalFreeSpace -= WORDS_TO_BYTES(sizeFreeRegion);
		
		uint64_t sizeOffset = WORDS_TO_BYTES(sizeFreeRegion);
		void* next = ADDRESS_PLUS_OFFSET(currentFreeBlock + sizeOffset, blockHeader);
		struct blockHeader *nextBlockHeader = INIT_STRUCT(blockHeader, next);
		// check if last one (size = 0.)
		if (nextBlockHeader->attribute == MSB_TO_ONE) { 
			// Tell next that block is not free anymore.
			nextBlockHeader->attribute = MSB_TO_ZERO; 
		} else {
			// If next block is not last one, then just set its flag to zero and keep size.
			// Flag is automatically set to zero.
			nextBlockHeader->attribute = GET_SIZE(nextBlockHeader->attribute); 
		}

		freeList = NEXT_BLOCK(currentBlockLinks);
	}
}
Ejemplo n.º 2
0
void *Z_Malloc(size_t size, int tag, void *user)
{
    memblock_t *start, *iter;
    memvolume_t *volume;

    if (tag < PU_APPSTATIC || tag > PU_PURGELEVEL)
    {
        App_Log(DE2_LOG_WARNING, "Z_Malloc: Invalid purgelevel %i, cannot allocate memory.", tag);
        return NULL;
    }
    if (!size)
    {
        // You can't allocate "nothing."
        return NULL;
    }

    lockZone();

    // Align to pointer size.
    size = ALIGNED(size);

    // Account for size of block header.
    size += sizeof(memblock_t);

    // Iterate through memory volumes until we can find one with enough free
    // memory. (Note: we *will *find one that's large enough.)
    for (volume = volumeRoot; ; volume = volume->next)
    {
        uint numChecked = 0;
        dd_bool gotoNextVolume = false;

        if (volume == NULL)
        {
            // We've run out of volumes.  Let's allocate a new one
            // with enough memory.
            size_t newVolumeSize = MEMORY_VOLUME_SIZE;

            if (newVolumeSize < size + 0x1000)
                newVolumeSize = size + 0x1000; // with some spare memory

            volume = createVolume(newVolumeSize);
        }

        if (isVolumeTooFull(volume))
        {
            // We should skip this one.
            continue;
        }

        DENG_ASSERT(volume->zone);

        // Scan through the block list looking for the first free block of
        // sufficient size, throwing out any purgable blocks along the
        // way.

        if (tag == PU_APPSTATIC || tag == PU_GAMESTATIC)
        {
            // Appstatic allocations may be around for a long time so make sure
            // they don't litter the volume. Their own rover will keep them as
            // tightly packed as possible.
            iter = volume->zone->staticRover;
        }
        else
        {
            // Everything else is allocated using the rover.
            iter = volume->zone->rover;
        }
        assert(iter->prev);

        // Back up a little to see if we have some space available nearby.
        start = iter = rewindRover(volume, iter, 3, size);
        numChecked = 0;

        // If the start is in a sequence, move it to the beginning of the
        // entire sequence. Sequences are handled as a single unpurgable entity,
        // so we can stop checking at its start.
        if (start->seqFirst)
        {
            start = start->seqFirst;
        }

        // We will scan ahead until we find something big enough.
        for ( ; !(isFreeBlock(iter) && iter->size >= size); numChecked++)
        {
            // Check for purgable blocks we can dispose of.
            if (!isFreeBlock(iter))
            {
                if (iter->tag >= PU_PURGELEVEL)
                {
                    memblock_t *old = iter;
                    iter = iter->prev; // Step back.
#ifdef LIBDENG_FAKE_MEMORY_ZONE
                    freeBlock(old->area, &start);
#else
                    freeBlock((byte *) old + sizeof(memblock_t), &start);
#endif
                }
                else
                {
                    if (iter->seqFirst)
                    {
                        // This block is part of a sequence of blocks, none of
                        // which can be purged. Skip the entire sequence.
                        iter = iter->seqFirst->seqLast;
                    }
                }
            }

            // Move to the next block.
            iter = advanceBlock(volume, iter);

            // Ensure that iter will eventually touch start.
            assert(!start->seqFirst || start->seqFirst == start ||
                   !start->seqFirst->prev->seqFirst ||
                   start->seqFirst->prev->seqFirst == start->seqFirst->prev->seqLast);

            if (iter == start && numChecked > 0)
            {
                // Scanned all the way through, no suitable space found.
                gotoNextVolume = true;
                App_Log(DE2_LOG_DEBUG,
                        "Z_Malloc: gave up on volume after %i checks", numChecked);
                break;
            }
        }

        // At this point we've found/created a big enough block or we are
        // skipping this volume entirely.

        if (gotoNextVolume) continue;

        // Found a block big enough.
        if (iter->size - size > MINFRAGMENT)
        {
            splitFreeBlock(iter, size);
        }

#ifdef LIBDENG_FAKE_MEMORY_ZONE
        iter->areaSize = size - sizeof(memblock_t);
        iter->area = M_Malloc(iter->areaSize);
#endif

        if (user)
        {
            iter->user = user;      // mark as an in use block
#ifdef LIBDENG_FAKE_MEMORY_ZONE
            *(void **) user = iter->area;
#else
            *(void **) user = (void *) ((byte *) iter + sizeof(memblock_t));
#endif
        }
        else
        {
            // An owner is required for purgable blocks.
            DENG_ASSERT(tag < PU_PURGELEVEL);

            iter->user = MEMBLOCK_USER_ANONYMOUS; // mark as in use, but unowned
        }
        iter->tag = tag;

        if (tag == PU_MAPSTATIC)
        {
            // Level-statics are linked into unpurgable sequences so they can
            // be skipped en masse.
            iter->seqFirst = iter;
            iter->seqLast = iter;
            if (iter->prev->seqFirst)
            {
                iter->seqFirst = iter->prev->seqFirst;
                iter->seqFirst->seqLast = iter;
            }
        }
        else
        {
            // Not part of a sequence.
            iter->seqLast = iter->seqFirst = NULL;
        }

        // Next allocation will start looking here, at the rover.
        if (tag == PU_APPSTATIC || tag == PU_GAMESTATIC)
        {
            volume->zone->staticRover = advanceBlock(volume, iter);
        }
        else
        {
            volume->zone->rover = advanceBlock(volume, iter);
        }

        // Keep tabs on how much memory is used.
        volume->allocatedBytes += iter->size;

        iter->volume = volume;
        iter->id = LIBDENG_ZONEID;

        unlockZone();

#ifdef LIBDENG_FAKE_MEMORY_ZONE
        return iter->area;
#else
        return (void *) ((byte *) iter + sizeof(memblock_t));
#endif
    }
}