2702 lines
65 KiB
C
2702 lines
65 KiB
C
|
/*
|
||
|
* $Id$
|
||
|
*
|
||
|
Copyright (c) 2016-2018 Chung, Hyung-Hwan. All rights reserved.
|
||
|
|
||
|
Redistribution and use in source and binary forms, with or without
|
||
|
modification, are permitted provided that the following conditions
|
||
|
are met:
|
||
|
1. Redistributions of source code must retain the above copyright
|
||
|
notice, this list of conditions and the following disclaimer.
|
||
|
2. Redistributions in binary form must reproduce the above copyright
|
||
|
notice, this list of conditions and the following disclaimer in the
|
||
|
documentation and/or other materials provided with the distribution.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*/
|
||
|
|
||
|
#include "hcl-prv.h"
|
||
|
#include <hcl-utl.h>
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
# include <windows.h>
|
||
|
# include <tchar.h>
|
||
|
# include <time.h>
|
||
|
# include <io.h>
|
||
|
# include <errno.h>
|
||
|
|
||
|
# if defined(HCL_HAVE_CFG_H) && defined(HCL_ENABLE_LIBLTDL)
|
||
|
# include <ltdl.h>
|
||
|
# define USE_LTDL
|
||
|
# else
|
||
|
# define USE_WIN_DLL
|
||
|
# endif
|
||
|
|
||
|
|
||
|
# include "poll-msw.h"
|
||
|
# define USE_POLL
|
||
|
# define XPOLLIN POLLIN
|
||
|
# define XPOLLOUT POLLOUT
|
||
|
# define XPOLLERR POLLERR
|
||
|
# define XPOLLHUP POLLHUP
|
||
|
|
||
|
|
||
|
#if !defined(SIZE_T)
|
||
|
# define SIZE_T unsigned long int
|
||
|
#endif
|
||
|
|
||
|
#elif defined(__OS2__)
|
||
|
# define INCL_DOSMODULEMGR
|
||
|
# define INCL_DOSPROCESS
|
||
|
# define INCL_DOSSEMAPHORES
|
||
|
# define INCL_DOSEXCEPTIONS
|
||
|
# define INCL_DOSMISC
|
||
|
# define INCL_DOSDATETIME
|
||
|
# define INCL_DOSFILEMGR
|
||
|
# define INCL_DOSERRORS
|
||
|
# include <os2.h>
|
||
|
# include <time.h>
|
||
|
# include <fcntl.h>
|
||
|
# include <io.h>
|
||
|
|
||
|
# include <errno.h>
|
||
|
|
||
|
/* fake XPOLLXXX values */
|
||
|
# define XPOLLIN (1 << 0)
|
||
|
# define XPOLLOUT (1 << 1)
|
||
|
# define XPOLLERR (1 << 2)
|
||
|
# define XPOLLHUP (1 << 3)
|
||
|
|
||
|
#elif defined(__DOS__)
|
||
|
# include <dos.h>
|
||
|
# include <time.h>
|
||
|
# include <io.h>
|
||
|
# include <signal.h>
|
||
|
# include <errno.h>
|
||
|
# include <fcntl.h>
|
||
|
# include <conio.h> /* inp, outp */
|
||
|
|
||
|
# if defined(_INTELC32_)
|
||
|
# define DOS_EXIT 0x4C
|
||
|
# include <i32.h>
|
||
|
# include <stk.h>
|
||
|
# else
|
||
|
# include <dosfunc.h>
|
||
|
# endif
|
||
|
|
||
|
/* fake XPOLLXXX values */
|
||
|
# define XPOLLIN (1 << 0)
|
||
|
# define XPOLLOUT (1 << 1)
|
||
|
# define XPOLLERR (1 << 2)
|
||
|
# define XPOLLHUP (1 << 3)
|
||
|
|
||
|
#elif defined(macintosh)
|
||
|
# include <Types.h>
|
||
|
# include <OSUtils.h>
|
||
|
# include <Timer.h>
|
||
|
|
||
|
# include <MacErrors.h>
|
||
|
# include <Process.h>
|
||
|
# include <Dialogs.h>
|
||
|
# include <TextUtils.h>
|
||
|
|
||
|
#else
|
||
|
|
||
|
# include <sys/types.h>
|
||
|
# include <unistd.h>
|
||
|
# include <fcntl.h>
|
||
|
# include <errno.h>
|
||
|
|
||
|
# if defined(HAVE_TIME_H)
|
||
|
# include <time.h>
|
||
|
# endif
|
||
|
# if defined(HAVE_SYS_TIME_H)
|
||
|
# include <sys/time.h>
|
||
|
# endif
|
||
|
# if defined(HAVE_SIGNAL_H)
|
||
|
# include <signal.h>
|
||
|
# endif
|
||
|
# if defined(HAVE_SYS_MMAN_H)
|
||
|
# include <sys/mman.h>
|
||
|
# endif
|
||
|
|
||
|
# if defined(HCL_ENABLE_LIBLTDL)
|
||
|
# include <ltdl.h>
|
||
|
# define USE_LTDL
|
||
|
# elif defined(HAVE_DLFCN_H)
|
||
|
# include <dlfcn.h>
|
||
|
# define USE_DLFCN
|
||
|
# elif defined(__APPLE__) || defined(__MACOSX__)
|
||
|
# define USE_MACH_O_DYLD
|
||
|
# include <mach-o/dyld.h>
|
||
|
# else
|
||
|
# error UNSUPPORTED DYNAMIC LINKER
|
||
|
# endif
|
||
|
|
||
|
# if defined(HAVE_TIME_H)
|
||
|
# include <time.h>
|
||
|
# endif
|
||
|
# if defined(HAVE_SYS_TIME_H)
|
||
|
# include <sys/time.h>
|
||
|
# endif
|
||
|
# if defined(HAVE_SIGNAL_H)
|
||
|
# include <signal.h>
|
||
|
# endif
|
||
|
# if defined(HAVE_SYS_MMAN_H)
|
||
|
# include <sys/mman.h>
|
||
|
# endif
|
||
|
|
||
|
# if defined(USE_THREAD)
|
||
|
# include <pthread.h>
|
||
|
# include <sched.h>
|
||
|
# endif
|
||
|
|
||
|
# if defined(HAVE_SYS_DEVPOLL_H)
|
||
|
/* solaris */
|
||
|
# include <sys/devpoll.h>
|
||
|
# define USE_DEVPOLL
|
||
|
# define XPOLLIN POLLIN
|
||
|
# define XPOLLOUT POLLOUT
|
||
|
# define XPOLLERR POLLERR
|
||
|
# define XPOLLHUP POLLHUP
|
||
|
# elif defined(HAVE_SYS_EVENT_H) && defined(HAVE_KQUEUE)
|
||
|
/* netbsd, openbsd, etc */
|
||
|
# include <sys/event.h>
|
||
|
# define USE_KQUEUE
|
||
|
/* fake XPOLLXXX values */
|
||
|
# define XPOLLIN (1 << 0)
|
||
|
# define XPOLLOUT (1 << 1)
|
||
|
# define XPOLLERR (1 << 2)
|
||
|
# define XPOLLHUP (1 << 3)
|
||
|
# elif defined(HAVE_SYS_EPOLL_H) && defined(HAVE_EPOLL_CREATE)
|
||
|
/* linux */
|
||
|
# include <sys/epoll.h>
|
||
|
# define USE_EPOLL
|
||
|
# define XPOLLIN EPOLLIN
|
||
|
# define XPOLLOUT EPOLLOUT
|
||
|
# define XPOLLERR EPOLLERR
|
||
|
# define XPOLLHUP EPOLLHUP
|
||
|
# elif defined(HAVE_POLL_H)
|
||
|
# include <poll.h>
|
||
|
# define USE_POLL
|
||
|
# define XPOLLIN POLLIN
|
||
|
# define XPOLLOUT POLLOUT
|
||
|
# define XPOLLERR POLLERR
|
||
|
# define XPOLLHUP POLLHUP
|
||
|
# else
|
||
|
# define USE_SELECT
|
||
|
/* fake XPOLLXXX values */
|
||
|
# define XPOLLIN (1 << 0)
|
||
|
# define XPOLLOUT (1 << 1)
|
||
|
# define XPOLLERR (1 << 2)
|
||
|
# define XPOLLHUP (1 << 3)
|
||
|
# endif
|
||
|
#endif
|
||
|
|
||
|
#if !defined(HCL_DEFAULT_PFMODDIR)
|
||
|
# define HCL_DEFAULT_PFMODDIR ""
|
||
|
#endif
|
||
|
|
||
|
#if !defined(HCL_DEFAULT_PFMODPREFIX)
|
||
|
# if defined(_WIN32)
|
||
|
# define HCL_DEFAULT_PFMODPREFIX "hcl-"
|
||
|
# elif defined(__OS2__)
|
||
|
# define HCL_DEFAULT_PFMODPREFIX "hcl"
|
||
|
# elif defined(__DOS__)
|
||
|
# define HCL_DEFAULT_PFMODPREFIX "hcl"
|
||
|
# else
|
||
|
# define HCL_DEFAULT_PFMODPREFIX "libhcl-"
|
||
|
# endif
|
||
|
#endif
|
||
|
|
||
|
#if !defined(HCL_DEFAULT_PFMODPOSTFIX)
|
||
|
# if defined(_WIN32)
|
||
|
# define HCL_DEFAULT_PFMODPOSTFIX ""
|
||
|
# elif defined(__OS2__)
|
||
|
# define HCL_DEFAULT_PFMODPOSTFIX ""
|
||
|
# elif defined(__DOS__)
|
||
|
# define HCL_DEFAULT_PFMODPOSTFIX ""
|
||
|
# else
|
||
|
# if defined(USE_DLFCN)
|
||
|
# define HCL_DEFAULT_PFMODPOSTFIX ".so"
|
||
|
# elif defined(USE_MACH_O_DYLD)
|
||
|
# define HCL_DEFAULT_PFMODPOSTFIX ".dylib"
|
||
|
# else
|
||
|
# define HCL_DEFAULT_PFMODPOSTFIX ""
|
||
|
# endif
|
||
|
# endif
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
||
|
# define HCL_IS_PATH_SEP(c) ((c) == '/' || (c) == '\\')
|
||
|
#else
|
||
|
# define HCL_IS_PATH_SEP(c) ((c) == '/')
|
||
|
#endif
|
||
|
|
||
|
/* TODO: handle path with a drive letter or in the UNC notation */
|
||
|
#define HCL_IS_PATH_ABSOLUTE(x) HCL_IS_PATH_SEP(x[0])
|
||
|
|
||
|
|
||
|
|
||
|
typedef struct xtn_t xtn_t;
|
||
|
struct xtn_t
|
||
|
{
|
||
|
hcl_t* next;
|
||
|
hcl_t* prev;
|
||
|
|
||
|
int vm_running;
|
||
|
int rcv_tick;
|
||
|
|
||
|
hcl_cmgr_t* input_cmgr;
|
||
|
hcl_cmgr_t* log_cmgr;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
int fd;
|
||
|
int fd_flag; /* bitwise OR'ed fo logfd_flag_t bits */
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
hcl_bch_t buf[4096];
|
||
|
hcl_oow_t len;
|
||
|
} out;
|
||
|
} log;
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
HANDLE waitable_timer;
|
||
|
DWORD tc_last;
|
||
|
DWORD tc_overflow;
|
||
|
#elif defined(__OS2__)
|
||
|
ULONG tc_last;
|
||
|
hcl_ntime_t tc_last_ret;
|
||
|
#elif defined(__DOS__)
|
||
|
clock_t tc_last;
|
||
|
hcl_ntime_t tc_last_ret;
|
||
|
#endif
|
||
|
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
int ep; /* /dev/poll */
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
int ep; /* kqueue */
|
||
|
#elif defined(USE_EPOLL)
|
||
|
int ep; /* epoll */
|
||
|
#elif defined(USE_POLL)
|
||
|
/* nothing */
|
||
|
#elif defined(USE_SELECT)
|
||
|
/* nothing */
|
||
|
#endif
|
||
|
|
||
|
#if defined(USE_THREAD)
|
||
|
struct
|
||
|
{
|
||
|
int p[2]; /* pipe for signaling */
|
||
|
pthread_t thr;
|
||
|
int up;
|
||
|
int abort;
|
||
|
} iothr;
|
||
|
#endif
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
int p[2]; /* pipe for signaling */
|
||
|
} sigfd;
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
/*TODO: make it dynamically changeable depending on the number of
|
||
|
* file descriptors added */
|
||
|
struct pollfd buf[64]; /* buffer for reading events */
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
struct
|
||
|
{
|
||
|
hcl_oow_t* ptr;
|
||
|
hcl_oow_t capa;
|
||
|
} reg;
|
||
|
struct kevent buf[64];
|
||
|
#elif defined(USE_EPOLL)
|
||
|
/*TODO: make it dynamically changeable depending on the number of
|
||
|
* file descriptors added */
|
||
|
struct epoll_event buf[64]; /* buffer for reading events */
|
||
|
#elif defined(USE_POLL)
|
||
|
struct
|
||
|
{
|
||
|
struct pollfd* ptr;
|
||
|
hcl_oow_t capa;
|
||
|
hcl_oow_t len;
|
||
|
#if defined(USE_THREAD)
|
||
|
pthread_mutex_t pmtx;
|
||
|
#endif
|
||
|
} reg; /* registry */
|
||
|
struct pollfd* buf;
|
||
|
|
||
|
#elif defined(USE_SELECT)
|
||
|
struct
|
||
|
{
|
||
|
fd_set rfds;
|
||
|
fd_set wfds;
|
||
|
int maxfd;
|
||
|
#if defined(USE_THREAD)
|
||
|
pthread_mutex_t smtx;
|
||
|
#endif
|
||
|
} reg;
|
||
|
|
||
|
struct select_fd_t buf[FD_SETSIZE];
|
||
|
#endif
|
||
|
|
||
|
hcl_oow_t len;
|
||
|
|
||
|
#if defined(USE_THREAD)
|
||
|
pthread_mutex_t mtx;
|
||
|
pthread_cond_t cnd;
|
||
|
pthread_cond_t cnd2;
|
||
|
#endif
|
||
|
|
||
|
int halting;
|
||
|
} ev;
|
||
|
};
|
||
|
|
||
|
#define GET_XTN(hcl) ((xtn_t*)((hcl_uint8_t*)hcl_getxtn(hcl) - HCL_SIZEOF(xtn_t)))
|
||
|
|
||
|
|
||
|
/* -----------------------------------------------------------------
|
||
|
* BASIC MEMORY MANAGER
|
||
|
* ----------------------------------------------------------------- */
|
||
|
|
||
|
static void* sys_alloc (hcl_mmgr_t* mmgr, hcl_oow_t size)
|
||
|
{
|
||
|
return malloc(size);
|
||
|
}
|
||
|
|
||
|
static void* sys_realloc (hcl_mmgr_t* mmgr, void* ptr, hcl_oow_t size)
|
||
|
{
|
||
|
return realloc(ptr, size);
|
||
|
}
|
||
|
|
||
|
static void sys_free (hcl_mmgr_t* mmgr, void* ptr)
|
||
|
{
|
||
|
free (ptr);
|
||
|
}
|
||
|
|
||
|
static hcl_mmgr_t sys_mmgr =
|
||
|
{
|
||
|
sys_alloc,
|
||
|
sys_realloc,
|
||
|
sys_free,
|
||
|
HCL_NULL
|
||
|
};
|
||
|
|
||
|
/* -----------------------------------------------------------------
|
||
|
* LOGGING SUPPORT
|
||
|
* ----------------------------------------------------------------- */
|
||
|
|
||
|
enum logfd_flag_t
|
||
|
{
|
||
|
LOGFD_TTY = (1 << 0),
|
||
|
LOGFD_OPENED_HERE = (1 << 1)
|
||
|
};
|
||
|
|
||
|
static int write_all (int fd, const hcl_bch_t* ptr, hcl_oow_t len)
|
||
|
{
|
||
|
while (len > 0)
|
||
|
{
|
||
|
hcl_ooi_t wr;
|
||
|
|
||
|
wr = write(fd, ptr, len);
|
||
|
|
||
|
if (wr <= -1)
|
||
|
{
|
||
|
#if defined(EAGAIN) && defined(EWOULDBLOCK) && (EAGAIN == EWOULDBLOCK)
|
||
|
if (errno == EAGAIN) continue;
|
||
|
#else
|
||
|
#if defined(EAGAIN)
|
||
|
if (errno == EAGAIN) continue;
|
||
|
#elif defined(EWOULDBLOCK)
|
||
|
if (errno == EWOULDBLOCK) continue;
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#if defined(EINTR)
|
||
|
/* TODO: would this interfere with non-blocking nature of this VM? */
|
||
|
if (errno == EINTR) continue;
|
||
|
#endif
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
ptr += wr;
|
||
|
len -= wr;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int write_log (hcl_t* hcl, int fd, const hcl_bch_t* ptr, hcl_oow_t len)
|
||
|
{
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
|
||
|
while (len > 0)
|
||
|
{
|
||
|
if (xtn->log.out.len > 0)
|
||
|
{
|
||
|
hcl_oow_t rcapa, cplen;
|
||
|
|
||
|
rcapa = HCL_COUNTOF(xtn->log.out.buf) - xtn->log.out.len;
|
||
|
cplen = (len >= rcapa)? rcapa: len;
|
||
|
|
||
|
HCL_MEMCPY (&xtn->log.out.buf[xtn->log.out.len], ptr, cplen);
|
||
|
xtn->log.out.len += cplen;
|
||
|
ptr += cplen;
|
||
|
len -= cplen;
|
||
|
|
||
|
if (xtn->log.out.len >= HCL_COUNTOF(xtn->log.out.buf))
|
||
|
{
|
||
|
int n;
|
||
|
n = write_all(fd, xtn->log.out.buf, xtn->log.out.len);
|
||
|
xtn->log.out.len = 0;
|
||
|
if (n <= -1) return -1;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hcl_oow_t rcapa;
|
||
|
|
||
|
rcapa = HCL_COUNTOF(xtn->log.out.buf);
|
||
|
if (len >= rcapa)
|
||
|
{
|
||
|
if (write_all(fd, ptr, rcapa) <= -1) return -1;
|
||
|
ptr += rcapa;
|
||
|
len -= rcapa;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
HCL_MEMCPY (xtn->log.out.buf, ptr, len);
|
||
|
xtn->log.out.len += len;
|
||
|
ptr += len;
|
||
|
len -= len;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void flush_log (hcl_t* hcl, int fd)
|
||
|
{
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
if (xtn->log.out.len > 0)
|
||
|
{
|
||
|
write_all (fd, xtn->log.out.buf, xtn->log.out.len);
|
||
|
xtn->log.out.len = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void log_write (hcl_t* hcl, hcl_bitmask_t mask, const hcl_ooch_t* msg, hcl_oow_t len)
|
||
|
{
|
||
|
hcl_bch_t buf[256];
|
||
|
hcl_oow_t ucslen, bcslen, msgidx;
|
||
|
int n;
|
||
|
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
int logfd;
|
||
|
|
||
|
if (mask & HCL_LOG_STDERR) logfd = 2;
|
||
|
else if (mask & HCL_LOG_STDOUT) logfd = 1;
|
||
|
else
|
||
|
{
|
||
|
logfd = xtn->log.fd;
|
||
|
if (logfd <= -1) return;
|
||
|
}
|
||
|
|
||
|
/* TODO: beautify the log message.
|
||
|
* do classification based on mask. */
|
||
|
if (!(mask & (HCL_LOG_STDOUT | HCL_LOG_STDERR)))
|
||
|
{
|
||
|
time_t now;
|
||
|
char ts[32];
|
||
|
size_t tslen;
|
||
|
struct tm tm, *tmp;
|
||
|
|
||
|
now = time(HCL_NULL);
|
||
|
#if defined(_WIN32)
|
||
|
tmp = localtime(&now);
|
||
|
tslen = strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S %z ", tmp);
|
||
|
if (tslen == 0)
|
||
|
{
|
||
|
tslen = sprintf(ts, "%04d-%02d-%02d %02d:%02d:%02d ", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
|
||
|
}
|
||
|
#elif defined(__OS2__)
|
||
|
#if defined(__WATCOMC__)
|
||
|
tmp = _localtime(&now, &tm);
|
||
|
#else
|
||
|
tmp = localtime(&now);
|
||
|
#endif
|
||
|
|
||
|
#if defined(__BORLANDC__)
|
||
|
/* the borland compiler doesn't handle %z properly - it showed 00 all the time */
|
||
|
tslen = strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S %Z ", tmp);
|
||
|
#else
|
||
|
tslen = strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S %z ", tmp);
|
||
|
#endif
|
||
|
if (tslen == 0)
|
||
|
{
|
||
|
tslen = sprintf(ts, "%04d-%02d-%02d %02d:%02d:%02d ", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
|
||
|
}
|
||
|
|
||
|
#elif defined(__DOS__)
|
||
|
tmp = localtime(&now);
|
||
|
/* since i know that %z/%Z is not available in strftime, i switch to sprintf immediately */
|
||
|
tslen = sprintf(ts, "%04d-%02d-%02d %02d:%02d:%02d ", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
|
||
|
#else
|
||
|
#if defined(HAVE_LOCALTIME_R)
|
||
|
tmp = localtime_r(&now, &tm);
|
||
|
#else
|
||
|
tmp = localtime(&now);
|
||
|
#endif
|
||
|
|
||
|
#if defined(HAVE_STRFTIME_SMALL_Z)
|
||
|
tslen = strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S %z ", tmp);
|
||
|
#else
|
||
|
tslen = strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S %Z ", tmp);
|
||
|
#endif
|
||
|
if (tslen == 0)
|
||
|
{
|
||
|
tslen = sprintf(ts, "%04d-%02d-%02d %02d:%02d:%02d ", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
|
||
|
}
|
||
|
#endif
|
||
|
write_log (hcl, logfd, ts, tslen);
|
||
|
}
|
||
|
|
||
|
if (logfd == xtn->log.fd && (xtn->log.fd_flag & LOGFD_TTY))
|
||
|
{
|
||
|
if (mask & HCL_LOG_FATAL) write_log (hcl, logfd, "\x1B[1;31m", 7);
|
||
|
else if (mask & HCL_LOG_ERROR) write_log (hcl, logfd, "\x1B[1;32m", 7);
|
||
|
else if (mask & HCL_LOG_WARN) write_log (hcl, logfd, "\x1B[1;33m", 7);
|
||
|
}
|
||
|
|
||
|
#if defined(HCL_OOCH_IS_UCH)
|
||
|
msgidx = 0;
|
||
|
while (len > 0)
|
||
|
{
|
||
|
ucslen = len;
|
||
|
bcslen = HCL_COUNTOF(buf);
|
||
|
|
||
|
n = hcl_convootobchars(hcl, &msg[msgidx], &ucslen, buf, &bcslen);
|
||
|
if (n == 0 || n == -2)
|
||
|
{
|
||
|
/* n = 0:
|
||
|
* converted all successfully
|
||
|
* n == -2:
|
||
|
* buffer not sufficient. not all got converted yet.
|
||
|
* write what have been converted this round. */
|
||
|
|
||
|
HCL_ASSERT (hcl, ucslen > 0); /* if this fails, the buffer size must be increased */
|
||
|
|
||
|
/* attempt to write all converted characters */
|
||
|
if (write_log(hcl, logfd, buf, bcslen) <= -1) break;
|
||
|
|
||
|
if (n == 0) break;
|
||
|
else
|
||
|
{
|
||
|
msgidx += ucslen;
|
||
|
len -= ucslen;
|
||
|
}
|
||
|
}
|
||
|
else if (n <= -1)
|
||
|
{
|
||
|
/* conversion error */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
write_log (hcl, logfd, msg, len);
|
||
|
#endif
|
||
|
|
||
|
if (logfd == xtn->log.fd && (xtn->log.fd_flag & LOGFD_TTY))
|
||
|
{
|
||
|
if (mask & (HCL_LOG_FATAL | HCL_LOG_ERROR | HCL_LOG_WARN)) write_log (hcl, logfd, "\x1B[0m", 4);
|
||
|
}
|
||
|
|
||
|
flush_log (hcl, logfd);
|
||
|
}
|
||
|
|
||
|
/* -----------------------------------------------------------------
|
||
|
* SYSTEM ERROR CONVERSION
|
||
|
* ----------------------------------------------------------------- */
|
||
|
static hcl_errnum_t errno_to_errnum (int errcode)
|
||
|
{
|
||
|
switch (errcode)
|
||
|
{
|
||
|
case ENOMEM: return HCL_ESYSMEM;
|
||
|
case EINVAL: return HCL_EINVAL;
|
||
|
|
||
|
#if defined(EBUSY)
|
||
|
case EBUSY: return HCL_EBUSY;
|
||
|
#endif
|
||
|
case EACCES: return HCL_EACCES;
|
||
|
#if defined(EPERM)
|
||
|
case EPERM: return HCL_EPERM;
|
||
|
#endif
|
||
|
#if defined(ENOTDIR)
|
||
|
case ENOTDIR: return HCL_ENOTDIR;
|
||
|
#endif
|
||
|
case ENOENT: return HCL_ENOENT;
|
||
|
#if defined(EEXIST)
|
||
|
case EEXIST: return HCL_EEXIST;
|
||
|
#endif
|
||
|
#if defined(EINTR)
|
||
|
case EINTR: return HCL_EINTR;
|
||
|
#endif
|
||
|
|
||
|
#if defined(EPIPE)
|
||
|
case EPIPE: return HCL_EPIPE;
|
||
|
#endif
|
||
|
|
||
|
#if defined(EAGAIN) && defined(EWOULDBLOCK) && (EAGAIN != EWOULDBLOCK)
|
||
|
case EAGAIN:
|
||
|
case EWOULDBLOCK: return HCL_EAGAIN;
|
||
|
#elif defined(EAGAIN)
|
||
|
case EAGAIN: return HCL_EAGAIN;
|
||
|
#elif defined(EWOULDBLOCK)
|
||
|
case EWOULDBLOCK: return HCL_EAGAIN;
|
||
|
#endif
|
||
|
|
||
|
#if defined(EBADF)
|
||
|
case EBADF: return HCL_EBADHND;
|
||
|
#endif
|
||
|
|
||
|
#if defined(EIO)
|
||
|
case EIO: return HCL_EIOERR;
|
||
|
#endif
|
||
|
|
||
|
default: return HCL_ESYSERR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
static hcl_errnum_t winerr_to_errnum (DWORD errcode)
|
||
|
{
|
||
|
switch (errcode)
|
||
|
{
|
||
|
case ERROR_NOT_ENOUGH_MEMORY:
|
||
|
case ERROR_OUTOFMEMORY:
|
||
|
return HCL_ESYSMEM;
|
||
|
|
||
|
case ERROR_INVALID_PARAMETER:
|
||
|
case ERROR_INVALID_NAME:
|
||
|
return HCL_EINVAL;
|
||
|
|
||
|
case ERROR_INVALID_HANDLE:
|
||
|
return HCL_EBADHND;
|
||
|
|
||
|
case ERROR_ACCESS_DENIED:
|
||
|
case ERROR_SHARING_VIOLATION:
|
||
|
return HCL_EACCES;
|
||
|
|
||
|
case ERROR_FILE_NOT_FOUND:
|
||
|
case ERROR_PATH_NOT_FOUND:
|
||
|
return HCL_ENOENT;
|
||
|
|
||
|
case ERROR_ALREADY_EXISTS:
|
||
|
case ERROR_FILE_EXISTS:
|
||
|
return HCL_EEXIST;
|
||
|
|
||
|
case ERROR_BROKEN_PIPE:
|
||
|
return HCL_EPIPE;
|
||
|
|
||
|
default:
|
||
|
return HCL_ESYSERR;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if defined(__OS2__)
|
||
|
static hcl_errnum_t os2err_to_errnum (APIRET errcode)
|
||
|
{
|
||
|
/* APIRET e */
|
||
|
switch (errcode)
|
||
|
{
|
||
|
case ERROR_NOT_ENOUGH_MEMORY:
|
||
|
return HCL_ESYSMEM;
|
||
|
|
||
|
case ERROR_INVALID_PARAMETER:
|
||
|
case ERROR_INVALID_NAME:
|
||
|
return HCL_EINVAL;
|
||
|
|
||
|
case ERROR_INVALID_HANDLE:
|
||
|
return HCL_EBADHND;
|
||
|
|
||
|
case ERROR_ACCESS_DENIED:
|
||
|
case ERROR_SHARING_VIOLATION:
|
||
|
return HCL_EACCES;
|
||
|
|
||
|
case ERROR_FILE_NOT_FOUND:
|
||
|
case ERROR_PATH_NOT_FOUND:
|
||
|
return HCL_ENOENT;
|
||
|
|
||
|
case ERROR_ALREADY_EXISTS:
|
||
|
return HCL_EEXIST;
|
||
|
|
||
|
/*TODO: add more mappings */
|
||
|
default:
|
||
|
return HCL_ESYSERR;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if defined(macintosh)
|
||
|
static hcl_errnum_t macerr_to_errnum (int errcode)
|
||
|
{
|
||
|
switch (e)
|
||
|
{
|
||
|
case notEnoughMemoryErr:
|
||
|
return HCL_ESYSMEM;
|
||
|
case paramErr:
|
||
|
return HCL_EINVAL;
|
||
|
|
||
|
case qErr: /* queue element not found during deletion */
|
||
|
case fnfErr: /* file not found */
|
||
|
case dirNFErr: /* direcotry not found */
|
||
|
case resNotFound: /* resource not found */
|
||
|
case resFNotFound: /* resource file not found */
|
||
|
case nbpNotFound: /* name not found on remove */
|
||
|
return HCL_ENOENT;
|
||
|
|
||
|
/*TODO: add more mappings */
|
||
|
default:
|
||
|
return HCL_ESYSERR;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static hcl_errnum_t _syserrstrb (hcl_t* hcl, int syserr_type, int syserr_code, hcl_bch_t* buf, hcl_oow_t len)
|
||
|
{
|
||
|
switch (syserr_type)
|
||
|
{
|
||
|
case 1:
|
||
|
#if defined(_WIN32)
|
||
|
if (buf)
|
||
|
{
|
||
|
DWORD rc;
|
||
|
rc = FormatMessageA (
|
||
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||
|
NULL, syserr_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||
|
buf, len, HCL_NULL
|
||
|
);
|
||
|
while (rc > 0 && buf[rc - 1] == '\r' || buf[rc - 1] == '\n') buf[--rc] = '\0';
|
||
|
}
|
||
|
return winerr_to_errnum(syserr_code);
|
||
|
#elif defined(__OS2__)
|
||
|
/* TODO: convert code to string */
|
||
|
if (buf) hcl_copy_bcstr (buf, len, "system error");
|
||
|
return os2err_to_errnum(syserr_code);
|
||
|
#elif defined(macintosh)
|
||
|
/* TODO: convert code to string */
|
||
|
if (buf) hcl_copy_bcstr (buf, len, "system error");
|
||
|
return os2err_to_errnum(syserr_code);
|
||
|
#else
|
||
|
/* in other systems, errno is still the native system error code.
|
||
|
* fall thru */
|
||
|
#endif
|
||
|
|
||
|
case 0:
|
||
|
#if defined(HAVE_STRERROR_R)
|
||
|
if (buf) strerror_r (syserr_code, buf, len);
|
||
|
#else
|
||
|
/* this is not thread safe */
|
||
|
if (buf) hcl_copy_bcstr (buf, len, strerror(syserr_code));
|
||
|
#endif
|
||
|
return errno_to_errnum(syserr_code);
|
||
|
}
|
||
|
|
||
|
if (buf) hcl_copy_bcstr (buf, len, "system error");
|
||
|
return HCL_ESYSERR;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* --------------------------------------------------------------------------
|
||
|
* ASSERTION SUPPORT
|
||
|
* -------------------------------------------------------------------------- */
|
||
|
|
||
|
#if defined(HCL_BUILD_RELEASE)
|
||
|
|
||
|
static void _assertfail (hcl_t* hcl, const hcl_bch_t* expr, const hcl_bch_t* file, hcl_oow_t line)
|
||
|
{
|
||
|
/* do nothing */
|
||
|
}
|
||
|
|
||
|
#else /* defined(HCL_BUILD_RELEASE) */
|
||
|
|
||
|
/* --------------------------------------------------------------------------
|
||
|
* SYSTEM DEPENDENT HEADERS
|
||
|
* -------------------------------------------------------------------------- */
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
# include <windows.h>
|
||
|
# include <errno.h>
|
||
|
#elif defined(__OS2__)
|
||
|
|
||
|
# define INCL_DOSERRORS
|
||
|
# include <os2.h>
|
||
|
#elif defined(__DOS__)
|
||
|
# include <dos.h>
|
||
|
# if defined(_INTELC32_)
|
||
|
# define DOS_EXIT 0x4C
|
||
|
# else
|
||
|
# include <dosfunc.h>
|
||
|
# endif
|
||
|
# include <errno.h>
|
||
|
#elif defined(vms) || defined(__vms)
|
||
|
# define __NEW_STARLET 1
|
||
|
# include <starlet.h> /* (SYS$...) */
|
||
|
# include <ssdef.h> /* (SS$...) */
|
||
|
# include <lib$routines.h> /* (lib$...) */
|
||
|
#elif defined(macintosh)
|
||
|
# include <MacErrors.h>
|
||
|
# include <Process.h>
|
||
|
# include <Dialogs.h>
|
||
|
# include <TextUtils.h>
|
||
|
#else
|
||
|
# include <sys/types.h>
|
||
|
# include <unistd.h>
|
||
|
# include <signal.h>
|
||
|
# include <errno.h>
|
||
|
#endif
|
||
|
|
||
|
#if defined(HCL_ENABLE_LIBUNWIND)
|
||
|
#include <libunwind.h>
|
||
|
static void backtrace_stack_frames (hcl_t* hcl)
|
||
|
{
|
||
|
unw_cursor_t cursor;
|
||
|
unw_context_t context;
|
||
|
int n;
|
||
|
|
||
|
unw_getcontext(&context);
|
||
|
unw_init_local(&cursor, &context);
|
||
|
|
||
|
hcl_logbfmt (hcl, HCL_LOG_UNTYPED | HCL_LOG_DEBUG, "[BACKTRACE]\n");
|
||
|
for (n = 0; unw_step(&cursor) > 0; n++)
|
||
|
{
|
||
|
unw_word_t ip, sp, off;
|
||
|
char symbol[256];
|
||
|
|
||
|
unw_get_reg (&cursor, UNW_REG_IP, &ip);
|
||
|
unw_get_reg (&cursor, UNW_REG_SP, &sp);
|
||
|
|
||
|
if (unw_get_proc_name(&cursor, symbol, HCL_COUNTOF(symbol), &off))
|
||
|
{
|
||
|
hcl_copy_bcstr (symbol, HCL_COUNTOF(symbol), "<unknown>");
|
||
|
}
|
||
|
|
||
|
hcl_logbfmt (hcl, HCL_LOG_UNTYPED | HCL_LOG_DEBUG,
|
||
|
"#%02d ip=0x%*p sp=0x%*p %s+0x%zu\n",
|
||
|
n, HCL_SIZEOF(void*) * 2, (void*)ip, HCL_SIZEOF(void*) * 2, (void*)sp, symbol, (hcl_oow_t)off);
|
||
|
}
|
||
|
}
|
||
|
#elif defined(HAVE_BACKTRACE)
|
||
|
#include <execinfo.h>
|
||
|
static void backtrace_stack_frames (hcl_t* hcl)
|
||
|
{
|
||
|
void* btarray[128];
|
||
|
hcl_oow_t btsize;
|
||
|
char** btsyms;
|
||
|
|
||
|
btsize = backtrace (btarray, HCL_COUNTOF(btarray));
|
||
|
btsyms = backtrace_symbols (btarray, btsize);
|
||
|
if (btsyms)
|
||
|
{
|
||
|
hcl_oow_t i;
|
||
|
hcl_logbfmt (hcl, HCL_LOG_UNTYPED | HCL_LOG_DEBUG, "[BACKTRACE]\n");
|
||
|
|
||
|
for (i = 0; i < btsize; i++)
|
||
|
{
|
||
|
hcl_logbfmt(hcl, HCL_LOG_UNTYPED | HCL_LOG_DEBUG, " %s\n", btsyms[i]);
|
||
|
}
|
||
|
free (btsyms);
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
static void backtrace_stack_frames (hcl_t* hcl)
|
||
|
{
|
||
|
/* do nothing. not supported */
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static void _assertfail (hcl_t* hcl, const hcl_bch_t* expr, const hcl_bch_t* file, hcl_oow_t line)
|
||
|
{
|
||
|
hcl_logbfmt (hcl, HCL_LOG_UNTYPED | HCL_LOG_FATAL, "ASSERTION FAILURE: %s at %s:%zu\n", expr, file, line);
|
||
|
backtrace_stack_frames (hcl);
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
ExitProcess (249);
|
||
|
#elif defined(__OS2__)
|
||
|
DosExit (EXIT_PROCESS, 249);
|
||
|
#elif defined(__DOS__)
|
||
|
{
|
||
|
union REGS regs;
|
||
|
regs.h.ah = DOS_EXIT;
|
||
|
regs.h.al = 249;
|
||
|
intdos (®s, ®s);
|
||
|
}
|
||
|
#elif defined(vms) || defined(__vms)
|
||
|
lib$stop (SS$_ABORT); /* use SS$_OPCCUS instead? */
|
||
|
/* this won't be reached since lib$stop() terminates the process */
|
||
|
sys$exit (SS$_ABORT); /* this condition code can be shown with
|
||
|
* 'show symbol $status' from the command-line. */
|
||
|
#elif defined(macintosh)
|
||
|
|
||
|
ExitToShell ();
|
||
|
|
||
|
#else
|
||
|
|
||
|
kill (getpid(), SIGABRT);
|
||
|
_exit (1);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#endif /* defined(HCL_BUILD_RELEASE) */
|
||
|
|
||
|
|
||
|
/* -----------------------------------------------------------------
|
||
|
* HEAP ALLOCATION
|
||
|
* ----------------------------------------------------------------- */
|
||
|
|
||
|
static void* alloc_heap (hcl_t* hcl, hcl_oow_t size)
|
||
|
{
|
||
|
#if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) && defined(MAP_ANONYMOUS)
|
||
|
/* It's called via hcl_makeheap() when HCL creates a GC heap.
|
||
|
* The heap is large in size. I can use a different memory allocation
|
||
|
* function instead of an ordinary malloc.
|
||
|
* upon failure, it doesn't require to set error information as hcl_makeheap()
|
||
|
* set the error number to HCL_EOOMEM. */
|
||
|
|
||
|
#if !defined(MAP_HUGETLB) && (defined(__amd64__) || defined(__x86_64__))
|
||
|
# define MAP_HUGETLB 0x40000
|
||
|
#endif
|
||
|
|
||
|
hcl_oow_t* ptr;
|
||
|
int flags;
|
||
|
hcl_oow_t actual_size;
|
||
|
|
||
|
flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||
|
|
||
|
#if defined(MAP_HUGETLB)
|
||
|
flags |= MAP_HUGETLB;
|
||
|
#endif
|
||
|
|
||
|
#if defined(MAP_UNINITIALIZED)
|
||
|
flags |= MAP_UNINITIALIZED;
|
||
|
#endif
|
||
|
|
||
|
actual_size = HCL_SIZEOF(hcl_oow_t) + size;
|
||
|
actual_size = HCL_ALIGN_POW2(actual_size, 2 * 1024 * 1024);
|
||
|
ptr = (hcl_oow_t*)mmap(NULL, actual_size, PROT_READ | PROT_WRITE, flags, -1, 0);
|
||
|
if (ptr == MAP_FAILED)
|
||
|
{
|
||
|
#if defined(MAP_HUGETLB)
|
||
|
flags &= ~MAP_HUGETLB;
|
||
|
ptr = (hcl_oow_t*)mmap(NULL, actual_size, PROT_READ | PROT_WRITE, flags, -1, 0);
|
||
|
if (ptr == MAP_FAILED) return HCL_NULL;
|
||
|
#else
|
||
|
return HCL_NULL;
|
||
|
#endif
|
||
|
}
|
||
|
*ptr = actual_size;
|
||
|
|
||
|
return (void*)(ptr + 1);
|
||
|
|
||
|
#else
|
||
|
return HCL_MMGR_ALLOC(hcl->_mmgr, size);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void free_heap (hcl_t* hcl, void* ptr)
|
||
|
{
|
||
|
#if defined(HAVE_MMAP) && defined(HAVE_MUNMAP)
|
||
|
hcl_oow_t* actual_ptr;
|
||
|
actual_ptr = (hcl_oow_t*)ptr - 1;
|
||
|
munmap (actual_ptr, *actual_ptr);
|
||
|
#else
|
||
|
return HCL_MMGR_FREE(hcl->_mmgr, ptr);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* -----------------------------------------------------------------
|
||
|
* POSSIBLY MONOTONIC TIME
|
||
|
* ----------------------------------------------------------------- */
|
||
|
|
||
|
void vm_gettime (hcl_t* hcl, hcl_ntime_t* now)
|
||
|
{
|
||
|
#if defined(_WIN32)
|
||
|
|
||
|
#if defined(_WIN64) || (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600))
|
||
|
hcl_uint64_t bigsec, bigmsec;
|
||
|
bigmsec = GetTickCount64();
|
||
|
#else
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
hcl_uint64_t bigsec, bigmsec;
|
||
|
DWORD msec;
|
||
|
|
||
|
msec = GetTickCount(); /* this can sustain for 49.7 days */
|
||
|
if (msec < xtn->tc_last)
|
||
|
{
|
||
|
/* i assume the difference is never bigger than 49.7 days */
|
||
|
/*diff = (HCL_TYPE_MAX(DWORD) - xtn->tc_last) + 1 + msec;*/
|
||
|
xtn->tc_overflow++;
|
||
|
bigmsec = ((hcl_uint64_t)HCL_TYPE_MAX(DWORD) * xtn->tc_overflow) + msec;
|
||
|
}
|
||
|
else bigmsec = msec;
|
||
|
xtn->tc_last = msec;
|
||
|
#endif
|
||
|
|
||
|
bigsec = HCL_MSEC_TO_SEC(bigmsec);
|
||
|
bigmsec -= HCL_SEC_TO_MSEC(bigsec);
|
||
|
HCL_INIT_NTIME(now, bigsec, HCL_MSEC_TO_NSEC(bigmsec));
|
||
|
|
||
|
#elif defined(__OS2__)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
hcl_uint64_t bigsec, bigmsec;
|
||
|
ULONG msec;
|
||
|
|
||
|
/* TODO: use DosTmrQueryTime() and DosTmrQueryFreq()? */
|
||
|
DosQuerySysInfo (QSV_MS_COUNT, QSV_MS_COUNT, &msec, HCL_SIZEOF(msec)); /* milliseconds */
|
||
|
/* it must return NO_ERROR */
|
||
|
if (msec < xtn->tc_last)
|
||
|
{
|
||
|
xtn->tc_overflow++;
|
||
|
bigmsec = ((hcl_uint64_t)HCL_TYPE_MAX(ULONG) * xtn->tc_overflow) + msec;
|
||
|
}
|
||
|
else bigmsec = msec;
|
||
|
xtn->tc_last = msec;
|
||
|
|
||
|
bigsec = HCL_MSEC_TO_SEC(bigmsec);
|
||
|
bigmsec -= HCL_SEC_TO_MSEC(bigsec);
|
||
|
HCL_INIT_NTIME (now, bigsec, HCL_MSEC_TO_NSEC(bigmsec));
|
||
|
|
||
|
#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_INIT_NTIME (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_INIT_NTIME(now, ts.tv_sec, ts.tv_nsec);
|
||
|
#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)
|
||
|
struct timespec ts;
|
||
|
clock_gettime (CLOCK_REALTIME, &ts);
|
||
|
HCL_INIT_NTIME(now, ts.tv_sec, ts.tv_nsec);
|
||
|
#else
|
||
|
struct timeval tv;
|
||
|
gettimeofday (&tv, HCL_NULL);
|
||
|
HCL_INIT_NTIME(now, tv.tv_sec, HCL_USEC_TO_NSEC(tv.tv_usec));
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* -----------------------------------------------------------------
|
||
|
* IO MULTIPLEXING
|
||
|
* ----------------------------------------------------------------- */
|
||
|
|
||
|
static int _add_poll_fd (hcl_t* hcl, int fd, int event_mask)
|
||
|
{
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
struct pollfd ev;
|
||
|
|
||
|
HCL_ASSERT (hcl, xtn->ep >= 0);
|
||
|
ev.fd = fd;
|
||
|
ev.events = event_mask;
|
||
|
ev.revents = 0;
|
||
|
if (write(xtn->ep, &ev, HCL_SIZEOF(ev)) != HCL_SIZEOF(ev))
|
||
|
{
|
||
|
hcl_seterrwithsyserr (hcl, 0, errno);
|
||
|
HCL_DEBUG2 (hcl, "Cannot add file descriptor %d to devpoll - %hs\n", fd, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
struct kevent ev;
|
||
|
hcl_oow_t rindex, roffset;
|
||
|
hcl_oow_t rv = 0;
|
||
|
|
||
|
rindex = (hcl_oow_t)fd / (HCL_BITSOF(hcl_oow_t) >> 1);
|
||
|
roffset = ((hcl_oow_t)fd << 1) % HCL_BITSOF(hcl_oow_t);
|
||
|
|
||
|
if (rindex >= xtn->ev.reg.capa)
|
||
|
{
|
||
|
hcl_oow_t* tmp;
|
||
|
hcl_oow_t newcapa;
|
||
|
|
||
|
HCL_STATIC_ASSERT (HCL_SIZEOF(*tmp) == HCL_SIZEOF(*xtn->ev.reg.ptr));
|
||
|
|
||
|
newcapa = rindex + 1;
|
||
|
newcapa = HCL_ALIGN_POW2(newcapa, 16);
|
||
|
|
||
|
tmp = (hcl_oow_t*)hcl_reallocmem(hcl, xtn->ev.reg.ptr, newcapa * HCL_SIZEOF(*tmp));
|
||
|
if (!tmp)
|
||
|
{
|
||
|
const hcl_ooch_t* oldmsg = hcl_backuperrmsg(hcl);
|
||
|
hcl_seterrbfmt (hcl, HCL_ESYSERR, "unable to add file descriptor %d to kqueue - %js", fd, oldmsg);
|
||
|
HCL_DEBUG1 (hcl, "%js", hcl_geterrmsg(hcl));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
HCL_MEMSET (&tmp[xtn->ev.reg.capa], 0, newcapa - xtn->ev.reg.capa);
|
||
|
xtn->ev.reg.ptr = tmp;
|
||
|
xtn->ev.reg.capa = newcapa;
|
||
|
}
|
||
|
|
||
|
if (event_mask & XPOLLIN)
|
||
|
{
|
||
|
/*EV_SET (&ev, fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, 0);*/
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.ident = fd;
|
||
|
ev.flags = EV_ADD;
|
||
|
#if defined(USE_THREAD)
|
||
|
ev.flags |= EV_CLEAR; /* EV_CLEAR for edge trigger? */
|
||
|
#endif
|
||
|
ev.filter = EVFILT_READ;
|
||
|
if (kevent(xtn->ep, &ev, 1, HCL_NULL, 0, HCL_NULL) == -1)
|
||
|
{
|
||
|
hcl_seterrwithsyserr (hcl, 0, errno);
|
||
|
HCL_DEBUG2 (hcl, "Cannot add file descriptor %d to kqueue for read - %hs\n", fd, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
rv |= 1;
|
||
|
}
|
||
|
if (event_mask & XPOLLOUT)
|
||
|
{
|
||
|
/*EV_SET (&ev, fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, 0);*/
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.ident = fd;
|
||
|
ev.flags = EV_ADD;
|
||
|
#if defined(USE_THREAD)
|
||
|
ev.flags |= EV_CLEAR; /* EV_CLEAR for edge trigger? */
|
||
|
#endif
|
||
|
ev.filter = EVFILT_WRITE;
|
||
|
if (kevent(xtn->ep, &ev, 1, HCL_NULL, 0, HCL_NULL) == -1)
|
||
|
{
|
||
|
hcl_seterrwithsyserr (hcl, 0, errno);
|
||
|
HCL_DEBUG2 (hcl, "Cannot add file descriptor %d to kqueue for write - %hs\n", fd, strerror(errno));
|
||
|
|
||
|
if (event_mask & XPOLLIN)
|
||
|
{
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.ident = fd;
|
||
|
ev.flags = EV_DELETE;
|
||
|
ev.filter = EVFILT_READ;
|
||
|
kevent(xtn->ep, &ev, 1, HCL_NULL, 0, HCL_NULL);
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
rv |= 2;
|
||
|
}
|
||
|
|
||
|
HCL_SETBITS (hcl_oow_t, xtn->ev.reg.ptr[rindex], roffset, 2, rv);
|
||
|
return 0;
|
||
|
|
||
|
#elif defined(USE_EPOLL)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
struct epoll_event ev;
|
||
|
|
||
|
HCL_ASSERT (hcl, xtn->ep >= 0);
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.events = event_mask;
|
||
|
#if defined(USE_THREAD) && defined(EPOLLET)
|
||
|
/* epoll_wait may return again if the worker thread consumes events.
|
||
|
* switch to level-trigger. */
|
||
|
/* TODO: verify if EPOLLLET is desired */
|
||
|
ev.events |= EPOLLET/* | EPOLLRDHUP | EPOLLHUP */;
|
||
|
#endif
|
||
|
/*ev.data.ptr = (void*)event_data;*/
|
||
|
ev.data.fd = fd;
|
||
|
if (epoll_ctl(xtn->ep, EPOLL_CTL_ADD, fd, &ev) == -1)
|
||
|
{
|
||
|
hcl_seterrwithsyserr (hcl, 0, errno);
|
||
|
HCL_DEBUG2 (hcl, "Cannot add file descriptor %d to epoll - %hs\n", fd, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
#elif defined(USE_POLL)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
|
||
|
MUTEX_LOCK (&xtn->ev.reg.pmtx);
|
||
|
if (xtn->ev.reg.len >= xtn->ev.reg.capa)
|
||
|
{
|
||
|
struct pollfd* tmp, * tmp2;
|
||
|
hcl_oow_t newcapa;
|
||
|
|
||
|
newcapa = HCL_ALIGN_POW2 (xtn->ev.reg.len + 1, 256);
|
||
|
tmp = (struct pollfd*)hcl_reallocmem(hcl, xtn->ev.reg.ptr, newcapa * HCL_SIZEOF(*tmp));
|
||
|
tmp2 = (struct pollfd*)hcl_reallocmem(hcl, xtn->ev.buf, newcapa * HCL_SIZEOF(*tmp2));
|
||
|
if (!tmp || !tmp2)
|
||
|
{
|
||
|
HCL_DEBUG2 (hcl, "Cannot add file descriptor %d to poll - %hs\n", fd, strerror(errno));
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.pmtx);
|
||
|
if (tmp) hcl_freemem (hcl, tmp);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
xtn->ev.reg.ptr = tmp;
|
||
|
xtn->ev.reg.capa = newcapa;
|
||
|
|
||
|
xtn->ev.buf = tmp2;
|
||
|
}
|
||
|
|
||
|
xtn->ev.reg.ptr[xtn->ev.reg.len].fd = fd;
|
||
|
xtn->ev.reg.ptr[xtn->ev.reg.len].events = event_mask;
|
||
|
xtn->ev.reg.ptr[xtn->ev.reg.len].revents = 0;
|
||
|
xtn->ev.reg.len++;
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.pmtx);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
#elif defined(USE_SELECT)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
|
||
|
MUTEX_LOCK (&xtn->ev.reg.smtx);
|
||
|
if (event_mask & XPOLLIN)
|
||
|
{
|
||
|
FD_SET (fd, &xtn->ev.reg.rfds);
|
||
|
if (fd > xtn->ev.reg.maxfd) xtn->ev.reg.maxfd = fd;
|
||
|
}
|
||
|
if (event_mask & XPOLLOUT)
|
||
|
{
|
||
|
FD_SET (fd, &xtn->ev.reg.wfds);
|
||
|
if (fd > xtn->ev.reg.maxfd) xtn->ev.reg.maxfd = fd;
|
||
|
}
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.smtx);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
#else
|
||
|
|
||
|
HCL_DEBUG1 (hcl, "Cannot add file descriptor %d to poll - not implemented\n", fd);
|
||
|
hcl_seterrnum (hcl, HCL_ENOIMPL);
|
||
|
return -1;
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
static int _del_poll_fd (hcl_t* hcl, int fd)
|
||
|
{
|
||
|
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
struct pollfd ev;
|
||
|
|
||
|
HCL_ASSERT (hcl, xtn->ep >= 0);
|
||
|
ev.fd = fd;
|
||
|
ev.events = POLLREMOVE;
|
||
|
ev.revents = 0;
|
||
|
if (write(xtn->ep, &ev, HCL_SIZEOF(ev)) != HCL_SIZEOF(ev))
|
||
|
{
|
||
|
hcl_seterrwithsyserr (hcl, 0, errno);
|
||
|
HCL_DEBUG2 (hcl, "Cannot remove file descriptor %d from devpoll - %hs\n", fd, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
hcl_oow_t rindex, roffset;
|
||
|
int rv;
|
||
|
struct kevent ev;
|
||
|
|
||
|
rindex = (hcl_oow_t)fd / (HCL_BITSOF(hcl_oow_t) >> 1);
|
||
|
roffset = ((hcl_oow_t)fd << 1) % HCL_BITSOF(hcl_oow_t);
|
||
|
|
||
|
if (rindex >= xtn->ev.reg.capa)
|
||
|
{
|
||
|
hcl_seterrbfmt (hcl, HCL_EINVAL, "unknown file descriptor %d", fd);
|
||
|
HCL_DEBUG2 (hcl, "Cannot remove file descriptor %d from kqueue - %js\n", fd, hcl_geterrmsg(hcl));
|
||
|
return -1;
|
||
|
};
|
||
|
|
||
|
rv = HCL_GETBITS (hcl_oow_t, xtn->ev.reg.ptr[rindex], roffset, 2);
|
||
|
|
||
|
if (rv & 1)
|
||
|
{
|
||
|
/*EV_SET (&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);*/
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.ident = fd;
|
||
|
ev.flags = EV_DELETE;
|
||
|
ev.filter = EVFILT_READ;
|
||
|
kevent(xtn->ep, &ev, 1, HCL_NULL, 0, HCL_NULL);
|
||
|
/* no error check for now */
|
||
|
}
|
||
|
|
||
|
if (rv & 2)
|
||
|
{
|
||
|
/*EV_SET (&ev, fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);*/
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.ident = fd;
|
||
|
ev.flags = EV_DELETE;
|
||
|
ev.filter = EVFILT_WRITE;
|
||
|
kevent(xtn->ep, &ev, 1, HCL_NULL, 0, HCL_NULL);
|
||
|
/* no error check for now */
|
||
|
}
|
||
|
|
||
|
HCL_SETBITS (hcl_oow_t, xtn->ev.reg.ptr[rindex], roffset, 2, 0);
|
||
|
return 0;
|
||
|
|
||
|
#elif defined(USE_EPOLL)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
struct epoll_event ev;
|
||
|
|
||
|
HCL_ASSERT (hcl, xtn->ep >= 0);
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
if (epoll_ctl(xtn->ep, EPOLL_CTL_DEL, fd, &ev) == -1)
|
||
|
{
|
||
|
hcl_seterrwithsyserr (hcl, 0, errno);
|
||
|
HCL_DEBUG2 (hcl, "Cannot remove file descriptor %d from epoll - %hs\n", fd, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
#elif defined(USE_POLL)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
hcl_oow_t i;
|
||
|
|
||
|
/* TODO: performance boost. no linear search */
|
||
|
MUTEX_LOCK (&xtn->ev.reg.pmtx);
|
||
|
for (i = 0; i < xtn->ev.reg.len; i++)
|
||
|
{
|
||
|
if (xtn->ev.reg.ptr[i].fd == fd)
|
||
|
{
|
||
|
xtn->ev.reg.len--;
|
||
|
HCL_MEMMOVE (&xtn->ev.reg.ptr[i], &xtn->ev.reg.ptr[i+1], (xtn->ev.reg.len - i) * HCL_SIZEOF(*xtn->ev.reg.ptr));
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.pmtx);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.pmtx);
|
||
|
|
||
|
|
||
|
HCL_DEBUG1 (hcl, "Cannot remove file descriptor %d from poll - not found\n", fd);
|
||
|
hcl_seterrnum (hcl, HCL_ENOENT);
|
||
|
return -1;
|
||
|
|
||
|
#elif defined(USE_SELECT)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
|
||
|
MUTEX_LOCK (&xtn->ev.reg.smtx);
|
||
|
FD_CLR (fd, &xtn->ev.reg.rfds);
|
||
|
FD_CLR (fd, &xtn->ev.reg.wfds);
|
||
|
if (fd >= xtn->ev.reg.maxfd)
|
||
|
{
|
||
|
int i;
|
||
|
/* TODO: any way to make this search faster or to do without the search like this */
|
||
|
for (i = fd - 1; i >= 0; i--)
|
||
|
{
|
||
|
if (FD_ISSET(i, &xtn->ev.reg.rfds) || FD_ISSET(i, &xtn->ev.reg.wfds)) break;
|
||
|
}
|
||
|
xtn->ev.reg.maxfd = i;
|
||
|
}
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.smtx);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
#else
|
||
|
|
||
|
HCL_DEBUG1 (hcl, "Cannot remove file descriptor %d from poll - not implemented\n", fd);
|
||
|
hcl_seterrnum (hcl, HCL_ENOIMPL);
|
||
|
return -1;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
static int _mod_poll_fd (hcl_t* hcl, int fd, int event_mask)
|
||
|
{
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
|
||
|
if (_del_poll_fd (hcl, fd) <= -1) return -1;
|
||
|
|
||
|
if (_add_poll_fd (hcl, fd, event_mask) <= -1)
|
||
|
{
|
||
|
/* TODO: any good way to rollback successful deletion? */
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
hcl_oow_t rindex, roffset;
|
||
|
int rv, newrv = 0;
|
||
|
struct kevent ev;
|
||
|
|
||
|
rindex = (hcl_oow_t)fd / (HCL_BITSOF(hcl_oow_t) >> 1);
|
||
|
roffset = ((hcl_oow_t)fd << 1) % HCL_BITSOF(hcl_oow_t);
|
||
|
|
||
|
if (rindex >= xtn->ev.reg.capa)
|
||
|
{
|
||
|
hcl_seterrbfmt (hcl, HCL_EINVAL, "unknown file descriptor %d", fd);
|
||
|
HCL_DEBUG2 (hcl, "Cannot modify file descriptor %d in kqueue - %js\n", fd, hcl_geterrmsg(hcl));
|
||
|
return -1;
|
||
|
};
|
||
|
|
||
|
rv = HCL_GETBITS(hcl_oow_t, xtn->ev.reg.ptr[rindex], roffset, 2);
|
||
|
|
||
|
if (rv & 1)
|
||
|
{
|
||
|
if (!(event_mask & XPOLLIN))
|
||
|
{
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.ident = fd;
|
||
|
ev.flags = EV_DELETE;
|
||
|
ev.filter = EVFILT_READ;
|
||
|
if (kevent(xtn->ep, &ev, 1, HCL_NULL, 0, HCL_NULL) == -1) goto kqueue_syserr;
|
||
|
|
||
|
newrv &= ~1;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (event_mask & XPOLLIN)
|
||
|
{
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.ident = fd;
|
||
|
ev.flags = EV_ADD;
|
||
|
#if defined(USE_THREAD)
|
||
|
ev.flags |= EV_CLEAR; /* EV_CLEAR for edge trigger? */
|
||
|
#endif
|
||
|
ev.filter = EVFILT_READ;
|
||
|
if (kevent(xtn->ep, &ev, 1, HCL_NULL, 0, HCL_NULL) == -1) goto kqueue_syserr;
|
||
|
|
||
|
newrv |= 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (rv & 2)
|
||
|
{
|
||
|
if (!(event_mask & XPOLLOUT))
|
||
|
{
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.ident = fd;
|
||
|
ev.flags = EV_DELETE;
|
||
|
ev.filter = EVFILT_WRITE;
|
||
|
/* there is no operation rollback for the (rv & 1) case.
|
||
|
* the rollback action may fail again even if i try it */
|
||
|
if (kevent(xtn->ep, &ev, 1, HCL_NULL, 0, HCL_NULL) == -1) goto kqueue_syserr;
|
||
|
|
||
|
newrv &= ~2;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (event_mask & XPOLLOUT)
|
||
|
{
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.ident = fd;
|
||
|
ev.flags = EV_ADD;
|
||
|
#if defined(USE_THREAD)
|
||
|
ev.flags |= EV_CLEAR; /* EV_CLEAR for edge trigger? */
|
||
|
#endif
|
||
|
ev.filter = EVFILT_WRITE;
|
||
|
|
||
|
/* there is no operation rollback for the (rv & 1) case.
|
||
|
* the rollback action may fail again even if i try it */
|
||
|
if (kevent(xtn->ep, &ev, 1, HCL_NULL, 0, HCL_NULL) == -1) goto kqueue_syserr;
|
||
|
|
||
|
newrv |= 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HCL_SETBITS (hcl_oow_t, xtn->ev.reg.ptr[rindex], roffset, 2, newrv);
|
||
|
return 0;
|
||
|
|
||
|
kqueue_syserr:
|
||
|
hcl_seterrwithsyserr (hcl, 0, errno);
|
||
|
HCL_DEBUG2 (hcl, "Cannot modify file descriptor %d in kqueue - %hs\n", fd, strerror(errno));
|
||
|
return -1;
|
||
|
|
||
|
#elif defined(USE_EPOLL)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
struct epoll_event ev;
|
||
|
|
||
|
HCL_ASSERT (hcl, xtn->ep >= 0);
|
||
|
HCL_MEMSET (&ev, 0, HCL_SIZEOF(ev));
|
||
|
ev.events = event_mask;
|
||
|
#if defined(USE_THREAD) && defined(EPOLLET)
|
||
|
/* epoll_wait may return again if the worker thread consumes events.
|
||
|
* switch to level-trigger. */
|
||
|
/* TODO: verify if EPOLLLET is desired */
|
||
|
ev.events |= EPOLLET;
|
||
|
#endif
|
||
|
ev.data.fd = fd;
|
||
|
if (epoll_ctl(xtn->ep, EPOLL_CTL_MOD, fd, &ev) == -1)
|
||
|
{
|
||
|
hcl_seterrwithsyserr (hcl, 0, errno);
|
||
|
HCL_DEBUG2 (hcl, "Cannot modify file descriptor %d in epoll - %hs\n", fd, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
#elif defined(USE_POLL)
|
||
|
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
hcl_oow_t i;
|
||
|
|
||
|
MUTEX_LOCK (&xtn->ev.reg.pmtx);
|
||
|
for (i = 0; i < xtn->ev.reg.len; i++)
|
||
|
{
|
||
|
if (xtn->ev.reg.ptr[i].fd == fd)
|
||
|
{
|
||
|
HCL_MEMMOVE (&xtn->ev.reg.ptr[i], &xtn->ev.reg.ptr[i+1], (xtn->ev.reg.len - i - 1) * HCL_SIZEOF(*xtn->ev.reg.ptr));
|
||
|
xtn->ev.reg.ptr[i].fd = fd;
|
||
|
xtn->ev.reg.ptr[i].events = event_mask;
|
||
|
xtn->ev.reg.ptr[i].revents = 0;
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.pmtx);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.pmtx);
|
||
|
|
||
|
HCL_DEBUG1 (hcl, "Cannot modify file descriptor %d in poll - not found\n", fd);
|
||
|
hcl_seterrnum (hcl, HCL_ENOENT);
|
||
|
return -1;
|
||
|
|
||
|
#elif defined(USE_SELECT)
|
||
|
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
|
||
|
MUTEX_LOCK (&xtn->ev.reg.smtx);
|
||
|
HCL_ASSERT (hcl, fd <= xtn->ev.reg.maxfd);
|
||
|
|
||
|
if (event_mask & XPOLLIN)
|
||
|
FD_SET (fd, &xtn->ev.reg.rfds);
|
||
|
else
|
||
|
FD_CLR (fd, &xtn->ev.reg.rfds);
|
||
|
|
||
|
if (event_mask & XPOLLOUT)
|
||
|
FD_SET (fd, &xtn->ev.reg.wfds);
|
||
|
else
|
||
|
FD_CLR (fd, &xtn->ev.reg.wfds);
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.smtx);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
#else
|
||
|
HCL_DEBUG1 (hcl, "Cannot modify file descriptor %d in poll - not implemented\n", fd);
|
||
|
hcl_seterrnum (hcl, HCL_ENOIMPL);
|
||
|
return -1;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static int vm_muxadd (hcl_t* hcl, hcl_ooi_t io_handle, hcl_ooi_t mask)
|
||
|
{
|
||
|
int event_mask;
|
||
|
|
||
|
event_mask = 0;
|
||
|
if (mask & HCL_SEMAPHORE_IO_MASK_INPUT) event_mask |= XPOLLIN;
|
||
|
if (mask & HCL_SEMAPHORE_IO_MASK_OUTPUT) event_mask |= XPOLLOUT;
|
||
|
|
||
|
if (event_mask == 0)
|
||
|
{
|
||
|
HCL_DEBUG2 (hcl, "<vm_muxadd> Invalid semaphore mask %zd on handle %zd\n", mask, io_handle);
|
||
|
hcl_seterrbfmt (hcl, HCL_EINVAL, "invalid semaphore mask %zd on handle %zd", mask, io_handle);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return _add_poll_fd(hcl, io_handle, event_mask);
|
||
|
}
|
||
|
|
||
|
static int vm_muxmod (hcl_t* hcl, hcl_ooi_t io_handle, hcl_ooi_t mask)
|
||
|
{
|
||
|
int event_mask;
|
||
|
|
||
|
event_mask = 0;
|
||
|
if (mask & HCL_SEMAPHORE_IO_MASK_INPUT) event_mask |= XPOLLIN;
|
||
|
if (mask & HCL_SEMAPHORE_IO_MASK_OUTPUT) event_mask |= XPOLLOUT;
|
||
|
|
||
|
if (event_mask == 0)
|
||
|
{
|
||
|
HCL_DEBUG2 (hcl, "<vm_muxadd> Invalid semaphore mask %zd on handle %zd\n", mask, io_handle);
|
||
|
hcl_seterrbfmt (hcl, HCL_EINVAL, "invalid semaphore mask %zd on handle %zd", mask, io_handle);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return _mod_poll_fd(hcl, io_handle, event_mask);
|
||
|
}
|
||
|
|
||
|
static int vm_muxdel (hcl_t* hcl, hcl_ooi_t io_handle)
|
||
|
{
|
||
|
return _del_poll_fd(hcl, io_handle);
|
||
|
}
|
||
|
|
||
|
#if defined(USE_THREAD)
|
||
|
static void* iothr_main (void* arg)
|
||
|
{
|
||
|
hcl_t* hcl = (hcl_t*)arg;
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
|
||
|
/*while (!hcl->abort_req)*/
|
||
|
while (!xtn->iothr.abort)
|
||
|
{
|
||
|
if (xtn->ev.len <= 0) /* TODO: no mutex needed for this check? */
|
||
|
{
|
||
|
int n;
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
struct dvpoll dvp;
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
struct timespec ts;
|
||
|
#elif defined(USE_POLL)
|
||
|
hcl_oow_t nfds;
|
||
|
#elif defined(USE_SELECT)
|
||
|
struct timeval tv;
|
||
|
fd_set rfds;
|
||
|
fd_set wfds;
|
||
|
int maxfd;
|
||
|
#endif
|
||
|
|
||
|
poll_for_event:
|
||
|
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
dvp.dp_timeout = 10000; /* milliseconds */
|
||
|
dvp.dp_fds = xtn->ev.buf;
|
||
|
dvp.dp_nfds = HCL_COUNTOF(xtn->ev.buf);
|
||
|
n = ioctl (xtn->ep, DP_POLL, &dvp);
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
ts.tv_sec = 10;
|
||
|
ts.tv_nsec = 0;
|
||
|
n = kevent(xtn->ep, HCL_NULL, 0, xtn->ev.buf, HCL_COUNTOF(xtn->ev.buf), &ts);
|
||
|
/* n == 0: timeout
|
||
|
* n == -1: error */
|
||
|
#elif defined(USE_EPOLL)
|
||
|
n = epoll_wait(xtn->ep, xtn->ev.buf, HCL_COUNTOF(xtn->ev.buf), 10000); /* TODO: make this timeout value in the io thread */
|
||
|
#elif defined(USE_POLL)
|
||
|
MUTEX_LOCK (&xtn->ev.reg.pmtx);
|
||
|
HCL_MEMCPY (xtn->ev.buf, xtn->ev.reg.ptr, xtn->ev.reg.len * HCL_SIZEOF(*xtn->ev.buf));
|
||
|
nfds = xtn->ev.reg.len;
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.pmtx);
|
||
|
n = poll(xtn->ev.buf, nfds, 10000);
|
||
|
if (n > 0)
|
||
|
{
|
||
|
/* compact the return buffer as poll() doesn't */
|
||
|
hcl_oow_t i, j;
|
||
|
for (i = 0, j = 0; i < nfds && j < n; i++)
|
||
|
{
|
||
|
if (xtn->ev.buf[i].revents)
|
||
|
{
|
||
|
if (j != i) xtn->ev.buf[j] = xtn->ev.buf[i];
|
||
|
j++;
|
||
|
}
|
||
|
}
|
||
|
n = j;
|
||
|
}
|
||
|
#elif defined(USE_SELECT)
|
||
|
tv.tv_sec = 10;
|
||
|
tv.tv_usec = 0;
|
||
|
MUTEX_LOCK (&xtn->ev.reg.smtx);
|
||
|
maxfd = xtn->ev.reg.maxfd;
|
||
|
HCL_MEMCPY (&rfds, &xtn->ev.reg.rfds, HCL_SIZEOF(rfds));
|
||
|
HCL_MEMCPY (&wfds, &xtn->ev.reg.wfds, HCL_SIZEOF(wfds));
|
||
|
MUTEX_UNLOCK (&xtn->ev.reg.smtx);
|
||
|
n = select (maxfd + 1, &rfds, &wfds, HCL_NULL, &tv);
|
||
|
if (n > 0)
|
||
|
{
|
||
|
int fd, count = 0;
|
||
|
for (fd = 0; fd <= maxfd; fd++)
|
||
|
{
|
||
|
int events = 0;
|
||
|
if (FD_ISSET(fd, &rfds)) events |= XPOLLIN;
|
||
|
if (FD_ISSET(fd, &wfds)) events |= XPOLLOUT;
|
||
|
|
||
|
if (events)
|
||
|
{
|
||
|
HCL_ASSERT (hcl, count < HCL_COUNTOF(xtn->ev.buf));
|
||
|
xtn->ev.buf[count].fd = fd;
|
||
|
xtn->ev.buf[count].events = events;
|
||
|
count++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
n = count;
|
||
|
HCL_ASSERT (hcl, n > 0);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
pthread_mutex_lock (&xtn->ev.mtx);
|
||
|
if (n <= -1)
|
||
|
{
|
||
|
/* TODO: don't use HCL_DEBUG2. it's not thread safe... */
|
||
|
/* the following call has a race-condition issue when called in this separate thread */
|
||
|
/*HCL_DEBUG2 (hcl, "Warning: multiplexer wait failure - %d, %hs\n", errno, strerror(errno));*/
|
||
|
}
|
||
|
else if (n > 0)
|
||
|
{
|
||
|
xtn->ev.len = n;
|
||
|
}
|
||
|
pthread_cond_signal (&xtn->ev.cnd2);
|
||
|
pthread_mutex_unlock (&xtn->ev.mtx);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* the event buffer has not been emptied yet */
|
||
|
struct timespec ts;
|
||
|
|
||
|
pthread_mutex_lock (&xtn->ev.mtx);
|
||
|
if (xtn->ev.len <= 0)
|
||
|
{
|
||
|
/* it got emptied between the if check and pthread_mutex_lock() above */
|
||
|
pthread_mutex_unlock (&xtn->ev.mtx);
|
||
|
goto poll_for_event;
|
||
|
}
|
||
|
|
||
|
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)
|
||
|
clock_gettime (CLOCK_REALTIME, &ts);
|
||
|
#else
|
||
|
{
|
||
|
struct timeval tv;
|
||
|
gettimeofday (&tv, HCL_NULL);
|
||
|
ts.tv_sec = tv.tv_sec;
|
||
|
ts.tv_nsec = HCL_USEC_TO_NSEC(tv.tv_usec);
|
||
|
}
|
||
|
#endif
|
||
|
ts.tv_sec += 10;
|
||
|
pthread_cond_timedwait (&xtn->ev.cnd, &xtn->ev.mtx, &ts);
|
||
|
pthread_mutex_unlock (&xtn->ev.mtx);
|
||
|
}
|
||
|
|
||
|
/*sched_yield ();*/
|
||
|
}
|
||
|
|
||
|
return HCL_NULL;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static void vm_muxwait (hcl_t* hcl, const hcl_ntime_t* dur, hcl_vmprim_muxwait_cb_t muxwcb)
|
||
|
{
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
|
||
|
#if defined(USE_THREAD)
|
||
|
int n;
|
||
|
|
||
|
/* create a thread if mux wait is started at least once. */
|
||
|
if (!xtn->iothr.up)
|
||
|
{
|
||
|
xtn->iothr.up = 1;
|
||
|
if (pthread_create(&xtn->iothr.thr, HCL_NULL, iothr_main, hcl) != 0)
|
||
|
{
|
||
|
HCL_LOG2 (hcl, HCL_LOG_WARN, "Warning: pthread_create failure - %d, %hs\n", errno, strerror(errno));
|
||
|
xtn->iothr.up = 0;
|
||
|
/* TODO: switch to the non-threaded mode? */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (xtn->iothr.abort) return;
|
||
|
|
||
|
if (xtn->ev.len <= 0)
|
||
|
{
|
||
|
struct timespec ts;
|
||
|
hcl_ntime_t ns;
|
||
|
|
||
|
if (!dur) return; /* immediate check is requested. and there is no event */
|
||
|
|
||
|
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)
|
||
|
clock_gettime (CLOCK_REALTIME, &ts);
|
||
|
ns.sec = ts.tv_sec;
|
||
|
ns.nsec = ts.tv_nsec;
|
||
|
#else
|
||
|
{
|
||
|
struct timeval tv;
|
||
|
gettimeofday (&tv, HCL_NULL);
|
||
|
ns.sec = tv.tv_sec;
|
||
|
ns.nsec = HCL_USEC_TO_NSEC(tv.tv_usec);
|
||
|
}
|
||
|
#endif
|
||
|
HCL_ADD_NTIME (&ns, &ns, dur);
|
||
|
ts.tv_sec = ns.sec;
|
||
|
ts.tv_nsec = ns.nsec;
|
||
|
|
||
|
pthread_mutex_lock (&xtn->ev.mtx);
|
||
|
if (xtn->ev.len <= 0)
|
||
|
{
|
||
|
/* the event buffer is still empty */
|
||
|
pthread_cond_timedwait (&xtn->ev.cnd2, &xtn->ev.mtx, &ts);
|
||
|
}
|
||
|
pthread_mutex_unlock (&xtn->ev.mtx);
|
||
|
}
|
||
|
|
||
|
n = xtn->ev.len;
|
||
|
|
||
|
if (n > 0)
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
--n;
|
||
|
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
if (xtn->ev.buf[n].fd == xtn->iothr.p[0])
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
if (xtn->ev.buf[n].ident == xtn->iothr.p[0])
|
||
|
#elif defined(USE_EPOLL)
|
||
|
/*if (xtn->ev.buf[n].data.ptr == (void*)HCL_TYPE_MAX(hcl_oow_t))*/
|
||
|
if (xtn->ev.buf[n].data.fd == xtn->iothr.p[0])
|
||
|
#elif defined(USE_POLL)
|
||
|
if (xtn->ev.buf[n].fd == xtn->iothr.p[0])
|
||
|
#elif defined(USE_SELECT)
|
||
|
if (xtn->ev.buf[n].fd == xtn->iothr.p[0])
|
||
|
#else
|
||
|
# error UNSUPPORTED
|
||
|
#endif
|
||
|
{
|
||
|
hcl_uint8_t u8;
|
||
|
while (read(xtn->iothr.p[0], &u8, HCL_SIZEOF(u8)) > 0)
|
||
|
{
|
||
|
/* consume as much as possible */;
|
||
|
if (u8 == 'Q') xtn->iothr.abort = 1;
|
||
|
}
|
||
|
}
|
||
|
else if (muxwcb)
|
||
|
{
|
||
|
int revents;
|
||
|
hcl_ooi_t mask;
|
||
|
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
revents = xtn->ev.buf[n].revents;
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
if (xtn->ev.buf[n].filter == EVFILT_READ) mask = HCL_SEMAPHORE_IO_MASK_INPUT;
|
||
|
else if (xtn->ev.buf[n].filter == EVFILT_WRITE) mask = HCL_SEMAPHORE_IO_MASK_OUTPUT;
|
||
|
else mask = 0;
|
||
|
goto call_muxwcb_kqueue;
|
||
|
#elif defined(USE_EPOLL)
|
||
|
revents = xtn->ev.buf[n].events;
|
||
|
#elif defined(USE_POLL)
|
||
|
revents = xtn->ev.buf[n].revents;
|
||
|
#elif defined(USE_SELECT)
|
||
|
revents = xtn->ev.buf[n].events;
|
||
|
#endif
|
||
|
|
||
|
mask = 0;
|
||
|
if (revents & XPOLLIN) mask |= HCL_SEMAPHORE_IO_MASK_INPUT;
|
||
|
if (revents & XPOLLOUT) mask |= HCL_SEMAPHORE_IO_MASK_OUTPUT;
|
||
|
if (revents & XPOLLERR) mask |= HCL_SEMAPHORE_IO_MASK_ERROR;
|
||
|
if (revents & XPOLLHUP) mask |= HCL_SEMAPHORE_IO_MASK_HANGUP;
|
||
|
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
muxwcb (hcl, xtn->ev.buf[n].fd, mask);
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
call_muxwcb_kqueue:
|
||
|
muxwcb (hcl, xtn->ev.buf[n].ident, mask);
|
||
|
#elif defined(USE_EPOLL)
|
||
|
muxwcb (hcl, xtn->ev.buf[n].data.fd, mask);
|
||
|
#elif defined(USE_POLL)
|
||
|
muxwcb (hcl, xtn->ev.buf[n].fd, mask);
|
||
|
#elif defined(USE_SELECT)
|
||
|
muxwcb (hcl, xtn->ev.buf[n].fd, mask);
|
||
|
#else
|
||
|
# error UNSUPPORTED
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
while (n > 0);
|
||
|
|
||
|
pthread_mutex_lock (&xtn->ev.mtx);
|
||
|
xtn->ev.len = 0;
|
||
|
pthread_cond_signal (&xtn->ev.cnd);
|
||
|
pthread_mutex_unlock (&xtn->ev.mtx);
|
||
|
}
|
||
|
|
||
|
#else /* USE_THREAD */
|
||
|
int n;
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
int tmout;
|
||
|
struct dvpoll dvp;
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
struct timespec ts;
|
||
|
#elif defined(USE_EPOLL)
|
||
|
int tmout;
|
||
|
#elif defined(USE_POLL)
|
||
|
int tmout;
|
||
|
#elif defined(USE_SELECT)
|
||
|
struct timeval tv;
|
||
|
fd_set rfds, wfds;
|
||
|
int maxfd;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
tmout = dur? HCL_SECNSEC_TO_MSEC(dur->sec, dur->nsec): 0;
|
||
|
|
||
|
dvp.dp_timeout = tmout; /* milliseconds */
|
||
|
dvp.dp_fds = xtn->ev.buf;
|
||
|
dvp.dp_nfds = HCL_COUNTOF(xtn->ev.buf);
|
||
|
n = ioctl(xtn->ep, DP_POLL, &dvp);
|
||
|
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
|
||
|
if (dur)
|
||
|
{
|
||
|
ts.tv_sec = dur->sec;
|
||
|
ts.tv_nsec = dur->nsec;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ts.tv_sec = 0;
|
||
|
ts.tv_nsec = 0;
|
||
|
}
|
||
|
|
||
|
n = kevent(xtn->ep, HCL_NULL, 0, xtn->ev.buf, HCL_COUNTOF(xtn->ev.buf), &ts);
|
||
|
/* n == 0: timeout
|
||
|
* n == -1: error */
|
||
|
|
||
|
#elif defined(USE_EPOLL)
|
||
|
tmout = dur? HCL_SECNSEC_TO_MSEC(dur->sec, dur->nsec): 0;
|
||
|
n = epoll_wait(xtn->ep, xtn->ev.buf, HCL_COUNTOF(xtn->ev.buf), tmout);
|
||
|
|
||
|
#elif defined(USE_POLL)
|
||
|
tmout = dur? HCL_SECNSEC_TO_MSEC(dur->sec, dur->nsec): 0;
|
||
|
HCL_MEMCPY (xtn->ev.buf, xtn->ev.reg.ptr, xtn->ev.reg.len * HCL_SIZEOF(*xtn->ev.buf));
|
||
|
n = poll(xtn->ev.buf, xtn->ev.reg.len, tmout);
|
||
|
if (n > 0)
|
||
|
{
|
||
|
/* compact the return buffer as poll() doesn't */
|
||
|
hcl_oow_t i, j;
|
||
|
for (i = 0, j = 0; i < xtn->ev.reg.len && j < n; i++)
|
||
|
{
|
||
|
if (xtn->ev.buf[i].revents)
|
||
|
{
|
||
|
if (j != i) xtn->ev.buf[j] = xtn->ev.buf[i];
|
||
|
j++;
|
||
|
}
|
||
|
}
|
||
|
n = j;
|
||
|
}
|
||
|
#elif defined(USE_SELECT)
|
||
|
if (dur)
|
||
|
{
|
||
|
tv.tv_sec = dur->sec;
|
||
|
tv.tv_usec = HCL_NSEC_TO_USEC(dur->nsec);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tv.tv_sec = 0;
|
||
|
tv.tv_usec = 0;
|
||
|
}
|
||
|
maxfd = xtn->ev.reg.maxfd;
|
||
|
HCL_MEMCPY (&rfds, &xtn->ev.reg.rfds, HCL_SIZEOF(rfds));
|
||
|
HCL_MEMCPY (&wfds, &xtn->ev.reg.wfds, HCL_SIZEOF(wfds));
|
||
|
n = select(maxfd + 1, &rfds, &wfds, HCL_NULL, &tv);
|
||
|
if (n > 0)
|
||
|
{
|
||
|
int fd, count = 0;
|
||
|
for (fd = 0; fd <= maxfd; fd++)
|
||
|
{
|
||
|
int events = 0;
|
||
|
if (FD_ISSET(fd, &rfds)) events |= XPOLLIN;
|
||
|
if (FD_ISSET(fd, &wfds)) events |= XPOLLOUT;
|
||
|
|
||
|
if (events)
|
||
|
{
|
||
|
HCL_ASSERT (hcl, count < HCL_COUNTOF(xtn->ev.buf));
|
||
|
xtn->ev.buf[count].fd = fd;
|
||
|
xtn->ev.buf[count].events = events;
|
||
|
count++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
n = count;
|
||
|
HCL_ASSERT (hcl, n > 0);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (n <= -1)
|
||
|
{
|
||
|
hcl_seterrwithsyserr (hcl, 0, errno);
|
||
|
HCL_DEBUG2 (hcl, "Warning: multiplexer wait failure - %d, %s\n", errno, hcl_geterrmsg(hcl));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
xtn->ev.len = n;
|
||
|
}
|
||
|
|
||
|
/* the muxwcb must be valid all the time in a non-threaded mode */
|
||
|
HCL_ASSERT (hcl, muxwcb != HCL_NULL);
|
||
|
|
||
|
while (n > 0)
|
||
|
{
|
||
|
int revents;
|
||
|
hcl_ooi_t mask;
|
||
|
|
||
|
--n;
|
||
|
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
revents = xtn->ev.buf[n].revents;
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
if (xtn->ev.buf[n].filter == EVFILT_READ) mask = HCL_SEMAPHORE_IO_MASK_INPUT;
|
||
|
else if (xtn->ev.buf[n].filter == EVFILT_WRITE) mask = HCL_SEMAPHORE_IO_MASK_OUTPUT;
|
||
|
else mask = 0;
|
||
|
goto call_muxwcb_kqueue;
|
||
|
#elif defined(USE_EPOLL)
|
||
|
revents = xtn->ev.buf[n].events;
|
||
|
#elif defined(USE_POLL)
|
||
|
revents = xtn->ev.buf[n].revents;
|
||
|
#elif defined(USE_SELECT)
|
||
|
revents = xtn->ev.buf[n].events;
|
||
|
#else
|
||
|
revents = 0; /* TODO: fake. unsupported but to compile on such an unsupported system.*/
|
||
|
#endif
|
||
|
|
||
|
mask = 0;
|
||
|
if (revents & XPOLLIN) mask |= HCL_SEMAPHORE_IO_MASK_INPUT;
|
||
|
if (revents & XPOLLOUT) mask |= HCL_SEMAPHORE_IO_MASK_OUTPUT;
|
||
|
if (revents & XPOLLERR) mask |= HCL_SEMAPHORE_IO_MASK_ERROR;
|
||
|
if (revents & XPOLLHUP) mask |= HCL_SEMAPHORE_IO_MASK_HANGUP;
|
||
|
|
||
|
#if defined(USE_DEVPOLL)
|
||
|
muxwcb (hcl, xtn->ev.buf[n].fd, mask);
|
||
|
#elif defined(USE_KQUEUE)
|
||
|
call_muxwcb_kqueue:
|
||
|
muxwcb (hcl, xtn->ev.buf[n].ident, mask);
|
||
|
#elif defined(USE_EPOLL)
|
||
|
muxwcb (hcl, xtn->ev.buf[n].data.fd, mask);
|
||
|
#elif defined(USE_POLL)
|
||
|
muxwcb (hcl, xtn->ev.buf[n].fd, mask);
|
||
|
#elif defined(USE_SELECT)
|
||
|
muxwcb (hcl, xtn->ev.buf[n].fd, mask);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
xtn->ev.len = 0;
|
||
|
#endif /* USE_THREAD */
|
||
|
}
|
||
|
|
||
|
/* -----------------------------------------------------------------
|
||
|
* SLEEPING
|
||
|
* ----------------------------------------------------------------- */
|
||
|
|
||
|
#if defined(__DOS__)
|
||
|
# if defined(_INTELC32_)
|
||
|
void _halt_cpu (void);
|
||
|
# elif defined(__WATCOMC__)
|
||
|
void _halt_cpu (void);
|
||
|
# pragma aux _halt_cpu = "hlt"
|
||
|
# endif
|
||
|
#endif
|
||
|
|
||
|
static int vm_sleep (hcl_t* hcl, const hcl_ntime_t* dur)
|
||
|
{
|
||
|
#if defined(_WIN32)
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
if (xtn->waitable_timer)
|
||
|
{
|
||
|
LARGE_INTEGER li;
|
||
|
li.QuadPart = -(HCL_SECNSEC_TO_NSEC(dur->sec, dur->nsec) / 100); /* in 100 nanoseconds */
|
||
|
if(SetWaitableTimer(xtn->waitable_timer, &li, 0, HCL_NULL, HCL_NULL, FALSE) == FALSE) goto normal_sleep;
|
||
|
WaitForSingleObject(xtn->waitable_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(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
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* -----------------------------------------------------------------
|
||
|
* SHARED LIBRARY HANDLING
|
||
|
* ----------------------------------------------------------------- */
|
||
|
|
||
|
#if defined(USE_LTDL)
|
||
|
# define sys_dl_error() lt_dlerror()
|
||
|
# define sys_dl_open(x) lt_dlopen(x)
|
||
|
# define sys_dl_openext(x) lt_dlopenext(x)
|
||
|
# define sys_dl_close(x) lt_dlclose(x)
|
||
|
# define sys_dl_getsym(x,n) lt_dlsym(x,n)
|
||
|
|
||
|
#elif defined(USE_DLFCN)
|
||
|
# define sys_dl_error() dlerror()
|
||
|
# define sys_dl_open(x) dlopen(x,RTLD_NOW)
|
||
|
# define sys_dl_openext(x) dlopen(x,RTLD_NOW)
|
||
|
# define sys_dl_close(x) dlclose(x)
|
||
|
# define sys_dl_getsym(x,n) dlsym(x,n)
|
||
|
|
||
|
#elif defined(USE_WIN_DLL)
|
||
|
# define sys_dl_error() win_dlerror()
|
||
|
# define sys_dl_open(x) LoadLibraryExA(x, HCL_NULL, 0)
|
||
|
# define sys_dl_openext(x) LoadLibraryExA(x, HCL_NULL, 0)
|
||
|
# define sys_dl_close(x) FreeLibrary(x)
|
||
|
# define sys_dl_getsym(x,n) GetProcAddress(x,n)
|
||
|
|
||
|
#elif defined(USE_MACH_O_DYLD)
|
||
|
# define sys_dl_error() mach_dlerror()
|
||
|
# define sys_dl_open(x) mach_dlopen(x)
|
||
|
# define sys_dl_openext(x) mach_dlopen(x)
|
||
|
# define sys_dl_close(x) mach_dlclose(x)
|
||
|
# define sys_dl_getsym(x,n) mach_dlsym(x,n)
|
||
|
#endif
|
||
|
|
||
|
#if defined(USE_WIN_DLL)
|
||
|
|
||
|
static const char* win_dlerror (void)
|
||
|
{
|
||
|
/* TODO: handle wchar_t, hcl_ooch_t etc? */
|
||
|
static char buf[256];
|
||
|
DWORD rc;
|
||
|
|
||
|
rc = FormatMessageA (
|
||
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||
|
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||
|
buf, HCL_COUNTOF(buf), HCL_NULL
|
||
|
);
|
||
|
while (rc > 0 && buf[rc - 1] == '\r' || buf[rc - 1] == '\n')
|
||
|
{
|
||
|
buf[--rc] = '\0';
|
||
|
}
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
#elif defined(USE_MACH_O_DYLD)
|
||
|
static const char* mach_dlerror_str = "";
|
||
|
|
||
|
static void* mach_dlopen (const char* path)
|
||
|
{
|
||
|
NSObjectFileImage image;
|
||
|
NSObjectFileImageReturnCode rc;
|
||
|
void* handle;
|
||
|
|
||
|
mach_dlerror_str = "";
|
||
|
if ((rc = NSCreateObjectFileImageFromFile(path, &image)) != NSObjectFileImageSuccess)
|
||
|
{
|
||
|
switch (rc)
|
||
|
{
|
||
|
case NSObjectFileImageFailure:
|
||
|
case NSObjectFileImageFormat:
|
||
|
mach_dlerror_str = "unable to crate object file image";
|
||
|
break;
|
||
|
|
||
|
case NSObjectFileImageInappropriateFile:
|
||
|
mach_dlerror_str = "inappropriate file";
|
||
|
break;
|
||
|
|
||
|
case NSObjectFileImageArch:
|
||
|
mach_dlerror_str = "incompatible architecture";
|
||
|
break;
|
||
|
|
||
|
case NSObjectFileImageAccess:
|
||
|
mach_dlerror_str = "inaccessible file";
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
mach_dlerror_str = "unknown error";
|
||
|
break;
|
||
|
}
|
||
|
return HCL_NULL;
|
||
|
}
|
||
|
handle = (void*)NSLinkModule(image, path, NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_RETURN_ON_ERROR);
|
||
|
NSDestroyObjectFileImage (image);
|
||
|
return handle;
|
||
|
}
|
||
|
|
||
|
static HCL_INLINE void mach_dlclose (void* handle)
|
||
|
{
|
||
|
mach_dlerror_str = "";
|
||
|
NSUnLinkModule (handle, NSUNLINKMODULE_OPTION_NONE);
|
||
|
}
|
||
|
|
||
|
static HCL_INLINE void* mach_dlsym (void* handle, const char* name)
|
||
|
{
|
||
|
mach_dlerror_str = "";
|
||
|
return (void*)NSAddressOfSymbol(NSLookupSymbolInModule(handle, name));
|
||
|
}
|
||
|
|
||
|
static const char* mach_dlerror (void)
|
||
|
{
|
||
|
int err_no;
|
||
|
const char* err_file;
|
||
|
NSLinkEditErrors err;
|
||
|
|
||
|
if (mach_dlerror_str[0] == '\0')
|
||
|
NSLinkEditError (&err, &err_no, &err_file, &mach_dlerror_str);
|
||
|
|
||
|
return mach_dlerror_str;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static void dl_startup (hcl_t* hcl)
|
||
|
{
|
||
|
#if defined(USE_LTDL)
|
||
|
lt_dlinit ();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void dl_cleanup (hcl_t* hcl)
|
||
|
{
|
||
|
#if defined(USE_LTDL)
|
||
|
lt_dlexit ();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void* dl_open (hcl_t* hcl, const hcl_ooch_t* name, int flags)
|
||
|
{
|
||
|
#if defined(USE_LTDL) || defined(USE_DLFCN) || defined(USE_MACH_O_DYLD)
|
||
|
hcl_bch_t stabuf[128], * bufptr;
|
||
|
hcl_oow_t ucslen, bcslen, bufcapa;
|
||
|
void* handle;
|
||
|
|
||
|
#if defined(HCL_OOCH_IS_UCH)
|
||
|
if (hcl_convootobcstr(hcl, name, &ucslen, HCL_NULL, &bufcapa) <= -1) return HCL_NULL;
|
||
|
/* +1 for terminating null. but it's not needed because HCL_COUNTOF(HCL_DEFAULT_PFMODPREFIX)
|
||
|
* and HCL_COUNTOF(HCL_DEFAULT_PFMODPOSTIFX) include the terminating nulls. Never mind about
|
||
|
* the extra 2 characters. */
|
||
|
#else
|
||
|
bufcapa = hcl_count_bcstr(name);
|
||
|
#endif
|
||
|
bufcapa += HCL_COUNTOF(HCL_DEFAULT_PFMODDIR) + HCL_COUNTOF(HCL_DEFAULT_PFMODPREFIX) + HCL_COUNTOF(HCL_DEFAULT_PFMODPOSTFIX) + 1;
|
||
|
|
||
|
if (bufcapa <= HCL_COUNTOF(stabuf)) bufptr = stabuf;
|
||
|
else
|
||
|
{
|
||
|
bufptr = (hcl_bch_t*)hcl_allocmem(hcl, bufcapa * HCL_SIZEOF(*bufptr));
|
||
|
if (!bufptr) return HCL_NULL;
|
||
|
}
|
||
|
|
||
|
if (flags & HCL_VMPRIM_DLOPEN_PFMOD)
|
||
|
{
|
||
|
hcl_oow_t len, i, xlen, dlen;
|
||
|
|
||
|
/* opening a primitive function module - mostly libhcl-xxxx.
|
||
|
* if PFMODPREFIX is absolute, never use PFMODDIR */
|
||
|
dlen = HCL_IS_PATH_ABSOLUTE(HCL_DEFAULT_PFMODPREFIX)?
|
||
|
0: hcl_copy_bcstr(bufptr, bufcapa, HCL_DEFAULT_PFMODDIR);
|
||
|
len = hcl_copy_bcstr(bufptr, bufcapa, HCL_DEFAULT_PFMODPREFIX);
|
||
|
len += dlen;
|
||
|
|
||
|
bcslen = bufcapa - len;
|
||
|
#if defined(HCL_OOCH_IS_UCH)
|
||
|
hcl_convootobcstr(hcl, name, &ucslen, &bufptr[len], &bcslen);
|
||
|
#else
|
||
|
bcslen = hcl_copy_bcstr(&bufptr[len], bcslen, name);
|
||
|
#endif
|
||
|
|
||
|
/* length including the directory, the prefix and the name. but excluding the postfix */
|
||
|
xlen = len + bcslen;
|
||
|
|
||
|
for (i = len; i < xlen; i++)
|
||
|
{
|
||
|
/* convert a period(.) to a dash(-) */
|
||
|
if (bufptr[i] == '.') bufptr[i] = '-';
|
||
|
}
|
||
|
|
||
|
retry:
|
||
|
hcl_copy_bcstr (&bufptr[xlen], bufcapa - xlen, HCL_DEFAULT_PFMODPOSTFIX);
|
||
|
|
||
|
/* both prefix and postfix attached. for instance, libhcl-xxx */
|
||
|
handle = sys_dl_openext(bufptr);
|
||
|
if (!handle)
|
||
|
{
|
||
|
HCL_DEBUG3 (hcl, "Unable to open(ext) PFMOD %hs[%js] - %hs\n", &bufptr[dlen], name, sys_dl_error());
|
||
|
|
||
|
if (dlen > 0)
|
||
|
{
|
||
|
handle = sys_dl_openext(&bufptr[0]);
|
||
|
if (handle) goto pfmod_open_ok;
|
||
|
HCL_DEBUG3 (hcl, "Unable to open(ext) PFMOD %hs[%js] - %hs\n", &bufptr[0], name, sys_dl_error());
|
||
|
}
|
||
|
|
||
|
/* try without prefix and postfix */
|
||
|
bufptr[xlen] = '\0';
|
||
|
handle = sys_dl_openext(&bufptr[len]);
|
||
|
if (!handle)
|
||
|
{
|
||
|
hcl_bch_t* dash;
|
||
|
const hcl_bch_t* dl_errstr;
|
||
|
dl_errstr = sys_dl_error();
|
||
|
HCL_DEBUG3 (hcl, "Unable to open(ext) PFMOD %hs[%js] - %hs\n", &bufptr[len], name, dl_errstr);
|
||
|
hcl_seterrbfmt (hcl, HCL_ESYSERR, "unable to open(ext) PFMOD %js - %hs", name, dl_errstr);
|
||
|
|
||
|
dash = hcl_rfind_bchar(bufptr, hcl_count_bcstr(bufptr), '-');
|
||
|
if (dash)
|
||
|
{
|
||
|
/* remove a segment at the back.
|
||
|
* [NOTE] a dash contained in the original name before
|
||
|
* period-to-dash transformation may cause extraneous/wrong
|
||
|
* loading reattempts. */
|
||
|
xlen = dash - bufptr;
|
||
|
goto retry;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
HCL_DEBUG3 (hcl, "Opened(ext) PFMOD %hs[%js] handle %p\n", &bufptr[len], name, handle);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pfmod_open_ok:
|
||
|
HCL_DEBUG3 (hcl, "Opened(ext) PFMOD %hs[%js] handle %p\n", &bufptr[dlen], name, handle);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* opening a raw shared object without a prefix and/or a postfix */
|
||
|
#if defined(HCL_OOCH_IS_UCH)
|
||
|
bcslen = bufcapa;
|
||
|
hcl_convootobcstr(hcl, name, &ucslen, bufptr, &bcslen);
|
||
|
#else
|
||
|
bcslen = hcl_copy_bcstr(bufptr, bufcapa, name);
|
||
|
#endif
|
||
|
|
||
|
if (hcl_find_bchar(bufptr, bcslen, '.'))
|
||
|
{
|
||
|
handle = sys_dl_open(bufptr);
|
||
|
if (!handle)
|
||
|
{
|
||
|
const hcl_bch_t* dl_errstr;
|
||
|
dl_errstr = sys_dl_error();
|
||
|
HCL_DEBUG2 (hcl, "Unable to open DL %hs - %hs\n", bufptr, dl_errstr);
|
||
|
hcl_seterrbfmt (hcl, HCL_ESYSERR, "unable to open DL %js - %hs", name, dl_errstr);
|
||
|
}
|
||
|
else HCL_DEBUG2 (hcl, "Opened DL %hs handle %p\n", bufptr, handle);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
handle = sys_dl_openext(bufptr);
|
||
|
if (!handle)
|
||
|
{
|
||
|
const hcl_bch_t* dl_errstr;
|
||
|
dl_errstr = sys_dl_error();
|
||
|
HCL_DEBUG2 (hcl, "Unable to open(ext) DL %hs - %s\n", bufptr, dl_errstr);
|
||
|
hcl_seterrbfmt (hcl, HCL_ESYSERR, "unable to open(ext) DL %js - %hs", name, dl_errstr);
|
||
|
}
|
||
|
else HCL_DEBUG2 (hcl, "Opened(ext) DL %hs handle %p\n", bufptr, handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bufptr != stabuf) hcl_freemem (hcl, bufptr);
|
||
|
return handle;
|
||
|
|
||
|
#else
|
||
|
|
||
|
/* TODO: support various platforms */
|
||
|
/* TODO: implemenent this */
|
||
|
HCL_DEBUG1 (hcl, "Dynamic loading not implemented - cannot open %js\n", name);
|
||
|
hcl_seterrbfmt (hcl, HCL_ENOIMPL, "dynamic loading not implemented - cannot open %js", name);
|
||
|
return HCL_NULL;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void dl_close (hcl_t* hcl, void* handle)
|
||
|
{
|
||
|
#if defined(USE_LTDL) || defined(USE_DLFCN) || defined(USE_MACH_O_DYLD)
|
||
|
HCL_DEBUG1 (hcl, "Closed DL handle %p\n", handle);
|
||
|
sys_dl_close (handle);
|
||
|
|
||
|
#else
|
||
|
/* TODO: implemenent this */
|
||
|
HCL_DEBUG1 (hcl, "Dynamic loading not implemented - cannot close handle %p\n", handle);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void* dl_getsym (hcl_t* hcl, void* handle, const hcl_ooch_t* name)
|
||
|
{
|
||
|
#if defined(USE_LTDL) || defined(USE_DLFCN) || defined(USE_MACH_O_DYLD)
|
||
|
hcl_bch_t stabuf[64], * bufptr;
|
||
|
hcl_oow_t bufcapa, ucslen, bcslen, i;
|
||
|
const hcl_bch_t* symname;
|
||
|
void* sym;
|
||
|
|
||
|
#if defined(HCL_OOCH_IS_UCH)
|
||
|
if (hcl_convootobcstr(hcl, name, &ucslen, HCL_NULL, &bcslen) <= -1) return HCL_NULL;
|
||
|
#else
|
||
|
bcslen = hcl_count_bcstr (name);
|
||
|
#endif
|
||
|
|
||
|
if (bcslen >= HCL_COUNTOF(stabuf) - 2)
|
||
|
{
|
||
|
bufcapa = bcslen + 3;
|
||
|
bufptr = (hcl_bch_t*)hcl_allocmem(hcl, bufcapa * HCL_SIZEOF(*bufptr));
|
||
|
if (!bufptr) return HCL_NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bufcapa = HCL_COUNTOF(stabuf);
|
||
|
bufptr = stabuf;
|
||
|
}
|
||
|
|
||
|
bcslen = bufcapa - 1;
|
||
|
#if defined(HCL_OOCH_IS_UCH)
|
||
|
hcl_convootobcstr (hcl, name, &ucslen, &bufptr[1], &bcslen);
|
||
|
#else
|
||
|
bcslen = hcl_copy_bcstr(&bufptr[1], bcslen, name);
|
||
|
#endif
|
||
|
|
||
|
/* convert a period(.) to an underscore(_) */
|
||
|
for (i = 1; i <= bcslen; i++) if (bufptr[i] == '.') bufptr[i] = '_';
|
||
|
|
||
|
symname = &bufptr[1]; /* try the name as it is */
|
||
|
|
||
|
sym = sys_dl_getsym(handle, symname);
|
||
|
if (!sym)
|
||
|
{
|
||
|
bufptr[0] = '_';
|
||
|
symname = &bufptr[0]; /* try _name */
|
||
|
sym = sys_dl_getsym(handle, symname);
|
||
|
if (!sym)
|
||
|
{
|
||
|
bufptr[bcslen + 1] = '_';
|
||
|
bufptr[bcslen + 2] = '\0';
|
||
|
|
||
|
symname = &bufptr[1]; /* try name_ */
|
||
|
sym = sys_dl_getsym(handle, symname);
|
||
|
|
||
|
if (!sym)
|
||
|
{
|
||
|
symname = &bufptr[0]; /* try _name_ */
|
||
|
sym = sys_dl_getsym(handle, symname);
|
||
|
if (!sym)
|
||
|
{
|
||
|
const hcl_bch_t* dl_errstr;
|
||
|
dl_errstr = sys_dl_error();
|
||
|
HCL_DEBUG3 (hcl, "Failed to get module symbol %js from handle %p - %hs\n", name, handle, dl_errstr);
|
||
|
hcl_seterrbfmt (hcl, HCL_ENOENT, "unable to get module symbol %hs - %hs", symname, dl_errstr);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sym) HCL_DEBUG3 (hcl, "Loaded module symbol %js from handle %p - %hs\n", name, handle, symname);
|
||
|
if (bufptr != stabuf) hcl_freemem (hcl, bufptr);
|
||
|
return sym;
|
||
|
|
||
|
#else
|
||
|
/* TODO: IMPLEMENT THIS */
|
||
|
HCL_DEBUG2 (hcl, "Dynamic loading not implemented - Cannot load module symbol %js from handle %p\n", name, handle);
|
||
|
hcl_seterrbfmt (hcl, HCL_ENOIMPL, "dynamic loading not implemented - Cannot load module symbol %js from handle %p", name, handle);
|
||
|
return HCL_NULL;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* -----------------------------------------------------------------
|
||
|
* EVENT CALLBACKS
|
||
|
* ----------------------------------------------------------------- */
|
||
|
|
||
|
#define ENABLE_LOG_INITIALLY
|
||
|
|
||
|
static HCL_INLINE void reset_log_to_default (xtn_t* xtn)
|
||
|
{
|
||
|
#if defined(ENABLE_LOG_INITIALLY)
|
||
|
xtn->log.fd = 2;
|
||
|
xtn->log.fd_flag = 0;
|
||
|
#if defined(HAVE_ISATTY)
|
||
|
if (isatty(xtn->log.fd)) xtn->log.fd_flag |= LOGFD_TTY;
|
||
|
#endif
|
||
|
#else
|
||
|
xtn->log.fd = -1;
|
||
|
xtn->log.fd_flag = 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static void cb_fini (hcl_t* hcl)
|
||
|
{
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
if ((xtn->log.fd_flag & LOGFD_OPENED_HERE) && xtn->log.fd >= 0) close (xtn->log.fd);
|
||
|
reset_log_to_default (xtn);
|
||
|
}
|
||
|
|
||
|
static void cb_opt_set (hcl_t* hcl, hcl_option_t id, const void* value)
|
||
|
{
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
int fd;
|
||
|
|
||
|
if (id != HCL_LOG_TARGET) return; /* return success. not interested */
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
fd = _open(hcl->option.log_target, _O_CREAT | _O_WRONLY | _O_APPEND | _O_BINARY , 0644);
|
||
|
#else
|
||
|
#if defined(HCL_OOCH_IS_UCH)
|
||
|
fd = open(hcl->option.log_targetx, O_CREAT | O_WRONLY | O_APPEND , 0644);
|
||
|
#else
|
||
|
fd = open(hcl->option.log_target, O_CREAT | O_WRONLY | O_APPEND , 0644);
|
||
|
#endif
|
||
|
#endif
|
||
|
if (fd == -1)
|
||
|
{
|
||
|
/* TODO: any warning that log file not changed? */
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((xtn->log.fd_flag & LOGFD_OPENED_HERE) && xtn->log.fd >= 0) close (xtn->log.fd);
|
||
|
|
||
|
xtn->log.fd = fd;
|
||
|
xtn->log.fd_flag = LOGFD_OPENED_HERE;
|
||
|
#if defined(HAVE_ISATTY)
|
||
|
if (isatty(xtn->log.fd)) xtn->log.fd_flag |= LOGFD_TTY;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int cb_vm_startup (hcl_t* hcl)
|
||
|
{
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
xtn->waitable_timer = CreateWaitableTimer(HCL_NULL, TRUE, HCL_NULL);
|
||
|
#endif
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void cb_vm_cleanup (hcl_t* hcl)
|
||
|
{
|
||
|
xtn_t* xtn = GET_XTN(hcl);
|
||
|
|
||
|
xtn->vm_running = 0;
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
if (xtn->waitable_timer)
|
||
|
{
|
||
|
CloseHandle (xtn->waitable_timer);
|
||
|
xtn->waitable_timer = HCL_NULL;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* -----------------------------------------------------------------
|
||
|
* STANDARD HCL
|
||
|
* ----------------------------------------------------------------- */
|
||
|
|
||
|
hcl_t* hcl_openstd (hcl_oow_t xtnsize, hcl_oow_t heapsize, hcl_errnum_t* errnum)
|
||
|
{
|
||
|
hcl_t* hcl;
|
||
|
hcl_vmprim_t vmprim;
|
||
|
hcl_cb_t cb;
|
||
|
|
||
|
HCL_MEMSET (&vmprim, 0, HCL_SIZEOF(vmprim));
|
||
|
//if (large_pages)
|
||
|
//{
|
||
|
vmprim.alloc_heap = alloc_heap;
|
||
|
vmprim.free_heap = free_heap;
|
||
|
//}
|
||
|
vmprim.log_write = log_write;
|
||
|
vmprim.syserrstrb = _syserrstrb;
|
||
|
vmprim.assertfail = _assertfail;
|
||
|
vmprim.dl_startup = dl_startup;
|
||
|
vmprim.dl_cleanup = dl_cleanup;
|
||
|
vmprim.dl_open = dl_open;
|
||
|
vmprim.dl_close = dl_close;
|
||
|
vmprim.dl_getsym = dl_getsym;
|
||
|
vmprim.vm_gettime = vm_gettime;
|
||
|
vmprim.vm_muxadd = vm_muxadd;
|
||
|
vmprim.vm_muxdel = vm_muxdel;
|
||
|
vmprim.vm_muxmod = vm_muxmod;
|
||
|
vmprim.vm_muxwait = vm_muxwait;
|
||
|
vmprim.vm_sleep = vm_sleep;
|
||
|
|
||
|
hcl = hcl_open(&sys_mmgr, HCL_SIZEOF(xtn_t) + xtnsize, heapsize, &vmprim, errnum);
|
||
|
if (HCL_UNLIKELY(!hcl)) return HCL_NULL;
|
||
|
|
||
|
/* adjust the object size by the sizeof xtn_t so that moo_getxtn() returns the right pointer. */
|
||
|
hcl->_instsize += HCL_SIZEOF(xtn_t);
|
||
|
|
||
|
reset_log_to_default (GET_XTN(hcl));
|
||
|
|
||
|
HCL_MEMSET (&cb, 0, HCL_SIZEOF(cb));
|
||
|
cb.fini = cb_fini;
|
||
|
cb.opt_set = cb_opt_set;
|
||
|
cb.vm_startup = cb_vm_startup;
|
||
|
cb.vm_cleanup = cb_vm_cleanup;
|
||
|
if (hcl_regcb(hcl, &cb) == HCL_NULL)
|
||
|
{
|
||
|
if (errnum) *errnum = hcl_geterrnum(hcl);
|
||
|
hcl_close (hcl);
|
||
|
return HCL_NULL;
|
||
|
}
|
||
|
|
||
|
return hcl;
|
||
|
}
|