/src/racket/src/thread.c
C | 7989 lines | 6099 code | 1374 blank | 516 comment | 1097 complexity | 53b08be251330184d36c3e02c55b6546 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
Large files files are truncated, but you can click here to view the full file
- /*
- Racket
- Copyright (c) 2004-2011 PLT Scheme Inc.
- Copyright (c) 1995-2001 Matthew Flatt
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301 USA.
- */
- /* This file implements Racket threads.
- Usually, Racket threads are implemented by copying the stack.
- The scheme_thread_block() function is called occasionally by the
- evaluator so that the current thread can be swapped out.
- do_swap_thread() performs the actual swap. Threads can also be
- implemented by the OS; the bottom part of this file contains
- OS-specific thread code.
- Much of the work in thread management is knowning when to go to
- sleep, to be nice to the OS outside of Racket. The rest of the
- work is implementing custodians (called "custodians" in the code),
- parameters, and wills. */
- /* Some copilers don't like re-def of GC_malloc in schemef.h: */
- #ifndef MZ_PRECISE_GC
- # define SCHEME_NO_GC_PROTO
- #endif
- #include "schpriv.h"
- #include "schmach.h"
- #include "schgc.h"
- #ifdef MZ_USE_FUTURES
- # include "future.h"
- #endif
- #ifndef PALMOS_STUFF
- # include <time.h>
- #endif
- #ifdef FILES_HAVE_FDS
- # include <sys/types.h>
- # include <sys/time.h>
- # ifdef SELECT_INCLUDE
- # include <sys/select.h>
- # endif
- # ifdef USE_BEOS_SOCKET_INCLUDE
- # include <be/net/socket.h>
- # endif
- #endif
- #ifdef USE_WINSOCK_TCP
- # ifdef USE_TCP
- # include <winsock.h>
- # endif
- #endif
- #ifdef USE_BEOS_PORT_THREADS
- # include <be/net/socket.h>
- #endif
- #ifdef USE_STACKAVAIL
- # include <malloc.h>
- #endif
- #ifdef UNISTD_INCLUDE
- # include <unistd.h>
- #endif
- #ifndef SIGNMZTHREAD
- # define SIGMZTHREAD SIGUSR2
- #endif
- #if defined(WINDOWS_PROCESSES) || defined(WINDOWS_FILE_HANDLES)
- # include <windows.h>
- THREAD_LOCAL_DECL(extern void *scheme_break_semaphore;)
- #endif
- #if defined(FILES_HAVE_FDS) \
- || defined(USE_BEOS_PORT_THREADS) \
- || (defined(USE_WINSOCK_TCP) && defined(USE_TCP)) \
- || (defined(WINDOWS_PROCESSES) || defined(WINDOWS_FILE_HANDLES))
- # define USING_FDS
- # if (!defined(USE_WINSOCK_TCP) || !defined(USE_TCP)) && !defined(FILES_HAVE_FDS)
- # include <sys/types.h>
- # endif
- #endif
- #include "schfd.h"
- #define DEFAULT_INIT_STACK_SIZE 1000
- #define MAX_INIT_STACK_SIZE 100000
- #ifdef SGC_STD_DEBUGGING
- # define SENORA_GC_NO_FREE
- #endif
- /* If a finalization callback invokes Scheme code,
- we can end up with a thread swap in the middle of a thread
- swap (where the outer swap was interrupted by GC). The
- following is a debugging flag to help detect and fix
- such problems. */
- #define WATCH_FOR_NESTED_SWAPS 0
- #if WATCH_FOR_NESTED_SWAPS
- static int swapping = 0;
- #endif
- extern void scheme_gmp_tls_init(intptr_t *s);
- extern void *scheme_gmp_tls_load(intptr_t *s);
- extern void scheme_gmp_tls_unload(intptr_t *s, void *p);
- extern void scheme_gmp_tls_snapshot(intptr_t *s, intptr_t *save);
- extern void scheme_gmp_tls_restore_snapshot(intptr_t *s, void *data, intptr_t *save, int do_free);
- static void check_ready_break();
- THREAD_LOCAL_DECL(extern int scheme_num_read_syntax_objects);
- THREAD_LOCAL_DECL(extern intptr_t scheme_hash_request_count);
- THREAD_LOCAL_DECL(extern intptr_t scheme_hash_iteration_count);
- THREAD_LOCAL_DECL(extern intptr_t scheme_code_page_total);
- #ifdef MZ_USE_JIT
- extern int scheme_jit_malloced;
- #else
- # define scheme_jit_malloced 0
- #endif
- /*========================================================================*/
- /* local variables and prototypes */
- /*========================================================================*/
- #define INIT_TB_SIZE 20
- #ifndef MZ_THREAD_QUANTUM_USEC
- # define MZ_THREAD_QUANTUM_USEC 10000
- #endif
- THREAD_LOCAL_DECL(static int buffer_init_size);
- THREAD_LOCAL_DECL(Scheme_Thread *scheme_current_thread = NULL);
- THREAD_LOCAL_DECL(Scheme_Thread *scheme_main_thread = NULL);
- THREAD_LOCAL_DECL(Scheme_Thread *scheme_first_thread = NULL);
- XFORM_NONGCING Scheme_Thread *scheme_get_current_thread() { return scheme_current_thread; }
- XFORM_NONGCING intptr_t scheme_get_multiple_count() { return scheme_current_thread->ku.multiple.count; }
- XFORM_NONGCING Scheme_Object **scheme_get_multiple_array() { return scheme_current_thread->ku.multiple.array; }
- XFORM_NONGCING void scheme_set_current_thread_ran_some() { scheme_current_thread->ran_some = 1; }
- THREAD_LOCAL_DECL(Scheme_Thread_Set *scheme_thread_set_top);
- THREAD_LOCAL_DECL(static int num_running_threads); /* not counting original */
- #ifdef LINK_EXTENSIONS_BY_TABLE
- Scheme_Thread **scheme_current_thread_ptr;
- volatile int *scheme_fuel_counter_ptr;
- #endif
- THREAD_LOCAL_DECL(static int swap_no_setjmp = 0);
- THREAD_LOCAL_DECL(static int thread_swap_count);
- THREAD_LOCAL_DECL(int scheme_did_gc_count);
- SHARED_OK static int init_load_on_demand = 1;
- #ifdef RUNSTACK_IS_GLOBAL
- THREAD_LOCAL_DECL(Scheme_Object **scheme_current_runstack_start);
- THREAD_LOCAL_DECL(Scheme_Object **scheme_current_runstack);
- THREAD_LOCAL_DECL(MZ_MARK_STACK_TYPE scheme_current_cont_mark_stack);
- THREAD_LOCAL_DECL(MZ_MARK_POS_TYPE scheme_current_cont_mark_pos);
- #endif
- THREAD_LOCAL_DECL(static Scheme_Custodian *main_custodian);
- THREAD_LOCAL_DECL(static Scheme_Custodian *last_custodian);
- THREAD_LOCAL_DECL(static Scheme_Hash_Table *limited_custodians = NULL);
- READ_ONLY static Scheme_Object *initial_inspector;
- #ifndef MZ_PRECISE_GC
- static int cust_box_count, cust_box_alloc;
- static Scheme_Custodian_Box **cust_boxes;
- # ifndef USE_SENORA_GC
- extern int GC_is_marked(void *);
- # endif
- #endif
- READ_ONLY Scheme_At_Exit_Proc replacement_at_exit;
- ROSYM Scheme_Object *scheme_parameterization_key;
- ROSYM Scheme_Object *scheme_exn_handler_key;
- ROSYM Scheme_Object *scheme_break_enabled_key;
- THREAD_LOCAL_DECL(intptr_t scheme_total_gc_time);
- THREAD_LOCAL_DECL(static intptr_t start_this_gc_time);
- THREAD_LOCAL_DECL(static intptr_t end_this_gc_time);
- static void get_ready_for_GC(void);
- static void done_with_GC(void);
- #ifdef MZ_PRECISE_GC
- static void inform_GC(int master_gc, int major_gc, intptr_t pre_used, intptr_t post_used,
- intptr_t pre_admin, intptr_t post_admin);
- #endif
- THREAD_LOCAL_DECL(static volatile short delayed_break_ready);
- THREAD_LOCAL_DECL(static Scheme_Thread *main_break_target_thread);
- THREAD_LOCAL_DECL(Scheme_Sleep_Proc scheme_place_sleep);
- HOOK_SHARED_OK void (*scheme_sleep)(float seconds, void *fds);
- HOOK_SHARED_OK void (*scheme_notify_multithread)(int on);
- HOOK_SHARED_OK void (*scheme_wakeup_on_input)(void *fds);
- HOOK_SHARED_OK int (*scheme_check_for_break)(void);
- HOOK_SHARED_OK Scheme_On_Atomic_Timeout_Proc scheme_on_atomic_timeout;
- HOOK_SHARED_OK static int atomic_timeout_auto_suspend;
- HOOK_SHARED_OK static int atomic_timeout_atomic_level;
- THREAD_LOCAL_DECL(struct Scheme_GC_Pre_Post_Callback_Desc *gc_prepost_callback_descs);
- ROSYM static Scheme_Object *read_symbol, *write_symbol, *execute_symbol, *delete_symbol, *exists_symbol;
- ROSYM static Scheme_Object *client_symbol, *server_symbol;
- THREAD_LOCAL_DECL(static int do_atomic = 0);
- THREAD_LOCAL_DECL(static int missed_context_switch = 0);
- THREAD_LOCAL_DECL(static int have_activity = 0);
- THREAD_LOCAL_DECL(int scheme_active_but_sleeping = 0);
- THREAD_LOCAL_DECL(static int thread_ended_with_activity);
- THREAD_LOCAL_DECL(int scheme_no_stack_overflow);
- THREAD_LOCAL_DECL(int all_breaks_disabled = 0);
- THREAD_LOCAL_DECL(static int needs_sleep_cancelled);
- THREAD_LOCAL_DECL(static double needs_sleep_time_end); /* back-door result */
- THREAD_LOCAL_DECL(static int tls_pos = 0);
- /* On swap, put target in a static variable, instead of on the stack,
- so that the swapped-out thread is less likely to have a pointer
- to the target thread. */
- THREAD_LOCAL_DECL(static Scheme_Thread *swap_target);
- THREAD_LOCAL_DECL(static Scheme_Object *scheduled_kills);
- THREAD_LOCAL_DECL(static Scheme_Object *the_nested_exn_handler);
- THREAD_LOCAL_DECL(static Scheme_Object *cust_closers);
- THREAD_LOCAL_DECL(static Scheme_Object *thread_swap_callbacks);
- THREAD_LOCAL_DECL(static Scheme_Object *thread_swap_out_callbacks);
- THREAD_LOCAL_DECL(static Scheme_Object *recycle_cell);
- THREAD_LOCAL_DECL(static Scheme_Object *maybe_recycle_cell);
- THREAD_LOCAL_DECL(static int recycle_cc_count);
- THREAD_LOCAL_DECL(struct Scheme_Hash_Table *place_local_misc_table);
- #if defined(MZ_PRECISE_GC) && defined(MZ_USE_PLACES)
- extern intptr_t GC_is_place();
- #endif
- #ifdef MZ_PRECISE_GC
- extern intptr_t GC_get_memory_use(void *c);
- #else
- extern MZ_DLLIMPORT long GC_get_memory_use();
- #endif
- typedef struct Thread_Cell {
- Scheme_Object so;
- char inherited, assigned;
- Scheme_Object *def_val;
- /* A thread's thread_cell table maps cells to keys weakly.
- This table maps keys to values weakly. The two weak
- levels ensure that thread cells are properly GCed
- when the value of a thread cell references the thread
- cell. */
- Scheme_Bucket_Table *vals;
- } Thread_Cell;
- #ifdef MZ_PRECISE_GC
- /* This is a trick to get the types right. Note that
- the layout of the weak box is defined by the
- GC spec. */
- typedef struct {
- short type;
- short hash_key;
- Scheme_Custodian *val;
- } Scheme_Custodian_Weak_Box;
- # define MALLOC_MREF() (Scheme_Custodian_Reference *)scheme_make_late_weak_box(NULL)
- # define CUSTODIAN_FAM(x) ((Scheme_Custodian_Weak_Box *)x)->val
- # define xCUSTODIAN_FAM(x) SCHEME_BOX_VAL(x)
- #else
- # define MALLOC_MREF() MALLOC_ONE_WEAK(Scheme_Custodian_Reference)
- # define CUSTODIAN_FAM(x) (*(x))
- # define xCUSTODIAN_FAM(x) (*(x))
- #endif
- typedef struct Proc_Global_Rec {
- const char *key;
- void *val;
- struct Proc_Global_Rec *next;
- } Proc_Global_Rec;
- SHARED_OK static Proc_Global_Rec *process_globals;
- #if defined(MZ_USE_MZRT)
- static mzrt_mutex *process_global_lock;
- #endif
- #ifdef MZ_PRECISE_GC
- static void register_traversers(void);
- #endif
- static Scheme_Object *custodian_require_mem(int argc, Scheme_Object *args[]);
- static Scheme_Object *custodian_limit_mem(int argc, Scheme_Object *args[]);
- static Scheme_Object *custodian_can_mem(int argc, Scheme_Object *args[]);
- static Scheme_Object *new_tracking_fun(int argc, Scheme_Object *args[]);
- static Scheme_Object *union_tracking_val(int argc, Scheme_Object *args[]);
- static Scheme_Object *collect_garbage(int argc, Scheme_Object *args[]);
- static Scheme_Object *current_memory_use(int argc, Scheme_Object *args[]);
- static Scheme_Object *sch_thread(int argc, Scheme_Object *args[]);
- static Scheme_Object *sch_thread_nokill(int argc, Scheme_Object *args[]);
- static Scheme_Object *sch_sleep(int argc, Scheme_Object *args[]);
- static Scheme_Object *thread_p(int argc, Scheme_Object *args[]);
- static Scheme_Object *thread_running_p(int argc, Scheme_Object *args[]);
- static Scheme_Object *thread_dead_p(int argc, Scheme_Object *args[]);
- static Scheme_Object *thread_wait(int argc, Scheme_Object *args[]);
- static Scheme_Object *sch_current(int argc, Scheme_Object *args[]);
- static Scheme_Object *kill_thread(int argc, Scheme_Object *args[]);
- static Scheme_Object *break_thread(int argc, Scheme_Object *args[]);
- static Scheme_Object *thread_suspend(int argc, Scheme_Object *args[]);
- static Scheme_Object *thread_resume(int argc, Scheme_Object *args[]);
- static Scheme_Object *make_thread_suspend(int argc, Scheme_Object *args[]);
- static Scheme_Object *make_thread_resume(int argc, Scheme_Object *args[]);
- static Scheme_Object *make_thread_dead(int argc, Scheme_Object *args[]);
- static void register_thread_sync();
- static Scheme_Object *sch_sync(int argc, Scheme_Object *args[]);
- static Scheme_Object *sch_sync_timeout(int argc, Scheme_Object *args[]);
- static Scheme_Object *sch_sync_enable_break(int argc, Scheme_Object *args[]);
- static Scheme_Object *sch_sync_timeout_enable_break(int argc, Scheme_Object *args[]);
- static Scheme_Object *evt_p(int argc, Scheme_Object *args[]);
- static Scheme_Object *evts_to_evt(int argc, Scheme_Object *args[]);
- static Scheme_Object *make_custodian(int argc, Scheme_Object *argv[]);
- static Scheme_Object *make_custodian_from_main(int argc, Scheme_Object *argv[]);
- static Scheme_Object *custodian_p(int argc, Scheme_Object *argv[]);
- static Scheme_Object *custodian_close_all(int argc, Scheme_Object *argv[]);
- static Scheme_Object *custodian_to_list(int argc, Scheme_Object *argv[]);
- static Scheme_Object *current_custodian(int argc, Scheme_Object *argv[]);
- static Scheme_Object *make_custodian_box(int argc, Scheme_Object *argv[]);
- static Scheme_Object *custodian_box_value(int argc, Scheme_Object *argv[]);
- static Scheme_Object *custodian_box_p(int argc, Scheme_Object *argv[]);
- static Scheme_Object *call_as_nested_thread(int argc, Scheme_Object *argv[]);
- static Scheme_Object *current_namespace(int argc, Scheme_Object *args[]);
- static Scheme_Object *namespace_p(int argc, Scheme_Object *args[]);
- static Scheme_Object *parameter_p(int argc, Scheme_Object *args[]);
- static Scheme_Object *parameter_procedure_eq(int argc, Scheme_Object *args[]);
- static Scheme_Object *make_parameter(int argc, Scheme_Object *args[]);
- static Scheme_Object *make_derived_parameter(int argc, Scheme_Object *args[]);
- static Scheme_Object *extend_parameterization(int argc, Scheme_Object *args[]);
- static Scheme_Object *parameterization_p(int argc, Scheme_Object *args[]);
- static Scheme_Object *reparameterize(int argc, Scheme_Object **argv);
- static Scheme_Object *make_thread_cell(int argc, Scheme_Object *args[]);
- static Scheme_Object *thread_cell_p(int argc, Scheme_Object *args[]);
- static Scheme_Object *thread_cell_get(int argc, Scheme_Object *args[]);
- static Scheme_Object *thread_cell_set(int argc, Scheme_Object *args[]);
- static Scheme_Object *thread_cell_values(int argc, Scheme_Object *args[]);
- static Scheme_Object *make_security_guard(int argc, Scheme_Object *argv[]);
- static Scheme_Object *security_guard_p(int argc, Scheme_Object *argv[]);
- static Scheme_Object *current_security_guard(int argc, Scheme_Object *argv[]);
- static Scheme_Object *make_thread_set(int argc, Scheme_Object *argv[]);
- static Scheme_Object *thread_set_p(int argc, Scheme_Object *argv[]);
- static Scheme_Object *current_thread_set(int argc, Scheme_Object *argv[]);
- static Scheme_Object *current_thread_initial_stack_size(int argc, Scheme_Object *argv[]);
- static void adjust_custodian_family(void *pr, void *ignored);
- static Scheme_Object *make_will_executor(int argc, Scheme_Object *args[]);
- static Scheme_Object *will_executor_p(int argc, Scheme_Object *args[]);
- static Scheme_Object *register_will(int argc, Scheme_Object *args[]);
- static Scheme_Object *will_executor_try(int argc, Scheme_Object *args[]);
- static Scheme_Object *will_executor_go(int argc, Scheme_Object *args[]);
- static Scheme_Object *will_executor_sema(Scheme_Object *w, int *repost);
- static Scheme_Object *check_break_now(int argc, Scheme_Object *args[]);
- static int syncing_ready(Scheme_Object *s, Scheme_Schedule_Info *sinfo);
- static void make_initial_config(Scheme_Thread *p);
- static int do_kill_thread(Scheme_Thread *p);
- static void suspend_thread(Scheme_Thread *p);
- static int check_sleep(int need_activity, int sleep_now);
- static void remove_thread(Scheme_Thread *r);
- static void exit_or_escape(Scheme_Thread *p);
- static int resume_suspend_ready(Scheme_Object *o, Scheme_Schedule_Info *sinfo);
- static int dead_ready(Scheme_Object *o, Scheme_Schedule_Info *sinfo);
- static int cust_box_ready(Scheme_Object *o);
- static int can_break_param(Scheme_Thread *p);
- static int post_system_idle();
- static Scheme_Object *current_stats(int argc, Scheme_Object *args[]);
- SHARED_OK static Scheme_Object **config_map;
- typedef struct {
- MZTAG_IF_REQUIRED
- short is_derived;
- Scheme_Object *key;
- Scheme_Object *guard;
- Scheme_Object *extract_guard;
- Scheme_Object *defcell;
- } ParamData;
- enum {
- CONFIG_DIRECT,
- CONFIG_INDIRECT
- };
- typedef struct Scheme_Thread_Custodian_Hop {
- Scheme_Object so;
- Scheme_Thread *p; /* really an indirection with precise gc */
- } Scheme_Thread_Custodian_Hop;
- SHARED_OK static Scheme_Custodian_Extractor *extractors;
- #define SETJMP(p) scheme_setjmpup(&p->jmpup_buf, p, p->stack_start)
- #define LONGJMP(p) scheme_longjmpup(&p->jmpup_buf)
- #define RESETJMP(p) scheme_reset_jmpup_buf(&p->jmpup_buf)
- #ifdef WIN32_THREADS
- /* Only set up for Boehm GC that thinks it's a DLL: */
- # define GC_THINKS_ITS_A_DLL_BUT_ISNT
- # ifdef GC_THINKS_ITS_A_DLL_BUT_ISNT
- extern BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved);
- # endif
- #endif
- #ifndef MZ_PRECISE_GC
- # define scheme_thread_hop_type scheme_thread_type
- #endif
- #ifdef MZ_PRECISE_GC
- uintptr_t scheme_get_current_thread_stack_start(void);
- #endif
- SHARED_OK Scheme_Object *initial_cmdline_vec;
- /*========================================================================*/
- /* initialization */
- /*========================================================================*/
- void scheme_init_thread(Scheme_Env *env)
- {
- #ifdef MZ_PRECISE_GC
- register_traversers();
- #endif
- REGISTER_SO(read_symbol);
- REGISTER_SO(write_symbol);
- REGISTER_SO(execute_symbol);
- REGISTER_SO(delete_symbol);
- REGISTER_SO(exists_symbol);
- REGISTER_SO(client_symbol);
- REGISTER_SO(server_symbol);
- read_symbol = scheme_intern_symbol("read");
- write_symbol = scheme_intern_symbol("write");
- execute_symbol = scheme_intern_symbol("execute");
- delete_symbol = scheme_intern_symbol("delete");
- exists_symbol = scheme_intern_symbol("exists");
- client_symbol = scheme_intern_symbol("client");
- server_symbol = scheme_intern_symbol("server");
-
- GLOBAL_PRIM_W_ARITY("dump-memory-stats" , scheme_dump_gc_stats, 0, -1, env);
- GLOBAL_PRIM_W_ARITY("vector-set-performance-stats!", current_stats , 1, 2, env);
- GLOBAL_PRIM_W_ARITY("make-empty-namespace", scheme_make_namespace, 0, 0, env);
- GLOBAL_PRIM_W_ARITY("thread" , sch_thread , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("thread/suspend-to-kill", sch_thread_nokill , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("sleep" , sch_sleep , 0, 1, env);
- GLOBAL_FOLDING_PRIM("thread?" , thread_p , 1, 1, 1, env);
- GLOBAL_PRIM_W_ARITY("thread-running?" , thread_running_p , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("thread-dead?" , thread_dead_p , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("thread-wait" , thread_wait , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("current-thread" , sch_current , 0, 0, env);
- GLOBAL_PRIM_W_ARITY("kill-thread" , kill_thread , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("break-thread" , break_thread , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("thread-suspend" , thread_suspend , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("thread-resume" , thread_resume , 1, 2, env);
- GLOBAL_PRIM_W_ARITY("thread-resume-evt" , make_thread_resume , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("thread-suspend-evt" , make_thread_suspend, 1, 1, env);
- GLOBAL_PRIM_W_ARITY("thread-dead-evt" , make_thread_dead , 1, 1, env);
- register_thread_sync();
- scheme_add_evt(scheme_thread_suspend_type, (Scheme_Ready_Fun)resume_suspend_ready, NULL, NULL, 1);
- scheme_add_evt(scheme_thread_resume_type, (Scheme_Ready_Fun)resume_suspend_ready, NULL, NULL, 1);
- scheme_add_evt(scheme_thread_dead_type, (Scheme_Ready_Fun)dead_ready, NULL, NULL, 1);
- scheme_add_evt(scheme_cust_box_type, cust_box_ready, NULL, NULL, 0);
- GLOBAL_PARAMETER("current-custodian" , current_custodian , MZCONFIG_CUSTODIAN, env);
- GLOBAL_PRIM_W_ARITY("make-custodian" , make_custodian , 0, 1, env);
- GLOBAL_FOLDING_PRIM("custodian?" , custodian_p , 1, 1, 1 , env);
- GLOBAL_PRIM_W_ARITY("custodian-shutdown-all", custodian_close_all , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("custodian-managed-list", custodian_to_list , 2, 2, env);
- GLOBAL_PRIM_W_ARITY("make-custodian-box" , make_custodian_box , 2, 2, env);
- GLOBAL_PRIM_W_ARITY("custodian-box-value" , custodian_box_value , 1, 1, env);
- GLOBAL_FOLDING_PRIM("custodian-box?" , custodian_box_p , 1, 1, 1 , env);
- GLOBAL_PRIM_W_ARITY("call-in-nested-thread" , call_as_nested_thread, 1, 2, env);
- GLOBAL_PARAMETER("current-namespace" , current_namespace, MZCONFIG_ENV, env);
- GLOBAL_PRIM_W_ARITY("namespace?" , namespace_p , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("security-guard?" , security_guard_p , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("make-security-guard", make_security_guard, 3, 4, env);
- GLOBAL_PARAMETER("current-security-guard", current_security_guard, MZCONFIG_SECURITY_GUARD, env);
- GLOBAL_PRIM_W_ARITY("thread-group?" , thread_set_p , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("make-thread-group", make_thread_set, 0, 1, env);
- GLOBAL_PARAMETER("current-thread-group", current_thread_set, MZCONFIG_THREAD_SET, env);
- GLOBAL_PRIM_W_ARITY("parameter?" , parameter_p , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("make-parameter" , make_parameter , 1, 2, env);
- GLOBAL_PRIM_W_ARITY("make-derived-parameter", make_derived_parameter, 3, 3, env);
- GLOBAL_PRIM_W_ARITY("parameter-procedure=?" , parameter_procedure_eq, 2, 2, env);
- GLOBAL_PRIM_W_ARITY("parameterization?" , parameterization_p , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("thread-cell?" , thread_cell_p , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("make-thread-cell" , make_thread_cell , 1, 2, env);
- GLOBAL_PRIM_W_ARITY("thread-cell-ref" , thread_cell_get , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("thread-cell-set!" , thread_cell_set , 2, 2, env);
- GLOBAL_PRIM_W_ARITY("current-preserved-thread-cell-values", thread_cell_values, 0, 1, env);
- GLOBAL_PRIM_W_ARITY("make-will-executor", make_will_executor, 0, 0, env);
- GLOBAL_PRIM_W_ARITY("will-executor?" , will_executor_p , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("will-register" , register_will , 3, 3, env);
- GLOBAL_PRIM_W_ARITY("will-try-execute" , will_executor_try , 1, 1, env);
- GLOBAL_PRIM_W_ARITY("will-execute" , will_executor_go , 1, 1, env);
-
- scheme_add_evt_through_sema(scheme_will_executor_type, will_executor_sema, NULL);
- GLOBAL_PRIM_W_ARITY("collect-garbage" , collect_garbage , 0, 0, env);
- GLOBAL_PRIM_W_ARITY("current-memory-use" , current_memory_use , 0, 1, env);
- GLOBAL_PRIM_W_ARITY("custodian-require-memory" , custodian_require_mem, 3, 3, env);
- GLOBAL_PRIM_W_ARITY("custodian-limit-memory" , custodian_limit_mem , 2, 3, env);
- GLOBAL_PRIM_W_ARITY("custodian-memory-accounting-available?", custodian_can_mem , 0, 0, env);
-
- GLOBAL_FOLDING_PRIM("evt?" , evt_p , 1, 1 , 1, env);
- GLOBAL_PRIM_W_ARITY2("sync" , sch_sync , 1, -1, 0, -1, env);
- GLOBAL_PRIM_W_ARITY2("sync/timeout" , sch_sync_timeout , 2, -1, 0, -1, env);
- GLOBAL_PRIM_W_ARITY2("sync/enable-break" , sch_sync_enable_break , 1, -1, 0, -1, env);
- GLOBAL_PRIM_W_ARITY2("sync/timeout/enable-break", sch_sync_timeout_enable_break, 2, -1, 0, -1, env);
- GLOBAL_PRIM_W_ARITY("choice-evt" , evts_to_evt , 0, -1, env);
- GLOBAL_PARAMETER("current-thread-initial-stack-size", current_thread_initial_stack_size, MZCONFIG_THREAD_INIT_STACK_SIZE, env);
- }
- void scheme_init_thread_places(void) {
- buffer_init_size = INIT_TB_SIZE;
- REGISTER_SO(recycle_cell);
- REGISTER_SO(maybe_recycle_cell);
- REGISTER_SO(gc_prepost_callback_descs);
- REGISTER_SO(place_local_misc_table);
- }
- void scheme_init_memtrace(Scheme_Env *env)
- {
- Scheme_Object *v;
- Scheme_Env *newenv;
- v = scheme_intern_symbol("#%memtrace");
- newenv = scheme_primitive_module(v, env);
-
- v = scheme_make_symbol("memory-trace-continuation-mark");
- scheme_add_global("memory-trace-continuation-mark", v , newenv);
- v = scheme_make_prim_w_arity(new_tracking_fun,
- "new-memtrace-tracking-function", 1, 1);
- scheme_add_global("new-memtrace-tracking-function", v, newenv);
- v = scheme_make_prim_w_arity(union_tracking_val,
- "unioned-memtrace-tracking-value", 1, 1);
- scheme_add_global("unioned-memtrace-tracking-value", v, newenv);
- scheme_finish_primitive_module(newenv);
- }
- void scheme_init_inspector() {
- REGISTER_SO(initial_inspector);
- initial_inspector = scheme_make_initial_inspectors();
- /* Keep the initial inspector in case someone resets Scheme (by
- calling scheme_basic_env() a second time. Using the same
- inspector after a reset lets us use the same initial module
- instances. */
- }
- Scheme_Object *scheme_get_current_inspector()
- XFORM_SKIP_PROC
- {
- Scheme_Config *c;
- if (scheme_defining_primitives)
- return initial_inspector;
- c = scheme_current_config();
- return scheme_get_param(c, MZCONFIG_INSPECTOR);
- }
- Scheme_Object *scheme_get_initial_inspector(void)
- {
- return initial_inspector;
- }
-
- void scheme_init_parameterization()
- {
- REGISTER_SO(scheme_exn_handler_key);
- REGISTER_SO(scheme_parameterization_key);
- REGISTER_SO(scheme_break_enabled_key);
- scheme_exn_handler_key = scheme_make_symbol("exnh");
- scheme_parameterization_key = scheme_make_symbol("paramz");
- scheme_break_enabled_key = scheme_make_symbol("break-on?");
- }
- void scheme_init_paramz(Scheme_Env *env)
- {
- Scheme_Object *v;
- Scheme_Env *newenv;
- v = scheme_intern_symbol("#%paramz");
- newenv = scheme_primitive_module(v, env);
-
- scheme_add_global_constant("exception-handler-key", scheme_exn_handler_key , newenv);
- scheme_add_global_constant("parameterization-key" , scheme_parameterization_key, newenv);
- scheme_add_global_constant("break-enabled-key" , scheme_break_enabled_key , newenv);
- GLOBAL_PRIM_W_ARITY("extend-parameterization" , extend_parameterization , 1, -1, newenv);
- GLOBAL_PRIM_W_ARITY("check-for-break" , check_break_now , 0, 0, newenv);
- GLOBAL_PRIM_W_ARITY("reparameterize" , reparameterize , 1, 1, newenv);
- GLOBAL_PRIM_W_ARITY("make-custodian-from-main", make_custodian_from_main, 0, 0, newenv);
- scheme_finish_primitive_module(newenv);
- scheme_protect_primitive_provide(newenv, NULL);
- }
- static Scheme_Object *collect_garbage(int c, Scheme_Object *p[])
- {
- scheme_collect_garbage();
- return scheme_void;
- }
- static Scheme_Object *current_memory_use(int argc, Scheme_Object *args[])
- {
- Scheme_Object *arg = NULL;
- intptr_t retval = 0;
- if (argc) {
- if(SAME_TYPE(SCHEME_TYPE(args[0]), scheme_custodian_type)) {
- arg = args[0];
- } else if(SCHEME_PROCP(args[0])) {
- arg = args[0];
- } else {
- scheme_wrong_type("current-memory-use",
- "custodian or memory-trace-function",
- 0, argc, args);
- }
- }
- #ifdef MZ_PRECISE_GC
- retval = GC_get_memory_use(arg);
- #else
- retval = GC_get_memory_use();
- #endif
-
- return scheme_make_integer_value(retval);
- }
- /*========================================================================*/
- /* custodians */
- /*========================================================================*/
- static void adjust_limit_table(Scheme_Custodian *c)
- {
- /* If a custodian has a limit and any object or children, then it
- must not be collected and merged with its parent. To prevent
- collection, we register the custodian in the `limite_custodians'
- table. */
- if (c->has_limit) {
- if (c->elems || CUSTODIAN_FAM(c->children)) {
- if (!c->recorded) {
- c->recorded = 1;
- if (!limited_custodians)
- limited_custodians = scheme_make_hash_table(SCHEME_hash_ptr);
- scheme_hash_set(limited_custodians, (Scheme_Object *)c, scheme_true);
- }
- } else if (c->recorded) {
- c->recorded = 0;
- if (limited_custodians)
- scheme_hash_set(limited_custodians, (Scheme_Object *)c, NULL);
- }
- }
- }
- static Scheme_Object *custodian_require_mem(int argc, Scheme_Object *args[])
- {
- intptr_t lim;
- Scheme_Custodian *c1, *c2, *cx;
- if(NOT_SAME_TYPE(SCHEME_TYPE(args[0]), scheme_custodian_type)) {
- scheme_wrong_type("custodian-require-memory", "custodian", 0, argc, args);
- return NULL;
- }
- if (SCHEME_INTP(args[1]) && (SCHEME_INT_VAL(args[1]) > 0)) {
- lim = SCHEME_INT_VAL(args[1]);
- } else if (SCHEME_BIGNUMP(args[1]) && SCHEME_BIGPOS(args[1])) {
- lim = 0x3fffffff; /* more memory than we actually have */
- } else {
- scheme_wrong_type("custodian-require-memory", "positive exact integer", 1, argc, args);
- return NULL;
- }
- if(NOT_SAME_TYPE(SCHEME_TYPE(args[2]), scheme_custodian_type)) {
- scheme_wrong_type("custodian-require-memory", "custodian", 2, argc, args);
- return NULL;
- }
- c1 = (Scheme_Custodian *)args[0];
- c2 = (Scheme_Custodian *)args[2];
- /* Check whether c1 is super to c2: */
- if (c1 == c2) {
- cx = NULL;
- } else {
- for (cx = c2; cx && NOT_SAME_OBJ(cx, c1); ) {
- cx = CUSTODIAN_FAM(cx->parent);
- }
- }
- if (!cx) {
- scheme_raise_exn(MZEXN_FAIL_CONTRACT,
- "custodian-require-memory: second custodian is not a sub-custodian of the first custodian");
- }
- #ifdef MZ_PRECISE_GC
- if (GC_set_account_hook(MZACCT_REQUIRE, c1, lim, c2))
- return scheme_void;
- #endif
- scheme_raise_exn(MZEXN_FAIL_UNSUPPORTED,
- "custodian-require-memory: not supported");
- return NULL; /* doesn't get here */
- }
- static Scheme_Object *custodian_limit_mem(int argc, Scheme_Object *args[])
- {
- intptr_t lim;
-
- if (NOT_SAME_TYPE(SCHEME_TYPE(args[0]), scheme_custodian_type)) {
- scheme_wrong_type("custodian-limit-memory", "custodian", 0, argc, args);
- return NULL;
- }
- if (SCHEME_INTP(args[1]) && (SCHEME_INT_VAL(args[1]) > 0)) {
- lim = SCHEME_INT_VAL(args[1]);
- } else if (SCHEME_BIGNUMP(args[1]) && SCHEME_BIGPOS(args[1])) {
- lim = 0x3fffffff; /* more memory than we actually have */
- } else {
- scheme_wrong_type("custodian-limit-memory", "positive exact integer", 1, argc, args);
- return NULL;
- }
- if (argc > 2) {
- if (NOT_SAME_TYPE(SCHEME_TYPE(args[2]), scheme_custodian_type)) {
- scheme_wrong_type("custodian-require-memory", "custodian", 2, argc, args);
- return NULL;
- }
- }
- ((Scheme_Custodian *)args[0])->has_limit = 1;
- adjust_limit_table((Scheme_Custodian *)args[0]);
- if (argc > 2) {
- ((Scheme_Custodian *)args[2])->has_limit = 1;
- adjust_limit_table((Scheme_Custodian *)args[2]);
- }
- #ifdef MZ_PRECISE_GC
- if (GC_set_account_hook(MZACCT_LIMIT, args[0], lim, (argc > 2) ? args[2] : args[0]))
- return scheme_void;
- #endif
- scheme_raise_exn(MZEXN_FAIL_UNSUPPORTED,
- "custodian-limit-memory: not supported");
- return NULL; /* doesn't get here */
- }
- static Scheme_Object *custodian_can_mem(int argc, Scheme_Object *args[])
- {
- #ifdef MZ_PRECISE_GC
- return (GC_accouting_enabled() ? scheme_true : scheme_false);
- #else
- return scheme_false;
- #endif
- }
- static Scheme_Object *new_tracking_fun(int argc, Scheme_Object *args[])
- {
- int retval = 0;
- #ifdef MZ_PRECISE_GC
- retval = GC_mtrace_new_id(args[0]);
- #endif
- return scheme_make_integer(retval);
- }
- static Scheme_Object *union_tracking_val(int argc, Scheme_Object *args[])
- {
- int retval = 0;
- #ifdef MZ_PRECISE_GC
- retval = GC_mtrace_union_current_with(SCHEME_INT_VAL(args[0]));
- #endif
- return scheme_make_integer(retval);
- }
- static void ensure_custodian_space(Scheme_Custodian *m, int k)
- {
- int i;
- if (m->count + k >= m->alloc) {
- Scheme_Object ***naya_boxes;
- Scheme_Custodian_Reference **naya_mrefs;
- Scheme_Close_Custodian_Client **naya_closers;
- void **naya_data;
- m->alloc = (m->alloc ? (2 * m->alloc) : 4);
- if (m->alloc < k)
- m->alloc += k;
-
- naya_boxes = MALLOC_N(Scheme_Object**, m->alloc);
- naya_closers = MALLOC_N(Scheme_Close_Custodian_Client*, m->alloc);
- naya_data = MALLOC_N(void*, m->alloc);
- naya_mrefs = MALLOC_N(Scheme_Custodian_Reference*, m->alloc);
- for (i = m->count; i--; ) {
- naya_boxes[i] = m->boxes[i];
- m->boxes[i] = NULL;
- naya_closers[i] = m->closers[i];
- m->closers[i] = NULL;
- naya_data[i] = m->data[i];
- m->data[i] = NULL;
- naya_mrefs[i] = m->mrefs[i];
- m->mrefs[i] = NULL;
- }
- m->boxes = naya_boxes;
- m->closers = naya_closers;
- m->data = naya_data;
- m->mrefs = naya_mrefs;
- }
- }
- static void add_managed_box(Scheme_Custodian *m,
- Scheme_Object **box, Scheme_Custodian_Reference *mref,
- Scheme_Close_Custodian_Client *f, void *data)
- {
- int i;
- for (i = m->count; i--; ) {
- if (!m->boxes[i]) {
- m->boxes[i] = box;
- m->closers[i] = f;
- m->data[i] = data;
- m->mrefs[i] = mref;
- m->elems++;
- adjust_limit_table(m);
- return;
- }
- }
- ensure_custodian_space(m, 1);
- m->boxes[m->count] = box;
- m->closers[m->count] = f;
- m->data[m->count] = data;
- m->mrefs[m->count] = mref;
- m->elems++;
- adjust_limit_table(m);
- m->count++;
- }
- static void remove_managed(Scheme_Custodian_Reference *mr, Scheme_Object *o,
- Scheme_Close_Custodian_Client **old_f, void **old_data)
- {
- Scheme_Custodian *m;
- int i;
- if (!mr)
- return;
- m = CUSTODIAN_FAM(mr);
- if (!m)
- return;
- for (i = m->count; i--; ) {
- if (m->boxes[i] && SAME_OBJ((xCUSTODIAN_FAM(m->boxes[i])), o)) {
- xCUSTODIAN_FAM(m->boxes[i]) = 0;
- m->boxes[i] = NULL;
- CUSTODIAN_FAM(m->mrefs[i]) = 0;
- m->mrefs[i] = NULL;
- if (old_f)
- *old_f = m->closers[i];
- if (old_data)
- *old_data = m->data[i];
- m->data[i] = NULL;
- --m->elems;
- adjust_limit_table(m);
- break;
- }
- }
- while (m->count && !m->boxes[m->count - 1]) {
- --m->count;
- }
- }
- static void adjust_custodian_family(void *mgr, void *skip_move)
- {
- /* Threads note: because this function is only called as a
- finalization callback, it is automatically syncronized by the GC
- locks. And it is synchronized against all finalizations, so a
- managee can't try to unregister while we're shuffling its
- custodian. */
- Scheme_Custodian *r = (Scheme_Custodian *)mgr, *parent, *m;
- int i;
- parent = CUSTODIAN_FAM(r->parent);
- if (parent) {
- /* Remove from parent's list of children: */
- if (CUSTODIAN_FAM(parent->children) == r) {
- CUSTODIAN_FAM(parent->children) = CUSTODIAN_FAM(r->sibling);
- } else {
- m = CUSTODIAN_FAM(parent->children);
- while (m && CUSTODIAN_FAM(m->sibling) != r) {
- m = CUSTODIAN_FAM(m->sibling);
- }
- if (m)
- CUSTODIAN_FAM(m->sibling) = CUSTODIAN_FAM(r->sibling);
- }
- /* Remove from global list: */
- if (CUSTODIAN_FAM(r->global_next))
- CUSTODIAN_FAM(CUSTODIAN_FAM(r->global_next)->global_prev) = CUSTODIAN_FAM(r->global_prev);
- else
- last_custodian = CUSTODIAN_FAM(r->global_prev);
- CUSTODIAN_FAM(CUSTODIAN_FAM(r->global_prev)->global_next) = CUSTODIAN_FAM(r->global_next);
-
- /* Add children to parent's list: */
- for (m = CUSTODIAN_FAM(r->children); m; ) {
- Scheme_Custodian *next = CUSTODIAN_FAM(m->sibling);
-
- CUSTODIAN_FAM(m->parent) = parent;
- CUSTODIAN_FAM(m->sibling) = CUSTODIAN_FAM(parent->children);
- CUSTODIAN_FAM(parent->children) = m;
- m = next;
- }
- adjust_limit_table(parent);
- /* Add remaining managed items to parent: */
- if (!skip_move) {
- for (i = 0; i < r->count; i++) {
- if (r->boxes[i]) {
- CUSTODIAN_FAM(r->mrefs[i]) = parent;
- add_managed_box(parent, r->boxes[i], r->mrefs[i], r->closers[i], r->data[i]);
- #ifdef MZ_PRECISE_GC
- {
- Scheme_Object *o;
- o = xCUSTODIAN_FAM(r->boxes[i]);
- if (SAME_TYPE(SCHEME_TYPE(o), scheme_thread_hop_type)) {
- o = WEAKIFIED(((Scheme_Thread_Custodian_Hop *)o)->p);
- if (o)
- GC_register_thread(o, parent);
- }
- }
- #endif
- }
- }
- }
- }
- CUSTODIAN_FAM(r->parent) = NULL;
- CUSTODIAN_FAM(r->sibling) = NULL;
- if (!skip_move)
- CUSTODIAN_FAM(r->children) = NULL;
- CUSTODIAN_FAM(r->global_prev) = NULL;
- CUSTODIAN_FAM(r->global_next) = NULL;
- }
- void insert_custodian(Scheme_Custodian *m, Scheme_Custodian *parent)
- {
- /* insert into parent's list: */
- CUSTODIAN_FAM(m->parent) = parent;
- if (parent) {
- CUSTODIAN_FAM(m->sibling) = CUSTODIAN_FAM(parent->children);
- CUSTODIAN_FAM(parent->children) = m;
- } else
- CUSTODIAN_FAM(m->sibling) = NULL;
- /* Insert into global chain. A custodian is always inserted
- directly after its parent, so families stay together, and
- the local list stays in the same order as the sibling list. */
- if (parent) {
- Scheme_Custodian *next;
- next = CUSTODIAN_FAM(parent->global_next);
- CUSTODIAN_FAM(m->global_next) = next;
- CUSTODIAN_FAM(m->global_prev) = parent;
- CUSTODIAN_FAM(parent->global_next) = m;
- if (next)
- CUSTODIAN_FAM(next->global_prev) = m;
- else
- last_custodian = m;
- } else {
- CUSTODIAN_FAM(m->global_next) = NULL;
- CUSTODIAN_FAM(m->global_prev) = NULL;
- }
- if (parent)
- adjust_limit_table(parent);
- }
- Scheme_Custodian *scheme_make_custodian(Scheme_Custodian *parent)
- {
- Scheme_Custodian *m;
- Scheme_Custodian_Reference *mw;
- if (!parent)
- parent = main_custodian; /* still NULL if we're creating main; that's ok */
-
- m = MALLOC_ONE_TAGGED(Scheme_Custodian);
- m->so.type = scheme_custodian_type;
- m->alloc = m->count = 0;
- mw = MALLOC_MREF();
- m->parent = mw;
- mw = MALLOC_MREF();
- m->children = mw;
- mw = MALLOC_MREF();
- m->sibling = mw;
- mw = MALLOC_MREF();
- m->global_next = mw;
- mw = MALLOC_MREF();
- m->global_prev = mw;
- CUSTODIAN_FAM(m->children) = NULL;
- insert_custodian(m, parent);
- scheme_add_finalizer(m, adjust_custodian_family, NULL);
- return m;
- }
- static void rebox_willdone_object(void *o, void *mr)
- {
- Scheme_Custodian *m = CUSTODIAN_FAM((Scheme_Custodian_Reference *)mr);
- Scheme_Close_Custodian_Client *f;
- void *data;
- /* Still needs management? */
- if (m) {
- #ifdef MZ_PRECISE_GC
- Scheme_Object *b;
- #else
- Scheme_Object **b;
- #endif
- remove_managed(mr, o, &f, &data);
- #ifdef MZ_PRECISE_GC
- b = scheme_box(NULL);
- #else
- b = MALLOC_ONE(Scheme_Object*); /* not atomic this time */
- #endif
- xCUSTODIAN_FAM(b) = o;
-
- /* Put the custodian back: */
- CUSTODIAN_FAM((Scheme_Custodian_Reference *)mr) = m;
- add_managed_box(m, (Scheme_Object **)b, (Scheme_Custodian_Reference *)mr, f, data);
- }
- }
- static void managed_object_gone(void *o, void *mr)
- {
- Scheme_Custodian *m = CUSTODIAN_FAM((Scheme_Custodian_Reference *)mr);
- /* Still has management? */
- if (m)
- remove_managed(mr, o, NULL, NULL);
- }
- int scheme_custodian_is_available(Scheme_Custodian *m) XFORM_SKIP_PROC
- /* may be called from a future thread */
- {
- if (m->shut_down)
- return 0;
- return 1;
- }
- void scheme_custodian_check_available(Scheme_Custodian *m, const char *who, const char *what)
- {
- if (!m)
- m = (Scheme_Custodian *)scheme_get_param(scheme_current_config(), MZCONFIG_CUSTODIAN);
-
- if (!scheme_custodian_is_available(m))
- scheme_arg_mismatch(who, "the custodian has been shut down: ",
- (Scheme_Object *)m);
- }
- Scheme_Custodian_Reference *scheme_add_managed(Scheme_Custodian *m, Scheme_Object *o,
- Scheme_Close_Custodian_Client *f, void *data,
- int must_close)
- {
- #ifdef MZ_PRECISE_GC
- Scheme_Object *b;
- #else
- Scheme_Object **b;
- #endif
- Scheme_Custodian_Reference *mr;
- if (!m)
- m = (Scheme_Custodian *)scheme_get_param(scheme_current_config(), MZCONFIG_CUSTODIAN);
-
- if (m->shut_down) {
- /* The custodian was shut down in the time that it took
- to allocate o. This situation should be avoided if at
- all possible, but here's the fail-safe. */
- if (f)
- f(o, data);
- return NULL;
- }
- #ifdef MZ_PRECISE_GC
- b = scheme_make_late_weak_box(NULL);
- #else
- b = MALLOC_ONE_WEAK(Scheme_Object*);
- #endif
- xCUSTODIAN_FAM(b) = o;
- mr = MALLOC_MREF();
- CUSTODIAN_FAM(mr) = m;
- /* The atomic link via the box `b' allows the execution of wills for
- o. After this, we should either drop the object or we have to
- hold on to the object strongly (for when custodian-close-all is
- called). */
- if (must_close)
- scheme_add_finalizer(o, rebox_willdone_object, mr);
- else
- scheme_add_finalizer(o, managed_object_gone, mr);
- add_managed_box(m, (Scheme_Object **)b, mr, f, data);
- return mr;
- }
- void scheme_remove_managed(Scheme_Custodian_Reference *mr, Scheme_Object *o)
- {
- /* Is this a good idea? I'm not sure: */
- scheme_subtract_finalizer(o, managed_object_gone, mr);
- scheme_subtract_finalizer(o, rebox_willdone_object, mr);
- remove_managed(mr, o, NULL, NULL);
- }
- Scheme_Thread *scheme_do_close_managed(Scheme_Custodian *m, Scheme_Exit_Closer_Func cf)
- {
- Scheme_Thread *kill_self = NULL;
- Scheme_Custodian *c, *start, *next_m;
- int i, is_thread;
- Scheme_Thread *the_thread;
- Scheme_Object *o;
- Scheme_Close_Custodian_Client *f;
- void *data;
- if (!m)
- m = main_custodian;
- if (m->shut_down)
- return NULL;
- m->shut_down = 1;
- /* Need to kill children first, transitively, so find
- last decendent. The family will be the global-list from
- m to this last decendent, inclusive. */
- for (c = m; CUSTODIAN_FAM(c->children); ) {
- for (c = CUSTODIAN_FAM(c->children); CUSTODIAN_FAM(c->sibling); ) {
- c = CUSTODIAN_FAM(c->sibling);
- }
- }
- start = m;
- m = c;
- while (1) {
- /* It matters that this loop starts at the top. See
- the m->count = i assignment below. */
- for (i = m->count; i--; ) {
- if (m->boxes[i]) {
- o = xCUSTODIAN_FAM(m->boxes[i]);
- f = m->closers[i];
- data = m->data[i];
- if (!cf && (SAME_TYPE(SCHEME_TYPE(o), scheme_thread_hop_type))) {
- /* We've added an indirection and made it weak. See mr_hop note above. */
- is_thread = 1;
- the_thread = (Scheme_Thread *)WEAKIFIED(((Scheme_Thread_Custodian_Hop *)o)->p);
- } else {
- is_thread = 0;
- the_thread = NULL;
- }
- xCUSTODIAN_FAM(m->boxes[i]) = NULL;
- CUSTODIAN_FAM(m->mrefs[i]) = NULL;
-
- /* Set m->count to i in case a GC happens while
- the closer is running. If there's a GC, then
- for_each_managed will be called. */
- m->count = i;
- if (is_thread && !the_thread) {
- /* Thread is already collected, so skip */
- } else if (cf) {
- cf(o, f, data);
- } else {
- if (is_thread) {
- if (the_thread) {
- /* Only kill the thread if it has no other custodians */
- if (SCHEME_NULLP(the_thread->extra_mrefs)) {
- if (do_kill_thread(the_thread))
- kill_self = the_thread;
- } else {
- Scheme_Custodian_Reference *mref;
- mref = m->mrefs[i];
- if (mref == the_thread->mref) {
- /* Designate a new main custodian for the thread */
- mref = (Scheme_Custodian_Reference *)SCHEME_CAR(the_thread->extra_mrefs);
- the_thread->mref = mref;
- the_thread->extra_mrefs = SCHEME_CDR(the_thread->extra_mrefs);
- #ifdef MZ_PRECISE_GC
- GC_register_thread(the_thread, CUSTODIAN_FAM(mref));
- #endif
- } else {
- /* Just remove mref from the list of extras */
- Scheme_Object *l, *prev = NULL;
- for (l = the_thread->extra_mrefs; 1; l = SCHEME_CDR(l)) {
- if (SAME_OBJ(SCHEME_CAR(l), (Scheme_Object *)mref)) {
- if (prev)
- SCHEME_CDR(prev) = SCHEME_CDR(l);
- else
- the_thread->extra_mrefs = SCHEME_CDR(l);
- break;
- }
- prev = l;
- }
- }
- }
- }
- } else {
- f(o, data);
- }
- }
- }
- }
- #ifdef MZ_PRECISE_GC
- {
- Scheme_Object *pr = m->cust_boxes, *wb;
- Scheme_Custodian_Box *cb;
- while (pr) {
- wb = SCHEME_CAR(pr);
- cb = (Scheme_Custodian_Box *)SCHEME_BOX_VAL(wb);
- if (cb) cb->v = NULL;
- pr = SCHEME_CDR(pr);
- }
- m->cust_boxes = NULL;
- }
- #endif
- m->count = 0;
- m->alloc = 0;
- m->elems = 0;
- m->boxes = NULL;
- m->closers = NULL;
- m->data = NULL;
- m->mrefs = NULL;
- m->shut_down = 1;
-
- if (SAME_OBJ(m, start))
- break;
- next_m = CUSTODIAN_FAM(m->global_prev);
- /* Remove this custodian from its parent */
- adjust_custodian_family(m, m);
- adjust_limit_table(m);
-
- m = next_m;
- }
- #ifdef MZ_USE_FUTURES
- scheme_future_check_custodians();
- #endif
- return kill_self;
- }
- typedef void (*Scheme_For_Each_Func)(Scheme_Object *);
- static void for_each_managed(Scheme_Type type, Scheme_For_Each_Func cf)
- XFORM_SKIP_PROC
- /* This function must not allocate. */
- {
- Scheme_Custodian *m;
- int i;
- if (SAME_TYPE(type, scheme_thread_type))
- type = scheme_thread_hop_type;
- /* back to front so children are first: */
- m = last_custodian;
- while (m) {
- for (i = m->count; i--; ) {
- if (m->boxes[i]) {
- Scheme_Object *o;
- o = xCUSTODIAN_FAM(m->boxes[i]);
-
- if (SAME_TYPE(SCHEME_TYPE(o), type)) {
- if (SAME_TYPE(type, scheme_thread_hop_type)) {
- /* We've added an indirection and made it weak. See mr_hop note above. */
- Scheme_Thread *t;
- t = (Scheme_Thread *)WEAKIFIED(((Scheme_Thread_Custodian_Hop *)o)->p);
- if (!t) {
- /* The thread is already collected */
- continue;
- } else if (SAME_OBJ(t->mref, m->mrefs[i]))
- o = (Scheme_Object *)t;
- else {
- /* The main custodian for this thread is someone else */
- continue;
- }
- }
- cf(o);
- }
- }
- }
- m = CUSTODIAN_FAM(m->global_prev);
- }
- }
- static void do_close_managed(Scheme_Custodian *m)
- /* The trick is that we may need to kill the thread
- that is running us. If so, delay it to the very
- end. */
- {
- if (scheme_do_close_managed(m, NULL)) {
- /* Kill/suspend self */
- if (scheme_current_thread->suspend_to_kill)
- suspend_thread(scheme_current_thread);
- else
- scheme_thread_block(0.0);
- }
- }
- void scheme_close_managed(Scheme_Custodian *m)
- {
- do_close_managed(m);
- /* Give killed threads time to die: */
- scheme_thread_block(0);
- scheme_current_thread->ran_some = 1;
- }
- static Scheme_Object *make_custodian(int argc, Scheme_Object *argv[])
- {
- Scheme_Custodian *m;
- if (argc) {
- if (!SCHEME_CUSTODIANP(argv[0]))
- scheme_wrong_type("make-custodian", "custodian", 0, argc, argv);
- m = (Scheme_Custodian *)argv[0];
- } else
- m = (Scheme_Custodian *)scheme_get_param(scheme_current_config(), MZCONFIG_CUSTODIAN);
- if (m->shut_down)
- scheme_arg_mismatch("make-custodian",
- "the custodian has been shut down: ",
- (Scheme_Object *)m);
- return (Scheme_Object *)scheme_make_custodian(m);
- }
- static Scheme_Object *make_custodian_from_main(int argc, Scheme_Object *argv[])
- {
- return (Scheme_Object *)scheme_make_custodian(NULL);
- }
- static Scheme_Object *custodian_p(int argc, Scheme_Object *argv[])
- {
- return SCHEME_CUSTODIANP(argv[0]) ? scheme_true : scheme_false;
- }
- static Scheme_Object *custodian_close_all(int argc, Scheme_Object *argv[])
- {
- if (!SCHEME_CUSTODIANP(argv[0]))
- scheme_wrong_type("custodian-shutdown-all", "custodian", 0, argc, argv);
- scheme_close_managed((Scheme_Custodian *)argv[0]);
- return scheme_void;
- }
- Scheme_Custodian* scheme_custodian_extract_reference(Scheme_Custodian_Reference *mr)
- {
- return CUSTODIAN_FAM(mr);
- }
- int scheme_custodian_is_shut_down(Scheme_Custodian* c)
- {
- return c->shut_down;
- }
- static Scheme_Object *extract_thread(Scheme_Object *o)
- {
- return (Scheme_Object *)WEAKIFIED(((Scheme_Thread_Custodian_Hop *)o)->p);
- }
- void scheme_init_custodian_extractors()
- {
- if (!extractors) {
- int n;
- n = scheme_num_types();
- REGISTER_SO(extractors);
- extractors = MALLOC_N_ATOMIC(Scheme_Custodian_Extractor, n);
- memset(extractors, 0, sizeof(Scheme_Custodian_Extractor) * n);
- extractors[scheme_thread_hop_type] = extract_thread;
- }
- }
- void scheme_add_custodian_extractor(Scheme_Type t, Scheme_Custodian_Extractor e)
- {
- if (t) {
- extractors[t] = e;
- }
- }
- static Scheme_Object *custodian_to_list(int argc, Scheme_Object *argv[])
- {
- Scheme_Custodian *m, *m2, *c;
- Scheme_Object **hold, *o;
- int i, j, cnt, kids;
- Scheme_Type type;
- Scheme_Custodian_Extractor ex;
- if (!SCHEME_CUSTODIANP(argv[0]))
- scheme_wrong_type("custodian-managed-list", "custodian", 0, argc, argv);
- if (!SCHEME_CUSTODIANP(argv[1]))
- scheme_wrong_type("custodian-managed-list", "custodian", 1, argc, argv);
- m = (Scheme_Custodian *)argv[0];
- m2 = (Scheme_Custodian *)argv[1];
- /* Check that the second manages the first: */
- c = CUSTODIAN_FAM(m->parent);
- while (c && NOT_SAME_OBJ(m2, c)) {
- c = CUSTODIAN_FAM(c->parent);
- }
- if (!c) {
- scheme_arg_mismatch("custodian-managed-list",
- "the second custodian does not "
- "manage the first custodian: ",
- argv[0]);
- }
- /* Init extractors: */
- scheme_add_custodian_extractor(0, NULL);
- /* Count children: */
- kids = 0;
- for (c = CUSTODIAN_FAM(m->children); c; c = CUSTODIAN_FAM(c->sibling)) {
- kids++;
- }
- /* Do all allocation first, since custodian links are weak.
- Furthermore, allocation may trigger collection of an otherwise
- unreferenced custodian, folding its items into this one,
- so loop until we've allocated enough. */
- do {
- cnt = m->count;
- hold = MALLOC_N(Scheme_Object *, cnt + kids);
- } while (cnt < m->count);
-
- /* Put managed items into hold array: */
- for (i = m->count, j = 0; i--; ) {
- if (m->boxes[i]) {
- o = xCUSTODIAN_FAM(m->boxes[i]);
-
- type = SCHEME_TYPE(o);
- ex = extractors[type];
- if (ex) {
- o = ex(o);
- }
- if (o) {
- hold[j] = o;
- j++;
- }
- }
- }
- /* Add kids: */
- for (c = CUSTODIAN_FAM(m->children); c; c = CUSTODIAN_FAM(c->sibling)) {
- hold[j] = (Scheme_Object *)c;
- j++;
- …
Large files files are truncated, but you can click here to view the full file