21 Star 49 Fork 0

Gitee 极速下载/julia-language

Create your Gitee Account
Explore and code with more than 13.5 million developers,Free private repositories !:)
Sign up
文件
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/JuliaLang/julia
Clone or Download
gcext.c 16.80 KB
Copy Edit Raw Blame History
Ian Butterworth authored 2025-01-28 11:43 +08:00 . Default to 1 interactive thread (#57087)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
// This file is a part of Julia. License is MIT: https://julialang.org/license
#include <stddef.h>
#include <stdio.h>
#include "julia.h"
#include "julia_gcext.h"
// Comparing pointers in C without triggering undefined behavior
// can be difficult. As the GC already assumes that the memory
// range goes from 0 to 2^k-1 (region tables), we simply convert
// to uintptr_t and compare those.
#ifdef __cplusplus
extern "C" {
#endif
static inline int cmp_ptr(void *p, void *q)
{
uintptr_t paddr = (uintptr_t)p;
uintptr_t qaddr = (uintptr_t)q;
if (paddr < qaddr)
return -1;
else if (paddr > qaddr)
return 1;
else
return 0;
}
static inline int lt_ptr(void *a, void *b)
{
return (uintptr_t)a < (uintptr_t)b;
}
/* align pointer to full word if misaligned */
static inline void *align_ptr(void *p)
{
uintptr_t u = (uintptr_t)p;
u &= ~(sizeof(p) - 1);
return (void *)u;
}
// We use treaps -- a form of balanced trees -- to be able to
// find allocations based on their address.
typedef struct treap_t {
struct treap_t *left, *right;
size_t prio;
void *addr;
size_t size;
} treap_t;
static treap_t *treap_free_list;
treap_t *alloc_treap(void)
{
treap_t *result;
if (treap_free_list) {
result = treap_free_list;
treap_free_list = treap_free_list->right;
}
else
result = malloc(sizeof(treap_t));
result->left = NULL;
result->right = NULL;
result->addr = NULL;
result->size = 0;
return result;
}
void free_treap(treap_t *t)
{
t->right = treap_free_list;
treap_free_list = t;
}
static inline int test_bigval_range(treap_t *node, void *p)
{
char *l = node->addr;
char *r = l + node->size;
if (lt_ptr(p, l))
return -1;
if (!lt_ptr(p, r))
return 1;
return 0;
}
#define L(t) ((t)->left)
#define R(t) ((t)->right)
static inline void treap_rot_right(treap_t **treap)
{
/* t l */
/* / \ / \ */
/* l r --> a t */
/* / \ / \ */
/* a b b r */
treap_t *t = *treap;
treap_t *l = L(t);
treap_t *a = L(l);
treap_t *b = R(l);
L(l) = a;
R(l) = t;
L(t) = b;
*treap = l;
}
static inline void treap_rot_left(treap_t **treap)
{
/* t r */
/* / \ / \ */
/* l r --> t b */
/* / \ / \ */
/* a b l a */
treap_t *t = *treap;
treap_t *r = R(t);
treap_t *a = L(r);
treap_t *b = R(r);
L(r) = t;
R(r) = b;
R(t) = a;
*treap = r;
}
static treap_t *treap_find(treap_t *treap, void *p)
{
while (treap) {
int c = test_bigval_range(treap, p);
if (c == 0)
return treap;
else if (c < 0)
treap = L(treap);
else
treap = R(treap);
}
return NULL;
}
static void treap_insert(treap_t **treap, treap_t *val)
{
treap_t *t = *treap;
if (t == NULL) {
L(val) = NULL;
R(val) = NULL;
*treap = val;
}
else {
int c = cmp_ptr(val->addr, t->addr);
if (c < 0) {
treap_insert(&L(t), val);
if (L(t)->prio > t->prio) {
treap_rot_right(treap);
}
}
else if (c > 0) {
treap_insert(&R(t), val);
if (R(t)->prio > t->prio) {
treap_rot_left(treap);
}
}
}
}
static void treap_delete_node(treap_t **treap)
{
for (;;) {
treap_t *t = *treap;
if (L(t) == NULL) {
*treap = R(t);
free_treap(t);
break;
}
else if (R(t) == NULL) {
*treap = L(t);
free_treap(t);
break;
}
else {
if (L(t)->prio > R(t)->prio) {
treap_rot_right(treap);
treap = &R(*treap);
}
else {
treap_rot_left(treap);
treap = &L(*treap);
}
}
}
}
static int treap_delete(treap_t **treap, void *addr)
{
while (*treap != NULL) {
int c = cmp_ptr(addr, (*treap)->addr);
if (c == 0) {
treap_delete_node(treap);
return 1;
}
else if (c < 0) {
treap = &L(*treap);
}
else {
treap = &R(*treap);
}
}
return 0;
}
static uint64_t xorshift_rng_state = 1;
static uint64_t xorshift_rng(void)
{
uint64_t x = xorshift_rng_state;
x = x ^ (x >> 12);
x = x ^ (x << 25);
x = x ^ (x >> 27);
xorshift_rng_state = x;
return x * (uint64_t)0x2545F4914F6CDD1DUL;
}
static treap_t *bigvals;
static size_t bigval_startoffset;
// Hooks to allocate and free external objects (bigval_t's).
void alloc_bigval(void *addr, size_t size)
{
treap_t *node = alloc_treap();
node->addr = addr;
node->size = size;
node->prio = xorshift_rng();
treap_insert(&bigvals, node);
}
void free_bigval(void *p)
{
if (p) {
treap_delete(&bigvals, p);
}
}
// Auxiliary roots. These serve no special purpose, except
// allowing us to verify that root scanning works.
#define NAUXROOTS 1024
static jl_value_t *aux_roots[NAUXROOTS];
size_t gc_counter_full, gc_counter_inc;
jl_value_t *get_aux_root(size_t n)
{
if (n >= NAUXROOTS)
jl_error("get_aux_root: index out of range");
return aux_roots[n];
}
void set_aux_root(size_t n, jl_value_t *val)
{
if (n >= NAUXROOTS)
jl_error("set_aux_root: index out of range");
aux_roots[n] = val;
}
size_t get_gc_counter(int full)
{
if (full)
return gc_counter_full;
else
return gc_counter_inc;
}
static size_t obj_sweeps = 0;
size_t get_obj_sweeps()
{
return obj_sweeps;
}
typedef struct {
size_t size;
size_t capacity;
jl_value_t *data[1];
} dynstack_t;
static jl_datatype_t *datatype_stack_internal;
static jl_datatype_t *datatype_stack_external;
static jl_datatype_t *datatype_stack;
static jl_ptls_t ptls;
static size_t gc_alloc_size(jl_value_t *val)
{
size_t size;
if (jl_typeis(val, datatype_stack))
size = sizeof(jl_value_t *);
else if (jl_typeis(val, datatype_stack_internal) || jl_typeis(val, datatype_stack_external))
size = offsetof(dynstack_t, data) +
((dynstack_t *)val)->capacity * sizeof(jl_value_t *);
else if (jl_typeis(val, jl_string_type)) {
size = jl_string_len(val) + sizeof(size_t) + 1;
// Round up to whole word size
if (size % sizeof(void *) != 0)
size += sizeof(void *) - size % sizeof(void *);
}
else
size = 0;
return size;
}
int internal_obj_scan(jl_value_t *val)
{
// FIXME: `jl_gc_internal_obj_base_ptr` is not allowed to be called from outside GC
if (jl_gc_internal_obj_base_ptr(val) == val) {
size_t size = gc_alloc_size(val);
char *addr = (char *)val;
for (size_t i = 0; i <= size; i++) {
if (jl_gc_internal_obj_base_ptr(addr + i) != val)
return 0;
}
return 1;
}
else {
treap_t *node = treap_find(bigvals, val);
if (!node)
return 0;
char *addr = node->addr;
if ((jl_value_t *)addr != val)
return 0;
size_t size = node->size;
for (size_t i = 0; i <= size; i++) {
if (treap_find(bigvals, addr + i) != node)
return 0;
}
return 1;
}
}
dynstack_t *allocate_stack_mem(size_t capacity)
{
size_t size = offsetof(dynstack_t, data) + capacity * sizeof(jl_value_t *);
jl_datatype_t *type = datatype_stack_internal;
if (size > jl_gc_max_internal_obj_size())
type = datatype_stack_external;
dynstack_t *result = (dynstack_t *)jl_gc_alloc_typed(ptls, size, type);
result->size = 0;
result->capacity = capacity;
return result;
}
void check_stack(const char *name, jl_value_t *p)
{
if (jl_typeis(p, datatype_stack))
return;
jl_type_error(name, (jl_value_t *)datatype_stack, p);
}
void check_stack_notempty(const char *name, jl_value_t *p)
{
check_stack(name, p);
dynstack_t *stk = *(dynstack_t **)p;
if (stk->size == 0)
jl_errorf("%s: dynstack_t empty", name);
}
// Stacks use double indirection in order to be resizable.
// The outer object is a single word containing a pointer to
// a `dynstack_t`, which can contain a variable number of
// Julia objects; the `capacity` field denotes the number of objects
// that can be stored without resizing storage, the `size` field
// denotes the actual number of objects. GC scanning should ignore
// any storage past those.
// Return the type of stacks
jl_value_t *stk_type()
{
return (jl_value_t *)datatype_stack;
}
// Create a new stack object
jl_value_t *stk_make()
{
jl_value_t *hdr =
jl_gc_alloc_typed(ptls, sizeof(jl_value_t *), datatype_stack);
JL_GC_PUSH1(hdr);
*(dynstack_t **)hdr = NULL;
dynstack_t *stk = allocate_stack_mem(8);
*(dynstack_t **)hdr = stk;
jl_gc_schedule_foreign_sweepfunc(ptls, (jl_value_t *)(stk));
JL_GC_POP();
return hdr;
}
// Return a pointer to the inner `dynstack_t` struct.
jl_value_t *stk_blob(jl_value_t *s)
{
return (jl_value_t *)(*(dynstack_t **)s);
}
// Push `v` on `s`.
void stk_push(jl_value_t *s, jl_value_t *v)
{
check_stack("push", s);
dynstack_t *stk = *(dynstack_t **)s;
if (stk->size < stk->capacity) {
stk->data[stk->size++] = v;
jl_gc_wb((jl_value_t *)stk, v);
}
else {
dynstack_t *newstk = allocate_stack_mem(stk->capacity * 3 / 2 + 1);
newstk->size = stk->size;
memcpy(newstk->data, stk->data, sizeof(jl_value_t *) * stk->size);
*(dynstack_t **)s = newstk;
newstk->data[newstk->size++] = v;
jl_gc_schedule_foreign_sweepfunc(ptls, (jl_value_t *)(newstk));
jl_gc_wb_back((jl_value_t *)newstk);
jl_gc_wb(s, (jl_value_t *)newstk);
}
}
// Return top value from `s`. Raise error if not empty.
jl_value_t *stk_top(jl_value_t *s)
{
check_stack_notempty("top", s);
dynstack_t *stk = *(dynstack_t **)s;
return stk->data[stk->size - 1];
}
// Pop a value from `s` and return it. Raise error if not empty.
jl_value_t *stk_pop(jl_value_t *s)
{
check_stack_notempty("pop", s);
dynstack_t *stk = *(dynstack_t **)s;
stk->size--;
return stk->data[stk->size];
}
// Number of objects on the stack.
size_t stk_size(jl_value_t *s)
{
check_stack("empty", s);
dynstack_t *stk = *(dynstack_t **)s;
return stk->size;
}
static jl_module_t *module;
// Mark auxiliary roots.
void root_scanner(int full)
{
for (int i = 0; i < NAUXROOTS; i++) {
if (aux_roots[i])
jl_gc_mark_queue_obj(ptls, aux_roots[i]);
}
}
// Test stack direction
static int is_lower_stack_frame(volatile char *frame_addr) {
volatile char buf[1];
return (uintptr_t) buf < (uintptr_t) frame_addr;
}
typedef volatile int (*volatile test_frame_func)(volatile char *frame_addr);
// To prevent inlining, we make this a volatile function pointer.
static test_frame_func is_lower_stack_frame_ptr =
(test_frame_func) is_lower_stack_frame;
static int stack_grows_down(void) {
volatile char buf[1];
return is_lower_stack_frame_ptr(buf);
}
void task_scanner(jl_task_t *task, int root_task)
{
int var_on_frame;
// The task scanner is not necessary for liveness, as the
// corresponding task stack is already part of the stack.
// Its purpose is simply to test that the task scanner
// doing actual work does not trigger a problem.
char *start_stack;
char *end_stack;
char *total_start_stack;
char *total_end_stack;
jl_active_task_stack(task, &start_stack, &end_stack, &total_start_stack, &total_end_stack);
// this is the live stack of a thread. Is it ours?
if (start_stack && task == (jl_task_t*)jl_get_current_task()) {
if (!(lt_ptr(start_stack, &var_on_frame) && lt_ptr(&var_on_frame, end_stack))) {
// error, current stack frame must be on the live stack.
jl_error("stack frame not part of the current task");
}
}
if (start_stack) {
void **start = (void **)start_stack;
void **end = (void **)end_stack;
while (start < end) {
void *p = *start++;
void *q = jl_gc_internal_obj_base_ptr(p);
if (q) {
jl_gc_mark_queue_obj(ptls, q);
}
}
}
}
// Hooks to run before and after GC.
//
// As a simple example, we only track counters for full
// and partial collections.
void pre_gc_func(int full)
{
if (full)
gc_counter_full++;
else
gc_counter_inc++;
}
void post_gc_func(int full) {}
// Mark the outer stack object (containing only a pointer to the data).
uintptr_t mark_stack(jl_ptls_t ptls, jl_value_t *p)
{
if (!*(void **)p)
return 0;
return jl_gc_mark_queue_obj(ptls, *(jl_value_t **)p) != 0;
}
// Mark the actual stack data.
// This is used both for `StackData` and `StackDataLarge`.
uintptr_t mark_stack_data(jl_ptls_t ptls, jl_value_t *p)
{
dynstack_t *stk = (dynstack_t *)p;
// Alternate between two marking approaches for testing so
// that we test both.
if (gc_counter_full & 1) {
jl_gc_mark_queue_objarray(ptls, p, stk->data, stk->size);
return 0;
}
else {
uintptr_t n = 0;
for (size_t i = 0; i < stk->size; i++) {
if (jl_gc_mark_queue_obj(ptls, stk->data[i]))
n++;
}
return n;
}
}
void sweep_stack_data(jl_value_t *p)
{
obj_sweeps++;
dynstack_t *stk = (dynstack_t *)p;
if (stk->size > stk->capacity) {
assert(0 && "internal error during sweeping");
abort();
}
}
// Safely execute Julia code
jl_value_t *checked_eval_string(const char *code)
{
jl_value_t *result = jl_eval_string(code);
if (jl_exception_occurred()) {
// none of these allocate, so a gc-root (JL_GC_PUSH) is not necessary
jl_call2(
jl_get_function(jl_base_module, "showerror"),
jl_stderr_obj(),
jl_exception_occurred());
jl_printf(jl_stderr_stream(), "\n");
jl_atexit_hook(1);
exit(1);
}
assert(result && "Missing return value but no exception occurred!");
return result;
}
void abort_with_error(int full)
{
abort();
}
int main()
{
// Install callbacks. This should happen before `jl_init()` and
// before any GC is called.
jl_gc_set_cb_notify_external_alloc(alloc_bigval, 1);
jl_gc_set_cb_notify_external_free(free_bigval, 1);
// single threaded mode
// Note: with -t1,1 a signal 10 occurs in task_scanner
jl_options.nthreadpools = 1;
jl_options.nthreads = 1;
int16_t ntpp[] = {jl_options.nthreads};
jl_options.nthreads_per_pool = ntpp;
jl_init();
if (jl_gc_enable_conservative_gc_support() < 0)
abort();
ptls = jl_get_ptls_states();
jl_gc_set_cb_root_scanner(root_scanner, 1);
jl_gc_set_cb_task_scanner(task_scanner, 1);
jl_gc_set_cb_pre_gc(pre_gc_func, 1);
jl_gc_set_cb_post_gc(post_gc_func, 1);
// Test that deregistration works
jl_gc_set_cb_root_scanner(abort_with_error, 1);
jl_gc_set_cb_root_scanner(abort_with_error, 0);
// Create module to store types in.
module = jl_new_module(jl_symbol("TestGCExt"), jl_main_module);
jl_set_const(jl_main_module, jl_symbol("TestGCExt"), (jl_value_t *)module);
// Define Julia types for our stack implementation.
datatype_stack = jl_new_foreign_type(
jl_symbol("Stack"), module, jl_any_type, mark_stack, NULL, 1, 0);
jl_set_const(module, jl_symbol("Stack"), (jl_value_t *)datatype_stack);
datatype_stack_internal = jl_new_foreign_type(
jl_symbol("StackData"),
module,
jl_any_type,
mark_stack_data,
sweep_stack_data,
1,
0);
jl_set_const(
module,
jl_symbol("StackData"),
(jl_value_t *)datatype_stack_internal);
datatype_stack_external = jl_new_foreign_type(
jl_symbol("StackDataLarge"),
module,
jl_any_type,
mark_stack_data,
sweep_stack_data,
1,
1);
jl_set_const(
module,
jl_symbol("StackDataLarge"),
(jl_value_t *)datatype_stack_external);
// Remember the offset of external objects
bigval_startoffset = jl_gc_external_obj_hdr_size();
// Run the actual tests
checked_eval_string(
"let dir = dirname(unsafe_string(Base.JLOptions().julia_bin))\n"
// disable the package manager
" ENV[\"JULIA_PKGDIR\"] = joinpath(dir, \"disabled\")\n"
"end");
checked_eval_string(
"module LocalTest\n"
" include(\"LocalTest.jl\")\n"
"end");
}
#ifdef __cplusplus
}
#endif
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/mirrors/julia-language.git
git@gitee.com:mirrors/julia-language.git
mirrors
julia-language
julia-language
master

Search