diff --git a/configure.ac b/configure.ac index ff0e9e8..ee76d9b 100644 --- a/configure.ac +++ b/configure.ac @@ -134,6 +134,7 @@ dnl check functions AC_CHECK_FUNCS([gettimeofday settimeofday clock_gettime clock_settime getitimer setitimer]) AC_CHECK_FUNCS([backtrace backtrace_symbols]) AC_CHECK_FUNCS([makecontext swapcontext getcontext setcontext]) +AC_CHECK_FUNCS([clock_nanosleep nanosleep usleep]) AC_CHECK_FUNCS([snprintf _vsnprintf _vsnwprintf]) AC_CHECK_FUNCS([isatty]) diff --git a/lib/exec.c b/lib/exec.c index 759d0e1..cf81b43 100644 --- a/lib/exec.c +++ b/lib/exec.c @@ -27,30 +27,6 @@ #include "hcl-prv.h" -/* TODO: remove these headers after having migrated system-dependent functions of of this file */ -#if defined(_WIN32) -# include -#elif defined(__OS2__) -# define INCL_DOSMISC -# define INCL_DOSDATETIME -# define INCL_DOSERRORS -# include -# include -#elif defined(__MSDOS__) -# include -#elif defined(macintosh) -# include -# include -# include -#else -# if defined(HAVE_TIME_H) -# include -# endif -# if defined(HAVE_SYS_TIME_H) -# include -# endif -#endif - #define PROC_STATE_RUNNING 3 #define PROC_STATE_WAITING 2 #define PROC_STATE_RUNNABLE 1 @@ -139,147 +115,21 @@ static HCL_INLINE const char* proc_state_to_string (int state) # define LOG_INST_3(hcl,fmt,a1,a2,a3) #endif -/* ------------------------------------------------------------------------- */ -static HCL_INLINE void vm_gettime (hcl_t* hcl, hcl_ntime_t* now) +static int vm_startup (hcl_t* hcl) { -#if defined(_WIN32) + HCL_DEBUG0 (hcl, "VM started up\n"); - /* TODO: */ + if (hcl->vmprim.vm_startup(hcl) <= -1) return -1; + hcl->vmprim.vm_gettime (hcl, &hcl->exec_start_time); /* raw time. no adjustment */ -#elif defined(__OS2__) - ULONG out; - -/* TODO: handle overflow?? */ -/* TODO: use DosTmrQueryTime() and DosTmrQueryFreq()? */ - DosQuerySysInfo (QSV_MS_COUNT, QSV_MS_COUNT, &out, HCL_SIZEOF(out)); /* milliseconds */ - /* it must return NO_ERROR */ - - HCL_INITNTIME (now, HCL_MSEC_TO_SEC(out), HCL_MSEC_TO_NSEC(out)); -#elif defined(__MSDOS__) && defined(_INTELC32_) - clock_t c; - -/* TODO: handle overflow?? */ - c = clock (); - now->sec = c / CLOCKS_PER_SEC; - #if (CLOCKS_PER_SEC == 1000) - now->nsec = HCL_MSEC_TO_NSEC(c % CLOCKS_PER_SEC); - #elif (CLOCKS_PER_SEC == 1000000L) - now->nsec = HCL_USEC_TO_NSEC(c % CLOCKS_PER_SEC); - #elif (CLOCKS_PER_SEC == 1000000000L) - now->nsec = (c % CLOCKS_PER_SEC); - #else - # error UNSUPPORTED CLOCKS_PER_SEC - #endif -#elif defined(macintosh) - UnsignedWide tick; - hcl_uint64_t tick64; - - Microseconds (&tick); - - tick64 = *(hcl_uint64_t*)&tick; - HCL_INITNTIME (now, HCL_USEC_TO_SEC(tick64), HCL_USEC_TO_NSEC(tick64)); - -#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - struct timespec ts; - clock_gettime (CLOCK_MONOTONIC, &ts); - HCL_INITNTIME(now, ts.tv_sec, ts.tv_nsec); - -#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME) - struct timespec ts; - clock_gettime (CLOCK_REALTIME, &ts); - HCL_INITNTIME(now, ts.tv_sec, ts.tv_nsec); - HCL_SUBNTIME (now, now, &hcl->vm_time_offset); /* offset */ -#else - struct timeval tv; - gettimeofday (&tv, HCL_NULL); - HCL_INITNTIME(now, tv.tv_sec, HCL_USEC_TO_NSEC(tv.tv_usec)); - - /* at the first call, vm_time_offset should be 0. so subtraction takes - * no effect. once it becomes non-zero, it offsets the actual time. - * this is to keep the returned time small enough to be held in a - * small integer on platforms where the small integer is not large enough */ - HCL_SUBNTIME (now, now, &hcl->vm_time_offset); -#endif -} - -static HCL_INLINE void vm_sleep (hcl_t* hcl, const hcl_ntime_t* dur) -{ -#if defined(_WIN32) - if (hcl->waitable_timer) - { - LARGE_INTEGER li; - li.QuadPart = -HCL_SECNSEC_TO_NSEC(dur->sec, dur->nsec); - if(SetWaitableTimer(timer, &li, 0, HCL_NULL, HCL_NULL, FALSE) == FALSE) goto normal_sleep; - WaitForSingleObject(timer, INFINITE); - } - else - { - normal_sleep: - /* fallback to normal Sleep() */ - Sleep (HCL_SECNSEC_TO_MSEC(dur->sec,dur->nsec)); - } -#elif defined(__OS2__) - - /* TODO: in gui mode, this is not a desirable method??? - * this must be made event-driven coupled with the main event loop */ - DosSleep (HCL_SECNSEC_TO_MSEC(dur->sec,dur->nsec)); - -#elif defined(macintosh) - - /* TODO: ... */ - -#elif defined(__MSDOS__) && defined(_INTELC32_) - - clock_t c; - - c = clock (); - c += dur->sec * CLOCKS_PER_SEC; - #if (CLOCKS_PER_SEC == 1000) - c += HCL_NSEC_TO_MSEC(dur->nsec); - #elif (CLOCKS_PER_SEC == 1000000L) - c += HCL_NSEC_TO_USEC(dur->nsec); - #elif (CLOCKS_PER_SEC == 1000000000L) - c += dur->nsec; - #else - # error UNSUPPORTED CLOCKS_PER_SEC - #endif - -/* TODO: handle clock overvlow */ -/* TODO: check if there is abortion request or interrupt */ - while (c > clock()) ; - -#else - struct timespec ts; - ts.tv_sec = dur->sec; - ts.tv_nsec = dur->nsec; - nanosleep (&ts, HCL_NULL); -#endif -} - - -static void vm_startup (hcl_t* hcl) -{ - hcl_ntime_t now; - -#if defined(_WIN32) - hcl->waitable_timer = CreateWaitableTimer(HCL_NULL, TRUE, HCL_NULL); -#endif - - /* reset hcl->vm_time_offset so that vm_gettime is not affected */ - HCL_INITNTIME(&hcl->vm_time_offset, 0, 0); - vm_gettime (hcl, &now); - hcl->vm_time_offset = now; + return 0; } static void vm_cleanup (hcl_t* hcl) { -#if defined(_WIN32) - if (hcl->waitable_timer) - { - CloseHandle (hcl->waitable_timer); - hcl->waitable_timer = HCL_NULL; - } -#endif + hcl->vmprim.vm_gettime (hcl, &hcl->exec_end_time); /* raw time. no adjustment */ + hcl->vmprim.vm_cleanup (hcl); + HCL_DEBUG0 (hcl, "VM started up\n"); } /* ------------------------------------------------------------------------- */ @@ -1224,7 +1074,7 @@ static int execute (hcl_t* hcl) HCL_ASSERT (hcl, hcl->active_context != HCL_NULL); - vm_startup (hcl); + if (vm_startup (hcl) <= -1) return -1; hcl->proc_switched = 0; while (1) @@ -1232,7 +1082,7 @@ static int execute (hcl_t* hcl) if (hcl->sem_heap_count > 0) { hcl_ntime_t ft, now; - vm_gettime (hcl, &now); + hcl->vmprim.vm_gettime (hcl, &now); do { @@ -1276,8 +1126,8 @@ static int execute (hcl_t* hcl) else if (hcl->processor->active == hcl->nil_process) { HCL_SUBNTIME (&ft, &ft, (hcl_ntime_t*)&now); - vm_sleep (hcl, &ft); /* TODO: change this to i/o multiplexer??? */ - vm_gettime (hcl, &now); + hcl->vmprim.vm_sleep (hcl, &ft); /* TODO: change this to i/o multiplexer??? */ + hcl->vmprim.vm_gettime (hcl, &now); } else { @@ -1327,8 +1177,9 @@ static int execute (hcl_t* hcl) if (hcl->ip >= hcl->code.bc.len) { - HCL_DEBUG2 (hcl, "IP(%zd) reached the end of bytecode(%zu). Stopping execution\n", hcl->ip, hcl->code.bc.len); - break; + HCL_DEBUG1 (hcl, "IP reached the end of bytecode(%zu). Stopping execution\n", hcl->code.bc.len); + return_value = hcl->_nil; + goto handle_return; } #if defined(HCL_DEBUG_VM_EXEC) diff --git a/lib/hcl-prv.h b/lib/hcl-prv.h index 32ba19e..49cd9ad 100644 --- a/lib/hcl-prv.h +++ b/lib/hcl-prv.h @@ -61,7 +61,8 @@ #if !defined(NDEBUG) #define HCL_DEBUG_VM_PROCESSOR 1 #define HCL_DEBUG_VM_EXEC 1 -#define MOO_DEBUG_BIGINT 1 +#define HCL_DEBUG_BIGINT 1 +#define HCL_PROFILE_VM 1 #endif /* allow the caller to drive process switching by calling diff --git a/lib/hcl.h b/lib/hcl.h index 9de2bce..40fbe54 100644 --- a/lib/hcl.h +++ b/lib/hcl.h @@ -700,14 +700,28 @@ typedef void (*hcl_log_write_t) (hcl_t* hcl, hcl_oow_t mask, const hcl_ooch_t* m typedef void (*hcl_syserrstrb_t) (hcl_t* hcl, int syserr, hcl_bch_t* buf, hcl_oow_t len); typedef void (*hcl_syserrstru_t) (hcl_t* hcl, int syserr, hcl_uch_t* buf, hcl_oow_t len); +typedef int (*hcl_vmprim_startup_t) (hcl_t* hcl); +typedef void (*hcl_vmprim_cleanup_t) (hcl_t* hcl); +typedef void (*hcl_vmprim_gettime_t) (hcl_t* hcl, hcl_ntime_t* now); + + +typedef void (*hcl_vmprim_sleep_t) (hcl_t* hcl, const hcl_ntime_t* duration); + struct hcl_vmprim_t { hcl_vmprim_dlopen_t dl_open; hcl_vmprim_dlclose_t dl_close; hcl_vmprim_dlsym_t dl_getsym; + hcl_log_write_t log_write; hcl_syserrstrb_t syserrstrb; hcl_syserrstru_t syserrstru; + + hcl_vmprim_startup_t vm_startup; + hcl_vmprim_cleanup_t vm_cleanup; + hcl_vmprim_gettime_t vm_gettime; + + hcl_vmprim_sleep_t vm_sleep; }; typedef struct hcl_vmprim_t hcl_vmprim_t; @@ -992,7 +1006,9 @@ struct hcl_t hcl_ooi_t ip; int proc_switched; /* TODO: this is temporary. implement something else to skip immediate context switching */ int switch_proc; - hcl_ntime_t vm_time_offset; + + hcl_ntime_t exec_start_time; + hcl_ntime_t exec_end_time; /* == END EXECUTION REGISTERS == */ /* == BIGINT CONVERSION == */ diff --git a/lib/main.c b/lib/main.c index 9cd409b..d23ac99 100644 --- a/lib/main.c +++ b/lib/main.c @@ -88,6 +88,8 @@ struct xtn_t const char* read_path; /* main source file */ const char* print_path; + int vm_running; + int logfd; int logmask; int logfd_istty; @@ -548,6 +550,316 @@ static void syserrstrb (hcl_t* hcl, int syserr, hcl_bch_t* buf, hcl_oow_t len) #endif } + +static int vm_startup (hcl_t* hcl) +{ +#if defined(_WIN32) + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + xtn->waitable_timer = CreateWaitableTimer(HCL_NULL, TRUE, HCL_NULL); + +#else + + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + int pcount = 0, flag; + +#if defined(USE_DEVPOLL) + xtn->ep = open ("/dev/poll", O_RDWR); + if (xtn->ep == -1) + { + hcl_syserr_to_errnum (errno); + HCL_DEBUG1 (hcl, "Cannot create devpoll - %hs\n", strerror(errno)); + goto oops; + } + + flag = fcntl (xtn->ep, F_GETFD); + if (flag >= 0) fcntl (xtn->ep, F_SETFD, flag | FD_CLOEXEC); + +#elif defined(USE_EPOLL) + #if defined(EPOLL_CLOEXEC) + xtn->ep = epoll_create1 (EPOLL_CLOEXEC); + #else + xtn->ep = epoll_create (1024); + #endif + if (xtn->ep == -1) + { + hcl_syserr_to_errnum (errno); + HCL_DEBUG1 (hcl, "Cannot create epoll - %hs\n", strerror(errno)); + goto oops; + } + + #if defined(EPOLL_CLOEXEC) + /* do nothing */ + #else + flag = fcntl (xtn->ep, F_GETFD); + if (flag >= 0) fcntl (xtn->ep, F_SETFD, flag | FD_CLOEXEC); + #endif + +#elif defined(USE_POLL) + + MUTEX_INIT (&xtn->ev.reg.pmtx); + +#elif defined(USE_SELECT) + FD_ZERO (&xtn->ev.reg.rfds); + FD_ZERO (&xtn->ev.reg.wfds); + xtn->ev.reg.maxfd = -1; + MUTEX_INIT (&xtn->ev.reg.smtx); +#endif /* USE_DEVPOLL */ + +#if defined(USE_THREAD) + if (pipe(xtn->p) == -1) + { + hcl_syserr_to_errnum (errno); + HCL_DEBUG1 (hcl, "Cannot create pipes - %hs\n", strerror(errno)); + goto oops; + } + pcount = 2; + + #if defined(O_CLOEXEC) + flag = fcntl (xtn->p[0], F_GETFD); + if (flag >= 0) fcntl (xtn->p[0], F_SETFD, flag | FD_CLOEXEC); + flag = fcntl (xtn->p[1], F_GETFD); + if (flag >= 0) fcntl (xtn->p[1], F_SETFD, flag | FD_CLOEXEC); + #endif + + #if defined(O_NONBLOCK) + flag = fcntl (xtn->p[0], F_GETFL); + if (flag >= 0) fcntl (xtn->p[0], F_SETFL, flag | O_NONBLOCK); + flag = fcntl (xtn->p[1], F_GETFL); + if (flag >= 0) fcntl (xtn->p[1], F_SETFL, flag | O_NONBLOCK); + #endif + + if (_add_poll_fd(hcl, xtn->p[0], XPOLLIN) <= -1) goto oops; + + pthread_mutex_init (&xtn->ev.mtx, HCL_NULL); + pthread_cond_init (&xtn->ev.cnd, HCL_NULL); + pthread_cond_init (&xtn->ev.cnd2, HCL_NULL); + + xtn->iothr_abort = 0; + xtn->iothr_up = 0; + /*pthread_create (&xtn->iothr, HCL_NULL, iothr_main, hcl);*/ + + +#endif /* USE_THREAD */ + + xtn->vm_running = 1; + return 0; + +oops: + +#if defined(USE_THREAD) + if (pcount > 0) + { + close (xtn->p[0]); + close (xtn->p[1]); + } +#endif + +#if defined(USE_DEVPOLL) || defined(USE_EPOLL) + if (xtn->ep >= 0) + { + close (xtn->ep); + xtn->ep = -1; + } +#endif + + return -1; +#endif +} + +static void vm_cleanup (hcl_t* hcl) +{ +#if defined(_WIN32) + xtn_t* xtn = (xatn_t*)hcl_getxtn(hcl); + if (xtn->waitable_timer) + { + CloseHandle (xtn->waitable_timer); + xtn->waitable_timer = HCL_NULL; + } +#else + xtn_t* xtn = (xtn_t*)hcl_getxtn(hcl); + + xtn->vm_running = 0; + +#if defined(USE_THREAD) + if (xtn->iothr_up) + { + xtn->iothr_abort = 1; + write (xtn->p[1], "Q", 1); + pthread_cond_signal (&xtn->ev.cnd); + pthread_join (xtn->iothr, HCL_NULL); + xtn->iothr_up = 0; + } + pthread_cond_destroy (&xtn->ev.cnd); + pthread_cond_destroy (&xtn->ev.cnd2); + pthread_mutex_destroy (&xtn->ev.mtx); + + _del_poll_fd (hcl, xtn->p[0]); + close (xtn->p[1]); + close (xtn->p[0]); +#endif /* USE_THREAD */ + +#if defined(USE_DEVPOLL) + if (xtn->ep >= 0) + { + close (xtn->ep); + xtn->ep = -1; + } + /*destroy_poll_data_space (hcl);*/ +#elif defined(USE_EPOLL) + if (xtn->ep >= 0) + { + close (xtn->ep); + xtn->ep = -1; + } +#elif defined(USE_POLL) + if (xtn->ev.reg.ptr) + { + hcl_freemem (hcl, xtn->ev.reg.ptr); + xtn->ev.reg.ptr = HCL_NULL; + xtn->ev.reg.len = 0; + xtn->ev.reg.capa = 0; + } + if (xtn->ev.buf) + { + hcl_freemem (hcl, xtn->ev.buf); + xtn->ev.buf = HCL_NULL; + } + /*destroy_poll_data_space (hcl);*/ + MUTEX_DESTROY (&xtn->ev.reg.pmtx); +#elif defined(USE_SELECT) + FD_ZERO (&xtn->ev.reg.rfds); + FD_ZERO (&xtn->ev.reg.wfds); + xtn->ev.reg.maxfd = -1; + MUTEX_DESTROY (&xtn->ev.reg.smtx); +#endif + +#endif +} + +static void vm_gettime (hcl_t* hcl, hcl_ntime_t* now) +{ +#if defined(_WIN32) + /* TODO: */ +#elif defined(__OS2__) + ULONG out; + +/* TODO: handle overflow?? */ +/* TODO: use DosTmrQueryTime() and DosTmrQueryFreq()? */ + DosQuerySysInfo (QSV_MS_COUNT, QSV_MS_COUNT, &out, HCL_SIZEOF(out)); /* milliseconds */ + /* it must return NO_ERROR */ + HCL_INITNTIME (now, HCL_MSEC_TO_SEC(out), HCL_MSEC_TO_NSEC(out)); +#elif defined(__DOS__) && (defined(_INTELC32_) || defined(__WATCOMC__)) + clock_t c; + +/* TODO: handle overflow?? */ + c = clock (); + now->sec = c / CLOCKS_PER_SEC; + #if (CLOCKS_PER_SEC == 100) + now->nsec = HCL_MSEC_TO_NSEC((c % CLOCKS_PER_SEC) * 10); + #elif (CLOCKS_PER_SEC == 1000) + now->nsec = HCL_MSEC_TO_NSEC(c % CLOCKS_PER_SEC); + #elif (CLOCKS_PER_SEC == 1000000L) + now->nsec = HCL_USEC_TO_NSEC(c % CLOCKS_PER_SEC); + #elif (CLOCKS_PER_SEC == 1000000000L) + now->nsec = (c % CLOCKS_PER_SEC); + #else + # error UNSUPPORTED CLOCKS_PER_SEC + #endif +#elif defined(macintosh) + UnsignedWide tick; + hcl_uint64_t tick64; + Microseconds (&tick); + tick64 = *(hcl_uint64_t*)&tick; + HCL_INITNTIME (now, HCL_USEC_TO_SEC(tick64), HCL_USEC_TO_NSEC(tick64)); +#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + struct timespec ts; + clock_gettime (CLOCK_MONOTONIC, &ts); + HCL_INITNTIME(now, ts.tv_sec, ts.tv_nsec); +#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME) + struct timespec ts; + clock_gettime (CLOCK_REALTIME, &ts); + HCL_INITNTIME(now, ts.tv_sec, ts.tv_nsec); +#else + struct timeval tv; + gettimeofday (&tv, HCL_NULL); + HCL_INITNTIME(now, tv.tv_sec, HCL_USEC_TO_NSEC(tv.tv_usec)); +#endif +} + +static void vm_sleep (hcl_t* hcl, const hcl_ntime_t* dur) +{ +#if defined(_WIN32) + xtn_t* xtn = hcl_getxtn(hcl); + if (xtn->waitable_timer) + { + LARGE_INTEGER li; + li.QuadPart = -HCL_SECNSEC_TO_NSEC(dur->sec, dur->nsec); + if(SetWaitableTimer(timer, &li, 0, HCL_NULL, HCL_NULL, FALSE) == FALSE) goto normal_sleep; + WaitForSingleObject(timer, INFINITE); + } + else + { + normal_sleep: + /* fallback to normal Sleep() */ + Sleep (HCL_SECNSEC_TO_MSEC(dur->sec,dur->nsec)); + } +#elif defined(__OS2__) + + /* TODO: in gui mode, this is not a desirable method??? + * this must be made event-driven coupled with the main event loop */ + DosSleep (HCL_SECNSEC_TO_MSEC(dur->sec,dur->nsec)); + +#elif defined(macintosh) + + /* TODO: ... */ + +#elif defined(__DOS__) && (defined(_INTELC32_) || defined(__WATCOMC__)) + + clock_t c; + + c = clock (); + c += dur->sec * CLOCKS_PER_SEC; + + #if (CLOCKS_PER_SEC == 100) + c += HCL_NSEC_TO_MSEC(dur->nsec) / 10; + #elif (CLOCKS_PER_SEC == 1000) + c += HCL_NSEC_TO_MSEC(dur->nsec); + #elif (CLOCKS_PER_SEC == 1000000L) + c += HCL_NSEC_TO_USEC(dur->nsec); + #elif (CLOCKS_PER_SEC == 1000000000L) + c += dur->nsec; + #else + # error UNSUPPORTED CLOCKS_PER_SEC + #endif + +/* TODO: handle clock overvlow */ +/* TODO: check if there is abortion request or interrupt */ + while (c > clock()) + { + _halt_cpu(); + } + +#else + #if defined(USE_THREAD) + /* the sleep callback is called only if there is no IO semaphore + * waiting. so i can safely call vm_muxwait() without a muxwait callback + * when USE_THREAD is true */ + vm_muxwait (hcl, dur, HCL_NULL); + #elif defined(HAVE_NANOSLEEP) + struct timespec ts; + ts.tv_sec = dur->sec; + ts.tv_nsec = dur->nsec; + nanosleep (&ts, HCL_NULL); + #elif defined(HAVE_USLEEP) + usleep (HCL_SECNSEC_TO_USEC(dur->sec, dur->nsec)); + #else + # error UNSUPPORT SLEEP + #endif +#endif +} + +/* ========================================================================= */ + static void fini_hcl (hcl_t* hcl) { xtn_t* xtn = hcl_getxtn(hcl); @@ -906,8 +1218,12 @@ int main (int argc, char* argv[]) memset (&vmprim, 0, HCL_SIZEOF(vmprim)); - vmprim.log_write = log_write; + vmprim.log_write = log_write; vmprim.syserrstrb = syserrstrb; + vmprim.vm_startup = vm_startup; + vmprim.vm_cleanup = vm_cleanup; + vmprim.vm_gettime = vm_gettime; + vmprim.vm_sleep = vm_sleep; hcl = hcl_open (&sys_mmgr, HCL_SIZEOF(xtn_t), 2048000lu, &vmprim, HCL_NULL); if (!hcl)