blob: c3acbf9c6c7c2e17742d6e488fbb2d24d2c2bc01 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Xiao-Feng Li, 2006/10/05
*/
#ifndef _GC_THREAD_H_
#define _GC_THREAD_H_
#include "../common/gc_space.h"
#include "../common/gc_metadata.h"
#define ALLOC_ZEROING
#ifdef PREFETCH_SUPPORTED
#define ALLOC_PREFETCH
#endif
#ifdef ALLOC_ZEROING /* ----------------- */
#ifdef ALLOC_PREFETCH /* vvvvvvvvvvvvvvvv */
extern POINTER_SIZE_INT PREFETCH_DISTANCE;
extern POINTER_SIZE_INT ZEROING_SIZE;
extern POINTER_SIZE_INT PREFETCH_STRIDE;
extern Boolean PREFETCH_ENABLED;
#else /* ALLOC_PREFETCH ^^^^^^^^^^^^^^^^ */
#define ZEROING_SIZE 2*KB
#endif /* !ALLOC_PREFETCH */
#endif /* ALLOC_ZEROING ----------------- */
extern POINTER_SIZE_INT tls_gc_offset;
inline void* gc_get_tls()
{
void* tls_base = vm_thread_local();
return (void*)*(POINTER_SIZE_INT*)((char*)tls_base + tls_gc_offset);
}
inline void gc_set_tls(void* gc_tls_info)
{
void* tls_base = vm_thread_local();
*(POINTER_SIZE_INT*)((char*)tls_base + tls_gc_offset) = (POINTER_SIZE_INT)gc_tls_info;
}
/* NOTE:: don't change the position of free/ceiling, because the offsets are constants for inlining */
typedef struct Allocator{
void *free;
void *ceiling;
void* end;
Block *alloc_block;
Chunk_Header ***local_chunks;
Space* alloc_space;
GC *gc;
VmThreadHandle thread_handle; /* This thread; */
unsigned int handshake_signal; /*Handshake is used in concurrent GC.*/
/* the number of allocated blocks. For collector, it reflects the load balance; for mutator, it reflects mutator activities. */
unsigned int num_alloc_blocks;
/* Time measurment. For collector, it's used to collect collection time; for mutator, it's used to collect mutator time. */
int64 time_measurement_start;
int64 time_measurement_end;
}Allocator;
inline void thread_local_unalloc(unsigned int size, Allocator* allocator)
{
void* free = allocator->free;
allocator->free = (void*)((POINTER_SIZE_INT)free - size);
return;
}
#ifdef ALLOC_ZEROING
FORCE_INLINE Partial_Reveal_Object* thread_local_alloc_zeroing(unsigned int size, Allocator* allocator)
{
POINTER_SIZE_INT free = (POINTER_SIZE_INT)allocator->free;
POINTER_SIZE_INT ceiling = (POINTER_SIZE_INT)allocator->ceiling;
POINTER_SIZE_INT new_free = free + size;
POINTER_SIZE_INT block_ceiling = (POINTER_SIZE_INT)allocator->end;
if( new_free > block_ceiling)
return NULL;
POINTER_SIZE_INT new_ceiling;
new_ceiling = new_free + ZEROING_SIZE;
new_ceiling = new_ceiling - (new_ceiling & 63);
#ifdef ALLOC_PREFETCH
if(PREFETCH_ENABLED) {
POINTER_SIZE_INT pre_addr = new_free, pref_stride= PREFETCH_STRIDE, pref_dist= new_ceiling + PREFETCH_DISTANCE;
do{
prefetchnta(pre_addr);
pre_addr += pref_stride;
}while(pre_addr< pref_dist);
}
#endif
if( new_ceiling > block_ceiling )
new_ceiling = block_ceiling;
allocator->ceiling = (void*)new_ceiling;
allocator->free = (void*)new_free;
memset((void*)ceiling, 0, new_ceiling - ceiling);
return (Partial_Reveal_Object*)free;
}
#endif /* ALLOC_ZEROING */
FORCE_INLINE Partial_Reveal_Object* thread_local_alloc(unsigned int size, Allocator* allocator)
{
POINTER_SIZE_INT free = (POINTER_SIZE_INT)allocator->free;
POINTER_SIZE_INT ceiling = (POINTER_SIZE_INT)allocator->ceiling;
POINTER_SIZE_INT new_free = free + size;
if (new_free <= ceiling){
allocator->free= (void*)new_free;
return (Partial_Reveal_Object*)free;
}
#ifndef ALLOC_ZEROING
return NULL;
#else
return thread_local_alloc_zeroing(size, allocator);
#endif /* #ifndef ALLOC_ZEROING */
}
FORCE_INLINE void allocator_init_free_block(Allocator* allocator, Block_Header* alloc_block)
{
assert(alloc_block->status == BLOCK_FREE);
alloc_block->status = BLOCK_IN_USE;
#ifdef USE_UNIQUE_MOVE_COMPACT_GC
alloc_block->num_multi_block = 0;
#endif
/* set allocation context */
void* new_free = alloc_block->free;
allocator->free = new_free;
#ifndef ALLOC_ZEROING
allocator->ceiling = alloc_block->ceiling;
memset(new_free, 0, GC_BLOCK_BODY_SIZE_BYTES);
#else
#ifdef ALLOC_PREFETCH
if(PREFETCH_ENABLED) {
POINTER_SIZE_INT pre_addr = (POINTER_SIZE_INT) new_free, pref_stride= PREFETCH_STRIDE, pref_dist= pre_addr + PREFETCH_DISTANCE;
do{
prefetchnta(pre_addr);
pre_addr += pref_stride;
}while(pre_addr< pref_dist);
}
#endif
POINTER_SIZE_INT new_ceiling = (POINTER_SIZE_INT)new_free + ZEROING_SIZE;
POINTER_SIZE_INT align = new_ceiling & 63;
allocator->ceiling = (void*)(new_ceiling - align);
memset(new_free, 0, ZEROING_SIZE - align);
#endif /* #ifndef ALLOC_ZEROING */
allocator->end = alloc_block->ceiling;
allocator->alloc_block = (Block*)alloc_block;
return;
}
inline void alloc_context_reset(Allocator* allocator)
{
Block_Header* block = (Block_Header*)allocator->alloc_block;
/* it can be NULL when GC happens before the mutator resumes (the memory is run out by other mutators),
or the function is called by collector after it finishes collection, before sleeps waiting for new task */
if( block != NULL ){
assert(block->status == BLOCK_IN_USE);
block->free = allocator->free;
block->status = BLOCK_USED;
allocator->alloc_block = NULL;
}
allocator->free = NULL;
allocator->ceiling = NULL;
allocator->end = NULL;
return;
}
#endif /* #ifndef _GC_THREAD_H_ */