Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring to be considered before adding MMTk #55608

Merged
merged 11 commits into from
Oct 19, 2024
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ ifeq ($(USE_SYSTEM_LIBUV),0)
UV_HEADERS += uv.h
UV_HEADERS += uv/*.h
endif
PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,work-stealing-queue.h gc-interface.h gc-tls.h julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h jloptions.h)
PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,work-stealing-queue.h gc-interface.h gc-tls.h gc-tls-common.h julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h jloptions.h)
ifeq ($(OS),WINNT)
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,win32_ucontext.h)
endif
Expand Down
205 changes: 205 additions & 0 deletions src/gc-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ extern "C" {

jl_gc_num_t gc_num = {0};

JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void)
{
return gc_num.total_time;
}

// =========================================================================== //
// GC Callbacks
// =========================================================================== //
Expand Down Expand Up @@ -485,10 +490,210 @@ JL_DLLEXPORT void jl_finalize(jl_value_t *o)
int gc_n_threads;
jl_ptls_t* gc_all_tls_states;

// =========================================================================== //
// Allocation
// =========================================================================== //

JL_DLLEXPORT void * jl_gc_alloc_typed(jl_ptls_t ptls, size_t sz, void *ty)
{
return jl_gc_alloc(ptls, sz, ty);
}

JL_DLLEXPORT jl_value_t *jl_gc_allocobj(size_t sz)
{
jl_ptls_t ptls = jl_current_task->ptls;
return jl_gc_alloc(ptls, sz, NULL);
}

// allocation wrappers that save the size of allocations, to allow using
// jl_gc_counted_* functions with a libc-compatible API.

JL_DLLEXPORT void *jl_malloc(size_t sz)
{
int64_t *p = (int64_t *)jl_gc_counted_malloc(sz + JL_SMALL_BYTE_ALIGNMENT);
if (p == NULL)
return NULL;
p[0] = sz;
return (void *)(p + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16
}

//_unchecked_calloc does not check for potential overflow of nm*sz
STATIC_INLINE void *_unchecked_calloc(size_t nm, size_t sz) {
size_t nmsz = nm*sz;
int64_t *p = (int64_t *)jl_gc_counted_calloc(nmsz + JL_SMALL_BYTE_ALIGNMENT, 1);
if (p == NULL)
return NULL;
p[0] = nmsz;
return (void *)(p + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16
}

JL_DLLEXPORT void *jl_calloc(size_t nm, size_t sz)
{
if (nm > SSIZE_MAX/sz - JL_SMALL_BYTE_ALIGNMENT)
return NULL;
return _unchecked_calloc(nm, sz);
}

JL_DLLEXPORT void jl_free(void *p)
{
if (p != NULL) {
int64_t *pp = (int64_t *)p - 2;
size_t sz = pp[0];
jl_gc_counted_free_with_size(pp, sz + JL_SMALL_BYTE_ALIGNMENT);
}
}

JL_DLLEXPORT void *jl_realloc(void *p, size_t sz)
{
int64_t *pp;
size_t szold;
if (p == NULL) {
pp = NULL;
szold = 0;
}
else {
pp = (int64_t *)p - 2;
szold = pp[0] + JL_SMALL_BYTE_ALIGNMENT;
}
int64_t *pnew = (int64_t *)jl_gc_counted_realloc_with_old_size(pp, szold, sz + JL_SMALL_BYTE_ALIGNMENT);
if (pnew == NULL)
return NULL;
pnew[0] = sz;
return (void *)(pnew + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16
}

// allocator entry points

JL_DLLEXPORT jl_value_t *(jl_gc_alloc)(jl_ptls_t ptls, size_t sz, void *ty)
{
return jl_gc_alloc_(ptls, sz, ty);
}

// =========================================================================== //
// Generic Memory
// =========================================================================== //

size_t jl_genericmemory_nbytes(jl_genericmemory_t *m) JL_NOTSAFEPOINT
{
const jl_datatype_layout_t *layout = ((jl_datatype_t*)jl_typetagof(m))->layout;
size_t sz = layout->size * m->length;
if (layout->flags.arrayelem_isunion)
// account for isbits Union array selector bytes
sz += m->length;
return sz;
}

// tracking Memorys with malloc'd storage
void jl_gc_track_malloced_genericmemory(jl_ptls_t ptls, jl_genericmemory_t *m, int isaligned){
// This is **NOT** a GC safe point.
mallocmemory_t *ma;
if (ptls->gc_tls_common.heap.mafreelist == NULL) {
ma = (mallocmemory_t*)malloc_s(sizeof(mallocmemory_t));
}
else {
ma = ptls->gc_tls_common.heap.mafreelist;
ptls->gc_tls_common.heap.mafreelist = ma->next;
}
ma->a = (jl_genericmemory_t*)((uintptr_t)m | !!isaligned);
ma->next = ptls->gc_tls_common.heap.mallocarrays;
ptls->gc_tls_common.heap.mallocarrays = ma;
}

// =========================================================================== //
// GC Debug
// =========================================================================== //

int gc_slot_to_fieldidx(void *obj, void *slot, jl_datatype_t *vt) JL_NOTSAFEPOINT
{
int nf = (int)jl_datatype_nfields(vt);
for (int i = 1; i < nf; i++) {
if (slot < (void*)((char*)obj + jl_field_offset(vt, i)))
return i - 1;
}
return nf - 1;
}

int gc_slot_to_arrayidx(void *obj, void *_slot) JL_NOTSAFEPOINT
{
char *slot = (char*)_slot;
jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(obj);
char *start = NULL;
size_t len = 0;
size_t elsize = sizeof(void*);
if (vt == jl_module_type) {
jl_module_t *m = (jl_module_t*)obj;
start = (char*)m->usings.items;
len = module_usings_length(m);
elsize = sizeof(struct _jl_module_using);
}
else if (vt == jl_simplevector_type) {
start = (char*)jl_svec_data(obj);
len = jl_svec_len(obj);
}
if (slot < start || slot >= start + elsize * len)
return -1;
return (slot - start) / elsize;
}

// =========================================================================== //
// GC Control
// =========================================================================== //

JL_DLLEXPORT uint32_t jl_get_gc_disable_counter(void) {
return jl_atomic_load_acquire(&jl_gc_disable_counter);
}

JL_DLLEXPORT int jl_gc_is_enabled(void)
{
jl_ptls_t ptls = jl_current_task->ptls;
return !ptls->disable_gc;
}

int gc_logging_enabled = 0;

JL_DLLEXPORT void jl_enable_gc_logging(int enable) {
gc_logging_enabled = enable;
}

JL_DLLEXPORT int jl_is_gc_logging_enabled(void) {
return gc_logging_enabled;
}


// collector entry point and control
_Atomic(uint32_t) jl_gc_disable_counter = 1;

JL_DLLEXPORT int jl_gc_enable(int on)
{
jl_ptls_t ptls = jl_current_task->ptls;
int prev = !ptls->disable_gc;
ptls->disable_gc = (on == 0);
if (on && !prev) {
// disable -> enable
if (jl_atomic_fetch_add(&jl_gc_disable_counter, -1) == 1) {
gc_num.allocd += gc_num.deferred_alloc;
gc_num.deferred_alloc = 0;
}
}
else if (prev && !on) {
// enable -> disable
jl_atomic_fetch_add(&jl_gc_disable_counter, 1);
// check if the GC is running and wait for it to finish
jl_gc_safepoint_(ptls);
}
return prev;
}

// =========================================================================== //
// MISC
// =========================================================================== //

JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value)
{
jl_ptls_t ptls = jl_current_task->ptls;
return jl_gc_new_weakref_th(ptls, value);
}

const uint64_t _jl_buff_tag[3] = {0x4eadc0004eadc000ull, 0x4eadc0004eadc000ull, 0x4eadc0004eadc000ull}; // aka 0xHEADER00
JL_DLLEXPORT uintptr_t jl_get_buff_tag(void) JL_NOTSAFEPOINT
{
Expand Down
12 changes: 12 additions & 0 deletions src/gc-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ extern jl_gc_callback_list_t *gc_cblist_notify_gc_pressure;
// malloc wrappers, aligned allocation
// =========================================================================== //

// data structure for tracking malloc'd genericmemory.
typedef struct _mallocmemory_t {
jl_genericmemory_t *a; // lowest bit is tagged if this is aligned memory
struct _mallocmemory_t *next;
} mallocmemory_t;

#if defined(_OS_WINDOWS_)
STATIC_INLINE void *jl_malloc_aligned(size_t sz, size_t align)
{
Expand Down Expand Up @@ -173,4 +179,10 @@ JL_DLLEXPORT void jl_finalize_th(jl_task_t *ct, jl_value_t *o);
extern int gc_n_threads;
extern jl_ptls_t* gc_all_tls_states;

// =========================================================================== //
// Logging
// =========================================================================== //

extern int gc_logging_enabled;

#endif // JL_GC_COMMON_H
42 changes: 0 additions & 42 deletions src/gc-debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -1105,48 +1105,6 @@ void gc_count_pool(void)
jl_safe_printf("************************\n");
}

int gc_slot_to_fieldidx(void *obj, void *slot, jl_datatype_t *vt) JL_NOTSAFEPOINT
{
int nf = (int)jl_datatype_nfields(vt);
for (int i = 1; i < nf; i++) {
if (slot < (void*)((char*)obj + jl_field_offset(vt, i)))
return i - 1;
}
return nf - 1;
}

int gc_slot_to_arrayidx(void *obj, void *_slot) JL_NOTSAFEPOINT
{
char *slot = (char*)_slot;
jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(obj);
char *start = NULL;
size_t len = 0;
size_t elsize = sizeof(void*);
if (vt == jl_module_type) {
jl_module_t *m = (jl_module_t*)obj;
start = (char*)m->usings.items;
len = module_usings_length(m);
elsize = sizeof(struct _jl_module_using);
}
else if (vt == jl_simplevector_type) {
start = (char*)jl_svec_data(obj);
len = jl_svec_len(obj);
}
if (slot < start || slot >= start + elsize * len)
return -1;
return (slot - start) / elsize;
}

static int gc_logging_enabled = 0;

JL_DLLEXPORT void jl_enable_gc_logging(int enable) {
gc_logging_enabled = enable;
}

JL_DLLEXPORT int jl_is_gc_logging_enabled(void) {
return gc_logging_enabled;
}

void _report_gc_finished(uint64_t pause, uint64_t freed, int full, int recollect, int64_t live_bytes) JL_NOTSAFEPOINT {
if (!gc_logging_enabled) {
return;
Expand Down
37 changes: 9 additions & 28 deletions src/gc-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ JL_DLLEXPORT void jl_gc_set_max_memory(uint64_t max_mem);
// should run a collection cycle again (e.g. a full mark right after a full sweep to ensure
// we do a full heap traversal).
JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection);
// Returns whether the thread with `tid` is a collector thread
JL_DLLEXPORT int gc_is_collector_thread(int tid) JL_NOTSAFEPOINT;

// ========================================================================= //
// Metrics
Expand Down Expand Up @@ -130,6 +132,13 @@ JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void);
// Allocation
// ========================================================================= //

// On GCC, this function is inlined when sz is constant (see julia_internal.h)
// In general, this function should implement allocation and should use the specific GC's logic
// to decide whether to allocate a small or a large object. Finally, note that this function
// **must** also set the type of the returning object to be `ty`. The type `ty` may also be used to record
// an allocation of that type in the allocation profiler.
struct _jl_value_t *jl_gc_alloc_(struct _jl_tls_states_t * ptls, size_t sz, void *ty);

// Allocates small objects and increments Julia allocation counterst. Size of the object
// header must be included in the object size. The (possibly unused in some implementations)
// offset to the arena in which we're allocating is passed in the second parameter, and the
Expand Down Expand Up @@ -157,26 +166,6 @@ JL_DLLEXPORT void *jl_gc_counted_calloc(size_t nm, size_t sz);
JL_DLLEXPORT void jl_gc_counted_free_with_size(void *p, size_t sz);
// Wrapper around Libc realloc that updates Julia allocation counters.
JL_DLLEXPORT void *jl_gc_counted_realloc_with_old_size(void *p, size_t old, size_t sz);
// Wrapper around Libc malloc that allocates a memory region with a few additional machine
// words before the actual payload that are used to record the size of the requested
// allocation. Also updates Julia allocation counters. The function returns a pointer to the
// payload as a result of the allocation.
JL_DLLEXPORT void *jl_malloc(size_t sz);
// Wrapper around Libc calloc that allocates a memory region with a few additional machine
// words before the actual payload that are used to record the size of the requested
// allocation. Also updates Julia allocation counters. The function returns a pointer to the
// payload as a result of the allocation.
JL_DLLEXPORT void *jl_calloc(size_t nm, size_t sz);
// Wrapper around Libc free that takes a pointer to the payload of a memory region allocated
// with jl_malloc or jl_calloc, and uses the size information stored in the first machine
// words of the memory buffer update Julia allocation counters, and then frees the
// corresponding memory buffer.
JL_DLLEXPORT void jl_free(void *p);
// Wrapper around Libc realloc that takes a memory region allocated with jl_malloc or
// jl_calloc, and uses the size information stored in the first machine words of the memory
// buffer to update Julia allocation counters, reallocating the corresponding memory buffer
// in the end.
JL_DLLEXPORT void *jl_realloc(void *p, size_t sz);
// Wrapper around Libc malloc that's used to dynamically allocate memory for Arrays and
// Strings. It increments Julia allocation counters and should check whether we're close to
// the Julia heap target, and therefore, whether we should run a collection. Note that this
Expand All @@ -190,14 +179,6 @@ JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz);
// thread-local allocator of the thread referenced by the first jl_ptls_t argument.
JL_DLLEXPORT struct _jl_weakref_t *jl_gc_new_weakref_th(struct _jl_tls_states_t *ptls,
struct _jl_value_t *value);
// Allocates a new weak-reference, assigns its value and increments Julia allocation
// counters. If thread-local allocators are used, then this function should allocate in the
// thread-local allocator of the current thread.
JL_DLLEXPORT struct _jl_weakref_t *jl_gc_new_weakref(struct _jl_value_t *value);
// Allocates an object whose size is specified by the function argument and increments Julia
// allocation counters. If thread-local allocators are used, then this function should
// allocate in the thread-local allocator of the current thread.
JL_DLLEXPORT struct _jl_value_t *jl_gc_allocobj(size_t sz);
// Permanently allocates a memory slot of the size specified by the first parameter. This
// block of memory is allocated in an immortal region that is never swept. The second
// parameter specifies whether the memory should be filled with zeros. The third and fourth
Expand Down
Loading