Compare commits

...
2 Commits
Author SHA1 Message Date
hyung-hwan 95246943e6 optimized event handling to minimize spurious events by treating io based signal more specially 2026-09-01 12:27:03 +09:00
hyung-hwan d5b4a1290f fixed a segfault for wrong finialization order in hak_fini
added HAK_PFBASE_CONST_SMOOI
defined SIGXXX in the sys module using HAK_PFBASE_CONST_SMOOI
added sys/event.h to configure.ac for check to fix wrong probe
updated the test files to use sys.SIGXXX instead of hardcoded linux-based signal number
2026-08-31 13:21:31 +09:00
21 changed files with 520 additions and 95 deletions
+2 -1
View File
@@ -75,7 +75,8 @@ hak_DEPENDENCIES = ../lib/libhak.la
if ENABLE_ISOCLINE if ENABLE_ISOCLINE
hak_CPPFLAGS += -I$(srcdir)/isocline/include -DHAVE_ISOCLINE_H -DHAVE_ISOCLINE_LIB hak_CPPFLAGS += -I$(srcdir)/isocline/include -DHAVE_ISOCLINE_H -DHAVE_ISOCLINE_LIB
hak_LDADD += ./libisocline.a hak_LDADD += ./libisocline.a
hak_DEPENDENCIES += ./libisocline.a ##hak_DEPENDENCIES += ./libisocline.a
hak_DEPENDENCIES += libisocline.a
endif endif
if ENABLE_HAKX if ENABLE_HAKX
+1 -1
View File
@@ -101,7 +101,7 @@ host_triplet = @host@
bin_PROGRAMS = hak$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2) bin_PROGRAMS = hak$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2)
@ENABLE_ISOCLINE_TRUE@am__append_3 = -I$(srcdir)/isocline/include -DHAVE_ISOCLINE_H -DHAVE_ISOCLINE_LIB @ENABLE_ISOCLINE_TRUE@am__append_3 = -I$(srcdir)/isocline/include -DHAVE_ISOCLINE_H -DHAVE_ISOCLINE_LIB
@ENABLE_ISOCLINE_TRUE@am__append_4 = ./libisocline.a @ENABLE_ISOCLINE_TRUE@am__append_4 = ./libisocline.a
@ENABLE_ISOCLINE_TRUE@am__append_5 = ./libisocline.a @ENABLE_ISOCLINE_TRUE@am__append_5 = libisocline.a
@ENABLE_HAKX_TRUE@am__append_6 = hakx @ENABLE_HAKX_TRUE@am__append_6 = hakx
@ENABLE_HAKGO_TRUE@am__append_7 = hakgo @ENABLE_HAKGO_TRUE@am__append_7 = hakgo
subdir = bin subdir = bin
Vendored
+6
View File
@@ -16895,6 +16895,12 @@ if test "x$ac_cv_header_sys_devpoll_h" = xyes
then : then :
printf "%s\n" "#define HAVE_SYS_DEVPOLL_H 1" >>confdefs.h printf "%s\n" "#define HAVE_SYS_DEVPOLL_H 1" >>confdefs.h
fi
ac_fn_c_check_header_compile "$LINENO" "sys/event.h" "ac_cv_header_sys_event_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_event_h" = xyes
then :
printf "%s\n" "#define HAVE_SYS_EVENT_H 1" >>confdefs.h
fi fi
ac_fn_c_check_header_compile "$LINENO" "sys/epoll.h" "ac_cv_header_sys_epoll_h" "$ac_includes_default" ac_fn_c_check_header_compile "$LINENO" "sys/epoll.h" "ac_cv_header_sys_epoll_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_epoll_h" = xyes if test "x$ac_cv_header_sys_epoll_h" = xyes
+1 -1
View File
@@ -110,7 +110,7 @@ AC_PROG_EGREP
AC_CHECK_HEADERS([stddef.h wchar.h wctype.h errno.h signal.h fcntl.h dirent.h]) AC_CHECK_HEADERS([stddef.h wchar.h wctype.h errno.h signal.h fcntl.h dirent.h])
AC_CHECK_HEADERS([time.h sys/time.h utime.h spawn.h execinfo.h ucontext.h]) AC_CHECK_HEADERS([time.h sys/time.h utime.h spawn.h execinfo.h ucontext.h])
AC_CHECK_HEADERS([dlfcn.h ltdl.h sys/mman.h sys/uio.h]) AC_CHECK_HEADERS([dlfcn.h ltdl.h sys/mman.h sys/uio.h])
AC_CHECK_HEADERS([sys/devpoll.h sys/epoll.h poll.h]) AC_CHECK_HEADERS([sys/devpoll.h sys/event.h sys/epoll.h poll.h])
AC_CHECK_HEADERS([libunwind.h quadmath.h]) AC_CHECK_HEADERS([libunwind.h quadmath.h])
AC_CHECK_HEADERS([sys/ioctl.h net/if.h]) AC_CHECK_HEADERS([sys/ioctl.h net/if.h])
AC_CHECK_HEADERS([sys/wait.h sys/resource.h]) AC_CHECK_HEADERS([sys/wait.h sys/resource.h])
+7 -2
View File
@@ -5450,12 +5450,17 @@ static HAK_INLINE int compile_dsymbol (hak_t* hak, hak_cnode_t* obj)
val = hak->_nil; val = hak->_nil;
break; break;
case HAK_PFBASE_CONST: case HAK_PFBASE_CONST_SMOOI:
{
/* TODO: create a value from the pfbase information. it needs to get extended first /* TODO: create a value from the pfbase information. it needs to get extended first
* can i make use of pfbase->handler type-cast to a differnt type? */ * can i make use of pfbase->handler type-cast to a differnt type? */
/* maxarg -> actual value cast to (hak_oow_t)(hak_ooi_t) */
hak_ooi_t v = (hak_ooi_t)pfbase->maxargs;
HAK_ASSERT(hak, HAK_IN_SMOOI_RANGE(v));
kernel_bits = 2; kernel_bits = 2;
val = hak->_nil; val = HAK_SMOOI_TO_OOP(v);
break; break;
}
default: default:
hak_popvolat(hak); hak_popvolat(hak);
+75 -5
View File
@@ -1470,6 +1470,29 @@ static HAK_INLINE int can_await_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
return (hak_oop_t)sem->group == hak->_nil; return (hak_oop_t)sem->group == hak->_nil;
} }
static HAK_INLINE void drop_unawaited_io_signal_count_in_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
{
hak_ooi_t index;
hak_ooi_t io_type;
if (sem->subtype != HAK_SMOOI_TO_OOP(HAK_SEMAPHORE_SUBTYPE_IO)) return;
/* io.index must be nil or smooi */
/*if ((hak_oop_t)sem->u.io.index == hak->_nil) return;*/
if (!HAK_OOP_IS_SMOOI(sem->u.io.index)) return;
/* u.io.index is nil once the semaphore has been unbound, so a stale IO
* subtype cannot lead us into the tuple array with a dead index. */
index = HAK_OOP_TO_SMOOI(sem->u.io.index);
io_type = HAK_OOP_TO_SMOOI(sem->u.io.type);
HAK_ASSERT(hak, index >= 0 && index < (hak_ooi_t)hak->sem_io_tuple_count);
HAK_ASSERT(hak, io_type == HAK_SEMAPHORE_IO_TYPE_INPUT || io_type == HAK_SEMAPHORE_IO_TYPE_OUTPUT);
if (hak->sem_io_tuple[index].unawaited[io_type] > 0)
hak->sem_io_tuple[index].unawaited[io_type]--;
}
static HAK_INLINE void await_semaphore (hak_t* hak, hak_oop_semaphore_t sem) static HAK_INLINE void await_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
{ {
hak_oop_process_t proc; hak_oop_process_t proc;
@@ -1487,10 +1510,10 @@ static HAK_INLINE void await_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
/* it's already signaled */ /* it's already signaled */
count--; count--;
sem->count = HAK_SMOOI_TO_OOP(count); sem->count = HAK_SMOOI_TO_OOP(count);
drop_unawaited_io_signal_count_in_semaphore(hak, sem);
if ((hak_oop_t)semgrp != hak->_nil && count == 0) if ((hak_oop_t)semgrp != hak->_nil && count == 0)
{ {
int sems_idx; int sems_idx;
/* TODO: if i disallow individual wait on a semaphore in a group, /* TODO: if i disallow individual wait on a semaphore in a group,
* this membership manipulation is redundant */ * this membership manipulation is redundant */
@@ -1552,6 +1575,7 @@ static HAK_INLINE hak_oop_t await_semaphore_group (hak_t* hak, hak_oop_semaphore
HAK_ASSERT(hak, count > 0); HAK_ASSERT(hak, count > 0);
count--; count--;
sem->count = HAK_SMOOI_TO_OOP(count); sem->count = HAK_SMOOI_TO_OOP(count);
drop_unawaited_io_signal_count_in_semaphore(hak, sem);
HAK_DELETE_FROM_OOP_LIST(hak, &semgrp->sems[HAK_SEMAPHORE_GROUP_SEMS_SIG], sem, grm); HAK_DELETE_FROM_OOP_LIST(hak, &semgrp->sems[HAK_SEMAPHORE_GROUP_SEMS_SIG], sem, grm);
sems_idx = count > 0? HAK_SEMAPHORE_GROUP_SEMS_SIG: HAK_SEMAPHORE_GROUP_SEMS_UNSIG; sems_idx = count > 0? HAK_SEMAPHORE_GROUP_SEMS_SIG: HAK_SEMAPHORE_GROUP_SEMS_UNSIG;
@@ -1778,6 +1802,7 @@ int hak_add_sem_to_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, hak_ooi_t
return -1; return -1;
} }
/* initialize it to -1 to indicate no binding yet */
for (i = hak->sem_io_map_capa; i < new_capa; i++) tmp[i] = -1; for (i = hak->sem_io_map_capa; i < new_capa; i++) tmp[i] = -1;
hak->sem_io_map = tmp; hak->sem_io_map = tmp;
@@ -1805,6 +1830,9 @@ int hak_add_sem_to_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, hak_ooi_t
tmp = (hak_sem_tuple_t*)hak_reallocmem(hak, hak->sem_io_tuple, HAK_SIZEOF(hak_sem_tuple_t) * new_capa); tmp = (hak_sem_tuple_t*)hak_reallocmem(hak, hak->sem_io_tuple, HAK_SIZEOF(hak_sem_tuple_t) * new_capa);
if (HAK_UNLIKELY(!tmp)) return -1; if (HAK_UNLIKELY(!tmp)) return -1;
/* initialize the newly added tuples with zeros */
HAK_MEMSET(&tmp[hak->sem_io_tuple_capa], 0, HAK_SIZEOF(hak_sem_tuple_t) * SEM_IO_TUPLE_INC);
hak->sem_io_tuple = tmp; hak->sem_io_tuple = tmp;
hak->sem_io_tuple_capa = new_capa; hak->sem_io_tuple_capa = new_capa;
} }
@@ -1822,6 +1850,8 @@ int hak_add_sem_to_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, hak_ooi_t
hak->sem_io_tuple[index].sem[HAK_SEMAPHORE_IO_TYPE_OUTPUT] = HAK_NULL; hak->sem_io_tuple[index].sem[HAK_SEMAPHORE_IO_TYPE_OUTPUT] = HAK_NULL;
hak->sem_io_tuple[index].handle = io_handle; hak->sem_io_tuple[index].handle = io_handle;
hak->sem_io_tuple[index].mask = 0; hak->sem_io_tuple[index].mask = 0;
hak->sem_io_tuple[index].unawaited[HAK_SEMAPHORE_IO_TYPE_INPUT] = 0;
hak->sem_io_tuple[index].unawaited[HAK_SEMAPHORE_IO_TYPE_OUTPUT] = 0;
new_mask = ((hak_ooi_t)1 << io_type); new_mask = ((hak_ooi_t)1 << io_type);
@@ -1861,6 +1891,7 @@ int hak_add_sem_to_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, hak_ooi_t
hak->sem_io_tuple[index].handle = io_handle; hak->sem_io_tuple[index].handle = io_handle;
hak->sem_io_tuple[index].mask = new_mask; hak->sem_io_tuple[index].mask = new_mask;
hak->sem_io_tuple[index].sem[io_type] = sem; hak->sem_io_tuple[index].sem[io_type] = sem;
hak->sem_io_tuple[index].unawaited[io_type] = 0; /* init for a new binding into the existing slot */
hak->sem_io_count++; hak->sem_io_count++;
if (tuple_added) if (tuple_added)
@@ -1883,7 +1914,7 @@ int hak_add_sem_to_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, hak_ooi_t
static int delete_sem_from_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, int force) static int delete_sem_from_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, int force)
{ {
hak_ooi_t index; hak_ooi_t index, unawaited;
hak_ooi_t new_mask, io_handle, io_type; hak_ooi_t new_mask, io_handle, io_type;
int x; int x;
@@ -1945,6 +1976,33 @@ static int delete_sem_from_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, in
sem->group->sem_io_count = HAK_SMOOI_TO_OOP(count); sem->group->sem_io_count = HAK_SMOOI_TO_OOP(count);
} }
/* ----------------------------------------------------------------------
* [OPTIMIZATION] it would work without this part and unawaited handling.
* ----------------------------------------------------------------------
* retract only what the multiplexer signaled. Those counts stand for an
* unconsumed edge on THIS descriptor; with the binding gone they refer to
* nothing, and a rebind re-arms the multiplexer anyway. Counts from a
* manual sem-signal are left alone: hak_pf_semaphore_signal() accepts an
* IO semaphore, so both sources share sem->count.
* ---------------------------------------------------------------------- */
unawaited = hak->sem_io_tuple[index].unawaited[io_type];
hak->sem_io_tuple[index].unawaited[io_type] = 0;
if (unawaited > 0)
{
hak_ooi_t count = HAK_OOP_TO_SMOOI(sem->count);
HAK_ASSERT(hak, unawaited <= count);
count -= unawaited;
sem->count = HAK_SMOOI_TO_OOP(count);
if (count == 0 && (hak_oop_t)sem->group != hak->_nil)
{
HAK_DELETE_FROM_OOP_LIST(hak, &sem->group->sems[HAK_SEMAPHORE_GROUP_SEMS_SIG], sem, grm);
HAK_APPEND_TO_OOP_LIST(hak, &sem->group->sems[HAK_SEMAPHORE_GROUP_SEMS_UNSIG], hak_oop_semaphore_t, sem, grm);
}
}
/* ---------------------------------------------------------------------- */
if (new_mask) if (new_mask)
{ {
hak->sem_io_tuple[index].mask = new_mask; hak->sem_io_tuple[index].mask = new_mask;
@@ -1975,7 +2033,7 @@ static int delete_sem_from_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, in
return 0; return 0;
} }
static void _signal_io_semaphore (hak_t* hak, hak_oop_semaphore_t sem) static hak_oop_process_t _signal_io_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
{ {
hak_oop_process_t proc; hak_oop_process_t proc;
@@ -1998,6 +2056,8 @@ static void _signal_io_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
switch_to_process_from_nil(hak, proc); switch_to_process_from_nil(hak, proc);
#endif #endif
} }
return proc;
} }
static void signal_io_semaphore (hak_t* hak, hak_ooi_t io_handle, hak_ooi_t mask) static void signal_io_semaphore (hak_t* hak, hak_ooi_t io_handle, hak_ooi_t mask)
@@ -2016,7 +2076,12 @@ static void signal_io_semaphore (hak_t* hak, hak_ooi_t io_handle, hak_ooi_t mask
if ((mask & (HAK_SEMAPHORE_IO_MASK_OUTPUT | HAK_SEMAPHORE_IO_MASK_ERROR)) || if ((mask & (HAK_SEMAPHORE_IO_MASK_OUTPUT | HAK_SEMAPHORE_IO_MASK_ERROR)) ||
(!insem && (mask & HAK_SEMAPHORE_IO_MASK_HANGUP))) (!insem && (mask & HAK_SEMAPHORE_IO_MASK_HANGUP)))
{ {
_signal_io_semaphore(hak, outsem); if ((hak_oop_t)_signal_io_semaphore(hak, outsem) == hak->_nil)
{
/* no process was waiting so the signal is pending and not consumed.
* raise the io signal count unawaited */
hak->sem_io_tuple[sem_io_index].unawaited[HAK_SEMAPHORE_IO_TYPE_OUTPUT]++;
}
} }
} }
@@ -2024,7 +2089,12 @@ static void signal_io_semaphore (hak_t* hak, hak_ooi_t io_handle, hak_ooi_t mask
{ {
if (mask & (HAK_SEMAPHORE_IO_MASK_INPUT | HAK_SEMAPHORE_IO_MASK_HANGUP | HAK_SEMAPHORE_IO_MASK_ERROR)) if (mask & (HAK_SEMAPHORE_IO_MASK_INPUT | HAK_SEMAPHORE_IO_MASK_HANGUP | HAK_SEMAPHORE_IO_MASK_ERROR))
{ {
_signal_io_semaphore(hak, insem); if ((hak_oop_t)_signal_io_semaphore(hak, insem) == hak->_nil)
{
/* no process was waiting so the signal is pending and not consumed.
* raise the io signal count unawaited */
hak->sem_io_tuple[sem_io_index].unawaited[HAK_SEMAPHORE_IO_TYPE_INPUT]++;
}
} }
} }
} }
+3
View File
@@ -403,6 +403,9 @@
/* Define to 1 if you have the <sys/epoll.h> header file. */ /* Define to 1 if you have the <sys/epoll.h> header file. */
#undef HAVE_SYS_EPOLL_H #undef HAVE_SYS_EPOLL_H
/* Define to 1 if you have the <sys/event.h> header file. */
#undef HAVE_SYS_EVENT_H
/* Define to 1 if you have the <sys/ioctl.h> header file. */ /* Define to 1 if you have the <sys/ioctl.h> header file. */
#undef HAVE_SYS_IOCTL_H #undef HAVE_SYS_IOCTL_H
+8 -4
View File
@@ -237,14 +237,18 @@ void hak_fini (hak_t* hak)
hak_cb_t* cb; hak_cb_t* cb;
hak_oow_t i; hak_oow_t i;
/* BEFORE the modules are unloaded. a handle's destructor is a function in
* the module that created it - mod/sys.c's proc_dtor(), say - so unloading
* first leaves hnd->dtor pointing into an unmapped object, and calling it
* jumps into nowhere. glibc often keeps a dlclose()d object mapped, which
* hides this; FreeBSD unmaps it and the process dies with a wild PC.
* we must finalize the handle table before unloading modules. */
hak_finihndtab(hak);
hak_rbt_walk(&hak->modtab, unload_module, hak); hak_rbt_walk(&hak->modtab, unload_module, hak);
hak_rbt_fini(&hak->modtab); hak_rbt_fini(&hak->modtab);
hak_htb_fini(&hak->static_mods); hak_htb_fini(&hak->static_mods);
/* after the modules, so that a module's unload can close its own handles
* first; whatever hak code leaked is closed here. */
hak_finihndtab(hak);
if (hak->log.len > 0) if (hak->log.len > 0)
{ {
/* flush pending log messages just in case. */ /* flush pending log messages just in case. */
+7 -1
View File
@@ -857,6 +857,9 @@ struct hak_semaphore_t
} waiting; /* list of processes waiting on this semaphore */ } waiting; /* list of processes waiting on this semaphore */
/* [END IMPORTANT] */ /* [END IMPORTANT] */
/* number of signals not consumed on ths semaphore.
* it's incremented when the semaphore is signaled.
* it's decremented when it's signaled at least once and awaited */
hak_oop_t count; /* SmallInteger */ hak_oop_t count; /* SmallInteger */
/* nil for normal. SmallInteger if associated with /* nil for normal. SmallInteger if associated with
@@ -1572,7 +1575,7 @@ enum hak_pfbase_type_t
{ {
HAK_PFBASE_FUNC = 0, HAK_PFBASE_FUNC = 0,
HAK_PFBASE_VAR = 1, HAK_PFBASE_VAR = 1,
HAK_PFBASE_CONST = 2 HAK_PFBASE_CONST_SMOOI = 2
}; };
typedef enum hak_pfbase_type_t hak_pfbase_type_t; typedef enum hak_pfbase_type_t hak_pfbase_type_t;
@@ -1640,11 +1643,14 @@ struct hak_mod_data_t
typedef struct hak_mod_data_t hak_mod_data_t; typedef struct hak_mod_data_t hak_mod_data_t;
/* The hak_sem_tuple_t type holds extra information on IO driven semaphore state.
* It doesn't belong to the object memory and is not visible outside the VM instance */
struct hak_sem_tuple_t struct hak_sem_tuple_t
{ {
hak_oop_semaphore_t sem[2]; /* [0] input, [1] output */ hak_oop_semaphore_t sem[2]; /* [0] input, [1] output */
hak_ooi_t handle; /* io handle */ hak_ooi_t handle; /* io handle */
hak_ooi_t mask; hak_ooi_t mask;
hak_ooi_t unawaited[2]; /* the number of IO-raised signal counts unawaited yet and not consumed */
}; };
typedef struct hak_sem_tuple_t hak_sem_tuple_t; typedef struct hak_sem_tuple_t hak_sem_tuple_t;
+90 -26
View File
@@ -221,7 +221,7 @@
# define XPOLLERR POLLERR # define XPOLLERR POLLERR
# define XPOLLHUP POLLHUP # define XPOLLHUP POLLHUP
# elif defined(HAVE_SYS_EVENT_H) && defined(HAVE_KQUEUE) # elif defined(HAVE_SYS_EVENT_H) && defined(HAVE_KQUEUE)
/* netbsd, openbsd, etc */ /* netbsd, openbsd, freebsd, etc */
# include <sys/event.h> # include <sys/event.h>
# define USE_KQUEUE # define USE_KQUEUE
/* fake XPOLLXXX values */ /* fake XPOLLXXX values */
@@ -1557,7 +1557,7 @@ static int _add_poll_fd (hak_t* hak, int fd, int event_mask)
return -1; return -1;
} }
HAK_MEMSET(&tmp[xtn->ev.reg.capa], 0, newcapa - xtn->ev.reg.capa); HAK_MEMSET(&tmp[xtn->ev.reg.capa], 0, (newcapa - xtn->ev.reg.capa) * HAK_SIZEOF(*tmp));
xtn->ev.reg.ptr = tmp; xtn->ev.reg.ptr = tmp;
xtn->ev.reg.capa = newcapa; xtn->ev.reg.capa = newcapa;
} }
@@ -1699,7 +1699,6 @@ static int _add_poll_fd (hak_t* hak, int fd, int event_mask)
static int _del_poll_fd (hak_t* hak, int fd) static int _del_poll_fd (hak_t* hak, int fd)
{ {
#if defined(USE_DEVPOLL) #if defined(USE_DEVPOLL)
xtn_t* xtn = GET_XTN(hak); xtn_t* xtn = GET_XTN(hak);
struct pollfd ev; struct pollfd ev;
@@ -1792,7 +1791,6 @@ static int _del_poll_fd (hak_t* hak, int fd)
} }
MUTEX_UNLOCK(&xtn->ev.reg.pmtx); MUTEX_UNLOCK(&xtn->ev.reg.pmtx);
HAK_DEBUG1(hak, "Cannot remove file descriptor %d from poll - not found\n", fd); HAK_DEBUG1(hak, "Cannot remove file descriptor %d from poll - not found\n", fd);
hak_seterrnum(hak, HAK_ENOENT); hak_seterrnum(hak, HAK_ENOENT);
return -1; return -1;
@@ -1818,7 +1816,6 @@ static int _del_poll_fd (hak_t* hak, int fd)
return 0; return 0;
#else #else
HAK_DEBUG1(hak, "Cannot remove file descriptor %d from poll - not implemented\n", fd); HAK_DEBUG1(hak, "Cannot remove file descriptor %d from poll - not implemented\n", fd);
hak_seterrnum(hak, HAK_ENOIMPL); hak_seterrnum(hak, HAK_ENOIMPL);
return -1; return -1;
@@ -1994,8 +1991,8 @@ kqueue_syserr:
FD_SET(fd, &xtn->ev.reg.wfds); FD_SET(fd, &xtn->ev.reg.wfds);
else else
FD_CLR(fd, &xtn->ev.reg.wfds); FD_CLR(fd, &xtn->ev.reg.wfds);
MUTEX_UNLOCK(&xtn->ev.reg.smtx);
MUTEX_UNLOCK(&xtn->ev.reg.smtx);
return 0; return 0;
#else #else
@@ -2005,6 +2002,52 @@ kqueue_syserr:
#endif #endif
} }
#if defined(USE_THREAD)
/* Drop multiplexer events already sitting in the buffer for this descriptor.
*
* iothr_main() reads events straight into xtn->ev.buf and publishes ev.len;
* the VM consumes them later. Removing a descriptor from the underlying
* multiplexer therefore does not reach events that have already been handed
* back. If the descriptor is then closed and its number reused before the VM
* drains the buffer - which sys.popen/sys.pclose does on every call - the
* stale event is dispatched against whatever now owns that number: a semaphore
* is signalled for a descriptor that was never ready, and the read that
* follows answers nothing. See t/mux-01.hak.
*
* Must not be called from inside the dispatch loop in vm_muxwait(). That loop
* caches ev.len and would walk entries this function compacts away. Nothing
* does today - signalling a semaphore only makes a process runnable, it does
* not run hak code - but the loop has no defence if that ever changes. */
static void purge_muxevts (hak_t* hak, int fd)
{
xtn_t* xtn = GET_XTN(hak);
hak_oow_t i, j;
MUTEX_LOCK(&xtn->ev.mtx);
for (i = 0, j = 0; i < xtn->ev.len; i++)
{
#if defined(USE_DEVPOLL) || defined(USE_POLL) || defined(USE_SELECT)
if (xtn->ev.buf[i].fd == fd) continue;
#elif defined(USE_KQUEUE)
if ((int)xtn->ev.buf[i].ident == fd) continue;
#elif defined(USE_EPOLL)
if (xtn->ev.buf[i].data.fd == fd) continue;
#endif
if (j != i) xtn->ev.buf[j] = xtn->ev.buf[i];
j++;
}
if (j != xtn->ev.len)
{
xtn->ev.len = j;
if (j <= 0) pthread_cond_signal(&xtn->ev.cnd); // TODO: use a generic wrapper??
}
MUTEX_UNLOCK(&xtn->ev.mtx);
}
#endif
static int vm_muxadd (hak_t* hak, hak_ooi_t io_handle, hak_ooi_t mask) static int vm_muxadd (hak_t* hak, hak_ooi_t io_handle, hak_ooi_t mask)
{ {
int event_mask; int event_mask;
@@ -2039,11 +2082,32 @@ static int vm_muxmod (hak_t* hak, hak_ooi_t io_handle, hak_ooi_t mask)
} }
return _mod_poll_fd(hak, io_handle, event_mask); return _mod_poll_fd(hak, io_handle, event_mask);
/* [NOTE]
* this may need the same mux event purge as vm_muxdel() for accuracy.
* if a file descriptor is removed for one direction while another direction
* is still watched, this function is invoked. in that case, xtn->evt.buf
* may have some stale events and they can raise spurious signals.
*
* TODO: per-direction event purge
*/
} }
static int vm_muxdel (hak_t* hak, hak_ooi_t io_handle) static int vm_muxdel (hak_t* hak, hak_ooi_t io_handle)
{ {
return _del_poll_fd(hak, io_handle); int n;
n = _del_poll_fd(hak, io_handle);
#if defined(USE_THREAD)
/* purge after the descriptor is out of the multiplexer, so nothing new can
* be queued for it behind us - and unconditionally, because a failed
* delete does not make a buffered event any less stale.
* delete_sem_from_sem_io_tuple() carries on regardless when force is set.
*/
purge_muxevts(hak, (int)io_handle);
#endif
return n;
} }
#if defined(USE_THREAD) #if defined(USE_THREAD)
@@ -2138,7 +2202,7 @@ static void* iothr_main (void* arg)
} }
#endif #endif
pthread_mutex_lock (&xtn->ev.mtx); MUTEX_LOCK(&xtn->ev.mtx);
if (n <= -1) if (n <= -1)
{ {
/* TODO: don't use HAK_DEBUG2. it's not thread safe... */ /* TODO: don't use HAK_DEBUG2. it's not thread safe... */
@@ -2150,18 +2214,18 @@ static void* iothr_main (void* arg)
xtn->ev.len = n; xtn->ev.len = n;
} }
pthread_cond_signal(&xtn->ev.cnd2); pthread_cond_signal(&xtn->ev.cnd2);
pthread_mutex_unlock (&xtn->ev.mtx); MUTEX_UNLOCK(&xtn->ev.mtx);
} }
else else
{ {
/* the event buffer has not been emptied yet */ /* the event buffer has not been emptied yet */
struct timespec ts; struct timespec ts;
pthread_mutex_lock (&xtn->ev.mtx); MUTEX_LOCK(&xtn->ev.mtx);
if (xtn->ev.len <= 0) if (xtn->ev.len <= 0)
{ {
/* it got emptied between the if check and pthread_mutex_lock() above */ /* it got emptied between the if check and pthread_mutex_lock() above */
pthread_mutex_unlock (&xtn->ev.mtx); MUTEX_UNLOCK(&xtn->ev.mtx);
goto poll_for_event; goto poll_for_event;
} }
@@ -2177,7 +2241,7 @@ static void* iothr_main (void* arg)
#endif #endif
ts.tv_sec += 10; ts.tv_sec += 10;
pthread_cond_timedwait(&xtn->ev.cnd, &xtn->ev.mtx, &ts); pthread_cond_timedwait(&xtn->ev.cnd, &xtn->ev.mtx, &ts);
pthread_mutex_unlock (&xtn->ev.mtx); MUTEX_UNLOCK(&xtn->ev.mtx);
} }
/*sched_yield ();*/ /*sched_yield ();*/
@@ -2231,13 +2295,13 @@ static void vm_muxwait (hak_t* hak, const hak_ntime_t* dur, hak_vmprim_muxwait_c
ts.tv_sec = ns.sec; ts.tv_sec = ns.sec;
ts.tv_nsec = ns.nsec; ts.tv_nsec = ns.nsec;
pthread_mutex_lock (&xtn->ev.mtx); MUTEX_LOCK(&xtn->ev.mtx);
if (xtn->ev.len <= 0) if (xtn->ev.len <= 0)
{ {
/* the event buffer is still empty */ /* the event buffer is still empty */
pthread_cond_timedwait(&xtn->ev.cnd2, &xtn->ev.mtx, &ts); pthread_cond_timedwait(&xtn->ev.cnd2, &xtn->ev.mtx, &ts);
} }
pthread_mutex_unlock (&xtn->ev.mtx); MUTEX_UNLOCK(&xtn->ev.mtx);
} }
n = xtn->ev.len; n = xtn->ev.len;
@@ -2278,10 +2342,11 @@ static void vm_muxwait (hak_t* hak, const hak_ntime_t* dur, hak_vmprim_muxwait_c
#if defined(USE_DEVPOLL) #if defined(USE_DEVPOLL)
revents = xtn->ev.buf[n].revents; revents = xtn->ev.buf[n].revents;
#elif defined(USE_KQUEUE) #elif defined(USE_KQUEUE)
if (xtn->ev.buf[n].filter == EVFILT_READ) mask = HAK_SEMAPHORE_IO_MASK_INPUT; revents = 0;
else if (xtn->ev.buf[n].filter == EVFILT_WRITE) mask = HAK_SEMAPHORE_IO_MASK_OUTPUT; if (xtn->ev.buf[n].filter == EVFILT_READ) revents |= XPOLLIN;
else mask = 0; else if (xtn->ev.buf[n].filter == EVFILT_WRITE) revents |= XPOLLOUT;
goto call_muxwcb_kqueue; if (xtn->ev.buf[n].flags & EV_EOF) revents |= XPOLLHUP;
if (xtn->ev.buf[n].flags & EV_ERROR) revents |= XPOLLERR;
#elif defined(USE_EPOLL) #elif defined(USE_EPOLL)
revents = xtn->ev.buf[n].events; revents = xtn->ev.buf[n].events;
#elif defined(USE_POLL) #elif defined(USE_POLL)
@@ -2299,7 +2364,6 @@ static void vm_muxwait (hak_t* hak, const hak_ntime_t* dur, hak_vmprim_muxwait_c
#if defined(USE_DEVPOLL) #if defined(USE_DEVPOLL)
muxwcb(hak, xtn->ev.buf[n].fd, mask); muxwcb(hak, xtn->ev.buf[n].fd, mask);
#elif defined(USE_KQUEUE) #elif defined(USE_KQUEUE)
call_muxwcb_kqueue:
muxwcb(hak, xtn->ev.buf[n].ident, mask); muxwcb(hak, xtn->ev.buf[n].ident, mask);
#elif defined(USE_EPOLL) #elif defined(USE_EPOLL)
muxwcb(hak, xtn->ev.buf[n].data.fd, mask); muxwcb(hak, xtn->ev.buf[n].data.fd, mask);
@@ -2314,10 +2378,10 @@ static void vm_muxwait (hak_t* hak, const hak_ntime_t* dur, hak_vmprim_muxwait_c
} }
while (n > 0); while (n > 0);
pthread_mutex_lock (&xtn->ev.mtx); MUTEX_LOCK(&xtn->ev.mtx);
xtn->ev.len = 0; xtn->ev.len = 0;
pthread_cond_signal(&xtn->ev.cnd); pthread_cond_signal(&xtn->ev.cnd);
pthread_mutex_unlock (&xtn->ev.mtx); MUTEX_UNLOCK(&xtn->ev.mtx);
} }
#else /* USE_THREAD */ #else /* USE_THREAD */
@@ -2450,10 +2514,11 @@ static void vm_muxwait (hak_t* hak, const hak_ntime_t* dur, hak_vmprim_muxwait_c
#if defined(USE_DEVPOLL) #if defined(USE_DEVPOLL)
revents = xtn->ev.buf[n].revents; revents = xtn->ev.buf[n].revents;
#elif defined(USE_KQUEUE) #elif defined(USE_KQUEUE)
if (xtn->ev.buf[n].filter == EVFILT_READ) mask = HAK_SEMAPHORE_IO_MASK_INPUT; revents = 0;
else if (xtn->ev.buf[n].filter == EVFILT_WRITE) mask = HAK_SEMAPHORE_IO_MASK_OUTPUT; if (xtn->ev.buf[n].filter == EVFILT_READ) revents |= XPOLLIN;
else mask = 0; else if (xtn->ev.buf[n].filter == EVFILT_WRITE) revents |= XPOLLOUT;
goto call_muxwcb_kqueue; if (xtn->ev.buf[n].flags & EV_EOF) revents |= XPOLLHUP;
if (xtn->ev.buf[n].flags & EV_ERROR) revents |= XPOLLERR;
#elif defined(USE_EPOLL) #elif defined(USE_EPOLL)
revents = xtn->ev.buf[n].events; revents = xtn->ev.buf[n].events;
#elif defined(USE_POLL) #elif defined(USE_POLL)
@@ -2473,7 +2538,6 @@ static void vm_muxwait (hak_t* hak, const hak_ntime_t* dur, hak_vmprim_muxwait_c
#if defined(USE_DEVPOLL) #if defined(USE_DEVPOLL)
muxwcb(hak, xtn->ev.buf[n].fd, mask); muxwcb(hak, xtn->ev.buf[n].fd, mask);
#elif defined(USE_KQUEUE) #elif defined(USE_KQUEUE)
call_muxwcb_kqueue:
muxwcb(hak, xtn->ev.buf[n].ident, mask); muxwcb(hak, xtn->ev.buf[n].ident, mask);
#elif defined(USE_EPOLL) #elif defined(USE_EPOLL)
muxwcb(hak, xtn->ev.buf[n].data.fd, mask); muxwcb(hak, xtn->ev.buf[n].data.fd, mask);
+71
View File
@@ -35,6 +35,7 @@
#include <hak-pio.h> #include <hak-pio.h>
#include <hak-str.h> #include <hak-str.h>
#include <stdlib.h> #include <stdlib.h>
#include <signal.h>
#if !defined(_WIN32) #if !defined(_WIN32)
# include <sys/types.h> # include <sys/types.h>
@@ -734,6 +735,76 @@ static hak_pfrc_t pf_sys_pclose (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
static hak_pfinfo_t pfinfos[] = static hak_pfinfo_t pfinfos[] =
{ {
#if defined(SIGABRT)
{ "SIGABRT", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGABRT }},
#endif
#if defined(SIGALRM)
{ "SIGALRM", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGALRM }},
#endif
#if defined(SIGBUS)
{ "SIGBUS", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGBUS }},
#endif
#if defined(SIGCHLD)
{ "SIGCHLD", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGCHLD }},
#endif
#if defined(SIGCONT)
{ "SIGCONT", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGCONT }},
#endif
#if defined(SIGFPE)
{ "SIGFPE", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGFPE }},
#endif
#if defined(SIGHUP)
{ "SIGHUP", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGHUP }},
#endif
#if defined(SIGILL)
{ "SIGILL", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGILL }},
#endif
#if defined(SIGINT)
{ "SIGINT", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGINT }},
#endif
#if defined(SIGKILL)
{ "SIGKILL", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGKILL }},
#endif
#if defined(SIGPIPE)
{ "SIGPIPE", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGPIPE }},
#endif
#if defined(SIGQUIT)
{ "SIGQUIT", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGQUIT }},
#endif
#if defined(SIGSEGV)
{ "SIGSEGV", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGSEGV }},
#endif
#if defined(SIGSTOP)
{ "SIGSTOP", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGSTOP }},
#endif
#if defined(SIGTERM)
{ "SIGTERM", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGTERM }},
#endif
#if defined(SIGTSTP)
{ "SIGTSTP", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGTSTP }},
#endif
#if defined(SIGTTIN)
{ "SIGTTIN", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGTTIN }},
#endif
#if defined(SIGTTOU)
{ "SIGTTOU", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGTTOU }},
#endif
#if defined(SIGURG)
{ "SIGURG", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGURG }},
#endif
#if defined(SIGUSR1)
{ "SIGUSR1", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGUSR1 }},
#endif
#if defined(SIGUSR2)
{ "SIGUSR2", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGUSR2 }},
#endif
#if defined(SIGVTALRM)
{ "SIGVTALRM", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGVTALRM }},
#endif
#if defined(SIGWINCH)
{ "SIGWINCH", { HAK_PFBASE_CONST_SMOOI, HAK_NULL, 0, (hak_oow_t)(hak_ooi_t)SIGWINCH }},
#endif
{ "close", { HAK_PFBASE_FUNC, pf_sys_close, 1, 1 } }, { "close", { HAK_PFBASE_FUNC, pf_sys_close, 1, 1 } },
{ "open", { HAK_PFBASE_FUNC, pf_sys_open, 2, 3 } }, { "open", { HAK_PFBASE_FUNC, pf_sys_open, 2, 3 } },
{ "pclose", { HAK_PFBASE_FUNC, pf_sys_pclose, 1, 1 } }, { "pclose", { HAK_PFBASE_FUNC, pf_sys_pclose, 1, 1 } },
+2 -2
View File
@@ -107,7 +107,7 @@ class ChildGroup(
## semaphore on the signal descriptor for the whole group, once. ## semaphore on the signal descriptor for the whole group, once.
if (not self.shared) { if (not self.shared) {
set shared true set shared true
system-catch-sig 17 ## SIGCHLD - define this as a constant... system-catch-sig sys.SIGCHLD
set sigsem (sem-new) set sigsem (sem-new)
semgr-add self.sg self.sigsem semgr-add self.sg self.sigsem
sem-signal-on-input self.sigsem (system-get-sigfd) sem-signal-on-input self.sigsem (system-get-sigfd)
@@ -228,7 +228,7 @@ class ChildGroup(
} }
if self.shared { if self.shared {
sem-unsignal self.sigsem sem-unsignal self.sigsem
system-uncatch-sig 17 system-uncatch-sig sys.SIGCHLD
set shared false set shared false
} }
} }
+3
View File
@@ -19,12 +19,15 @@ check_SCRIPTS = \
hnd-03.hak \ hnd-03.hak \
insta-01.hak \ insta-01.hak \
insta-02.hak \ insta-02.hak \
mux-01.hak \
mux-02.hak \
prim-01.hak \ prim-01.hak \
proc-01.hak \ proc-01.hak \
proclib-01.hak \ proclib-01.hak \
ret-01.hak \ ret-01.hak \
retvar-01.hak \ retvar-01.hak \
sig-01.hak \ sig-01.hak \
sig-02.hak \
sysproc-01.hak \ sysproc-01.hak \
sysproc-02.hak \ sysproc-02.hak \
tick-01.hak \ tick-01.hak \
+3
View File
@@ -557,12 +557,15 @@ check_SCRIPTS = \
hnd-03.hak \ hnd-03.hak \
insta-01.hak \ insta-01.hak \
insta-02.hak \ insta-02.hak \
mux-01.hak \
mux-02.hak \
prim-01.hak \ prim-01.hak \
proc-01.hak \ proc-01.hak \
proclib-01.hak \ proclib-01.hak \
ret-01.hak \ ret-01.hak \
retvar-01.hak \ retvar-01.hak \
sig-01.hak \ sig-01.hak \
sig-02.hak \
sysproc-01.hak \ sysproc-01.hak \
sysproc-02.hak \ sysproc-02.hak \
tick-01.hak \ tick-01.hak \
+1 -7
View File
@@ -23,7 +23,7 @@ sys.read r (core.basicNew ByteArray 4) ##ERROR: system handle 0
## a regular file is never accepted by the multiplexer: epoll refuses one ## a regular file is never accepted by the multiplexer: epoll refuses one
## outright, and poll() would report it permanently ready ## outright, and poll() would report it permanently ready
f := (sys.open "/proc/version" "r") f := (sys.open "/etc/passwd" "r")
s := (sem-new) s := (sem-new)
sem-signal-on-input s f ##ERROR: not of an acceptable kind sem-signal-on-input s f ##ERROR: not of an acceptable kind
@@ -41,12 +41,6 @@ sys.read (core.basicAt p 0) (core.basicNew ByteArray 4) ##ERROR: not of an accep
--- ---
## an exit handle is muxable but carries no bytes
p := (sys.popen "true" "r")
sys.read (core.basicAt p 4) (core.basicNew ByteArray 4) ##ERROR: not readable
---
## a missing file is reported, not guessed at ## a missing file is reported, not guessed at
sys.open "/nonexistent/definitely-not-here" "r" ##ERROR: open /nonexistent/definitely-not-here sys.open "/nonexistent/definitely-not-here" "r" ##ERROR: open /nonexistent/definitely-not-here
+54
View File
@@ -0,0 +1,54 @@
## a recycled descriptor number must not inherit a readiness event
##
## The io thread reads multiplexer events into a buffer that the VM drains
## later, so unregistering a descriptor does not reach events already handed
## back. sys.pclose then closes the descriptor and the next sys.popen gets the
## same number, at which point a leftover event is delivered against the new
## pipe: the semaphore fires for something that was never ready and the read
## finds nothing.
##
## The child sleeps before writing, so a wakeup that arrives before the write
## is unmistakably spurious rather than merely early - which is what makes this
## deterministic instead of a race. Each round must read its own three bytes.
fun chk(ok msg) {
if ok { printf "OK: %s\n" msg } \
else { printf "ERROR: %s\n" msg }
}
iosem := (sem-new)
tmo := (sem-new)
sg := (semgr-new)
semgr-add sg iosem
semgr-add sg tmo
## 1 when the handle became readable, 0 when the timer won instead
fun waitin(h secs) {
| s |
sem-signal tmo secs 0
sem-signal-on-input iosem h
s := (semgr-wait sg)
sem-unsignal iosem
sem-unsignal tmo
if (eqv? s tmo) { return 0 } else { return 1 }
}
fun round(n) {
| p outh buf ready got |
p := (sys.popen "sleep 0.4; echo hi" "r")
outh := (core.basicAt p 2)
ready := (waitin outh 5)
buf := (core.basicNew ByteArray 8)
got := (sys.read outh buf)
chk (== ready 1) "the child's output was reported ready"
## -1 here means the wakeup belonged to a previous round's pipe
chk (== got 3) "the ready descriptor really had the child's bytes"
sys.pclose (core.basicAt p 0)
}
## every round closes its pipe, so the next popen gets the same descriptor
## number back - that reuse is what exposes a stale event
round 1
round 2
round 3
round 4
+66
View File
@@ -0,0 +1,66 @@
## several io bindings alive at once, and unbinding one from the middle
##
## Each sem-signal-on-input adds an entry to the VM's io tuple array; removing
## one compacts the gap by migrating the last entry into the freed slot, which
## has to fix up the moved semaphore's recorded index. Nothing exercises that
## unless two or more bindings exist simultaneously - a single binding always
## occupies the last slot and compaction is a no-op.
##
## The children finish in a known order, so the semaphore each wait answers is
## predictable, and a mixed-up index shows as the wrong semaphore or a read
## that finds nothing.
fun chk(ok msg) {
if ok { printf "OK: %s\n" msg } \
else { printf "ERROR: %s\n" msg }
}
sg := (semgr-new)
sa := (sem-new)
sb := (sem-new)
sc := (sem-new)
tmo := (sem-new)
semgr-add sg sa
semgr-add sg sb
semgr-add sg sc
semgr-add sg tmo
## staggered so the completion order is a, then b, then c
pa := (sys.popen "sleep 0.2; echo aaa" "r")
pb := (sys.popen "sleep 0.5; echo bbb" "r")
pc := (sys.popen "sleep 0.8; echo ccc" "r")
ha := (core.basicAt pa 2)
hb := (core.basicAt pb 2)
hc := (core.basicAt pc 2)
## all three bound at the same time - three live tuple entries
sem-signal-on-input sa ha
sem-signal-on-input sb hb
sem-signal-on-input sc hc
sem-signal tmo 9 0
fun expect(want h name) {
| s buf got |
s := (semgr-wait sg)
chk (eqv? s want) name
buf := (core.basicNew ByteArray 8)
got := (sys.read h buf)
chk (== got 4) "the woken handle carried its child's bytes"
}
## unbinding sa frees the first slot, so the last entry migrates into it and
## sb/sc must still resolve to their own handles afterwards
expect sa ha "the first child woke its own semaphore"
sem-unsignal sa
expect sb hb "the second child woke its own semaphore after compaction"
sem-unsignal sb
expect sc hc "the third child woke its own semaphore after compaction"
sem-unsignal sc
sem-unsignal tmo
sys.pclose (core.basicAt pa 0)
sys.pclose (core.basicAt pb 0)
sys.pclose (core.basicAt pc 0)
+7 -7
View File
@@ -27,15 +27,15 @@ sys.close (core.basicAt q 0)
sys.close (core.basicAt q 1) sys.close (core.basicAt q 1)
## --- catch and uncatch are idempotent --- ## --- catch and uncatch are idempotent ---
chk (= (system-catch-sig 10) 10) "system-catch-sig returns the signal number" chk (= (system-catch-sig sys.SIGUSR1) sys.SIGUSR1) "system-catch-sig returns the signal number"
chk (= (system-catch-sig 10) 10) "catching an already caught signal is fine" chk (= (system-catch-sig sys.SIGUSR1) sys.SIGUSR1) "catching an already caught signal is fine"
chk (= (system-uncatch-sig 10) 10) "system-uncatch-sig returns the signal number" chk (= (system-uncatch-sig sys.SIGUSR1) sys.SIGUSR1) "system-uncatch-sig returns the signal number"
chk (= (system-uncatch-sig 10) 10) "uncatching an uncaught signal is fine" chk (= (system-uncatch-sig sys.SIGUSR1) sys.SIGUSR1) "uncatching an uncaught signal is fine"
## --- a real signal reaches hak code, without stalling the coprocesses --- ## --- a real signal reaches hak code, without stalling the coprocesses ---
## SIGCHLD is used because a child exiting is something this test can arrange ## SIGCHLD is used because a child exiting is something this test can arrange
## on its own, with no outside help. ## on its own, with no outside help.
system-catch-sig 17 system-catch-sig sys.SIGCHLD
h := (system-get-sigfd) h := (system-get-sigfd)
s := (sem-new) s := (sem-new)
@@ -76,7 +76,7 @@ fork ticker
sem-wait fin sem-wait fin
chk (= ticks 4) "coprocesses ran while a coprocess waited on a signal" chk (= ticks 4) "coprocesses ran while a coprocess waited on a signal"
chk (= signo 17) "the signal number came through the signal descriptor" chk (= signo sys.SIGCHLD) "the signal number came through the signal descriptor"
chk (= (sys.pwait proc) 4) "and the child's exit status is readable" chk (= (sys.pwait proc) 4) "and the child's exit status is readable"
sys.pclose proc sys.pclose proc
system-uncatch-sig 17 system-uncatch-sig sys.SIGCHLD
+61
View File
@@ -0,0 +1,61 @@
## the signal constants exported by the sys module
##
## Signal numbers differ between platforms - 10 is SIGUSR1 on Linux but SIGBUS
## on FreeBSD, 17 is SIGCHLD on Linux but SIGSTOP on FreeBSD - so hak code must
## name them rather than spell the numbers. These constants exist for that.
##
## Naming one that does not resolve is a compile-time error, which makes this
## file a guard on the module's pfinfo table too: that table is binary-searched
## by hak_findpfbase(), so an entry out of C-collation order stops resolving
## with no other symptom.
fun chk(ok msg) {
if ok { printf "OK: %s\n" msg } \
else { printf "ERROR: %s\n" msg }
}
fun isnum(v name) { chk (integer? v) name }
isnum sys.SIGABRT "sys.SIGABRT"
isnum sys.SIGALRM "sys.SIGALRM"
isnum sys.SIGBUS "sys.SIGBUS"
isnum sys.SIGCHLD "sys.SIGCHLD"
isnum sys.SIGCONT "sys.SIGCONT"
isnum sys.SIGFPE "sys.SIGFPE"
isnum sys.SIGHUP "sys.SIGHUP"
isnum sys.SIGILL "sys.SIGILL"
isnum sys.SIGINT "sys.SIGINT"
isnum sys.SIGKILL "sys.SIGKILL"
isnum sys.SIGPIPE "sys.SIGPIPE"
isnum sys.SIGQUIT "sys.SIGQUIT"
isnum sys.SIGSEGV "sys.SIGSEGV"
isnum sys.SIGSTOP "sys.SIGSTOP"
isnum sys.SIGTERM "sys.SIGTERM"
isnum sys.SIGTSTP "sys.SIGTSTP"
isnum sys.SIGTTIN "sys.SIGTTIN"
isnum sys.SIGTTOU "sys.SIGTTOU"
isnum sys.SIGURG "sys.SIGURG"
isnum sys.SIGUSR1 "sys.SIGUSR1"
isnum sys.SIGUSR2 "sys.SIGUSR2"
isnum sys.SIGVTALRM "sys.SIGVTALRM"
isnum sys.SIGWINCH "sys.SIGWINCH"
## the constants must be distinct from one another - a table entry pointing at
## the wrong value would otherwise pass every check above
chk (not (== sys.SIGKILL sys.SIGSTOP)) "SIGKILL and SIGSTOP differ"
chk (not (== sys.SIGUSR1 sys.SIGUSR2)) "SIGUSR1 and SIGUSR2 differ"
chk (not (== sys.SIGCHLD sys.SIGSTOP)) "SIGCHLD and SIGSTOP differ"
chk (not (== sys.SIGBUS sys.SIGUSR1)) "SIGBUS and SIGUSR1 differ"
## these three are the same number everywhere hak runs, so they can be pinned
chk (== sys.SIGKILL 9) "SIGKILL is 9"
chk (== sys.SIGSEGV 11) "SIGSEGV is 11"
chk (== sys.SIGTERM 15) "SIGTERM is 15"
## and the constants behave: a routable one can be caught, an unroutable one
## cannot, whatever the numbers happen to be here
chk (== (system-catch-sig sys.SIGUSR1) sys.SIGUSR1) "SIGUSR1 is catchable"
chk (== (system-uncatch-sig sys.SIGUSR1) sys.SIGUSR1) "SIGUSR1 is uncatchable again"
raised := false
try { system-catch-sig sys.SIGKILL } catch (e) { raised := true }
chk raised "SIGKILL is refused"
+7 -7
View File
@@ -1,32 +1,32 @@
## signals that cannot be caught ## signals that cannot be caught
system-catch-sig 9 ##ERROR: 9 not routable system-catch-sig sys.SIGKILL ##ERROR: not routable
--- ---
system-catch-sig 19 ##ERROR: 19 not routable system-catch-sig sys.SIGSTOP ##ERROR: not routable
--- ---
## signals that indicate a crash: turning one into a byte on a pipe and ## signals that indicate a crash: turning one into a byte on a pipe and
## carrying on would hide the fault rather than report it ## carrying on would hide the fault rather than report it
system-catch-sig 11 ##ERROR: 11 not routable system-catch-sig sys.SIGSEGV ##ERROR: not routable
--- ---
system-catch-sig 7 ##ERROR: 7 not routable system-catch-sig sys.SIGBUS ##ERROR: not routable
--- ---
system-catch-sig 8 ##ERROR: 8 not routable system-catch-sig sys.SIGFPE ##ERROR: not routable
--- ---
system-catch-sig 4 ##ERROR: 4 not routable system-catch-sig sys.SIGILL ##ERROR: not routable
--- ---
## the timer signal hak itself uses to switch processes ## the timer signal hak itself uses to switch processes
system-catch-sig 26 ##ERROR: 26 not routable system-catch-sig sys.SIGVTALRM ##ERROR: not routable
--- ---
+14
View File
@@ -98,3 +98,17 @@ p := (sys.popen "sleep 60" "r")
outh := (core.basicAt p 2) outh := (core.basicAt p 2)
sys.pclose (core.basicAt p 0) sys.pclose (core.basicAt p 0)
chk true "pclose released the group" chk true "pclose released the group"
## --- the exit handle ---
## it is muxable but carries no bytes. it exists only where the platform has
## pidfd_open(); elsewhere sys.popen answers nil for it, so guard the check
## rather than assuming Linux.
p := (sys.popen "true" "r")
xh := (core.basicAt p 4)
if (nil? xh) { printf "OK: no exit handle where the platform has no pidfd\n" } \
else {
raised := false
try { sys.read xh (core.basicNew ByteArray 4) } catch (e) { raised := true }
chk raised "the exit handle carries no bytes"
}
sys.pclose (core.basicAt p 0)