Compare commits

...
27 Commits
Author SHA1 Message Date
hyung-hwan 1d17f1e708 fixed the mssing stack finalization is multiple process manipulation primitive functions 2026-09-15 20:29:09 +09:00
hyung-hwan f99a8c87f0 updated the Semaphore methods to relay the return value of primitive calls 2026-09-12 11:49:10 +09:00
hyung-hwan 7be5d0fe61 code simplification by using MUXEVT_FD in more places 2026-09-12 11:33:51 +09:00
hyung-hwan baa01b22e3 relocated signal primitives to the sys module 2026-09-11 23:12:40 +09:00
hyung-hwan 58d1234020 added ticker to bin/main.go 2026-09-11 16:01:36 +09:00
hyung-hwan bcdc7d8d36 ported the same command line options to the go code 2026-09-11 14:36:51 +09:00
hyung-hwan 1a8cc6ef43 split HAK_OPT_MODLIBDIRS and HAK_OPT_INCDIRS to BCSTR/UCSTR 2026-09-11 12:33:30 +09:00
hyung-hwan 15b42bc269 make the style of option enumerators more uniform 2026-09-11 12:02:12 +09:00
hyung-hwan 992b1344a8 update hak.go with modlibdirs and incdirs... still not complete 2026-09-11 10:58:28 +09:00
hyung-hwan 27115ca792 added HAK_OPT_INCDIRS 2026-09-11 10:44:58 +09:00
hyung-hwan 05b7e3f728 added the option.incdirs field 2026-09-10 06:00:04 +00:00
hyung-hwan e63dc2c2a0 relocated some primitive functions(process,semaphore,etc) to the core module 2026-09-10 14:30:51 +09:00
hyung-hwan 92caa7e8db creating Seamphore and SemaphoreGroup classes 2026-09-10 13:53:25 +09:00
hyung-hwan 4b4238dc50 fixed the return in the catch block bug. the try_exist instruction should not have been emitted for return when directly in a catch block 2026-09-05 00:59:19 +09:00
hyung-hwan 175e666954 fixed the return in try bug. when compiling return, it was emitting the try_exit instruction too early before instructions for return value evaluation 2026-09-05 00:00:28 +09:00
hyung-hwan 586094ae8f added HAK_CLSTK_SIZE and EXSTK_SIZE 2026-09-03 20:35:32 +09:00
hyung-hwan de8c567a87 updated the poll/select branch to fix the delayed waked up of io signaling thread 2026-09-02 18:58:31 +09:00
hyung-hwan 10051c7851 updated the poll path to handle stale events in std.c 2026-09-02 15:11:20 +09:00
hyung-hwan df07bd7eb2 updated start_ticker and stop_ticker to use atomic operations if available
defined a few atomic operation macros
2026-09-02 13:11:18 +09:00
hyung-hwan e5ce0304f8 fixed a c89 violation in test code 2026-09-01 23:52:10 +09:00
hyung-hwan 58d3eaa562 more update to kqueue/kevent handling in std.c 2026-09-01 23:20:34 +09:00
hyung-hwan fdda6998f2 updated muxmod to purge the buffered events from the modified io handle 2026-09-01 23:07:20 +09:00
hyung-hwan 2cb4ddf0b3 changed the HAVE_ESCAPE_U check as the previous check didn't work well 2026-09-01 22:14:14 +09:00
hyung-hwan f77ec1049d added HAVE_ESCAPE_U check 2026-09-01 20:11:02 +09:00
hyung-hwan 897e7477bc minor fixes to cater for old compilers 2026-09-01 05:36:58 +00:00
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
50 changed files with 2743 additions and 698 deletions
+2 -1
View File
@@ -75,7 +75,8 @@ hak_DEPENDENCIES = ../lib/libhak.la
if ENABLE_ISOCLINE
hak_CPPFLAGS += -I$(srcdir)/isocline/include -DHAVE_ISOCLINE_H -DHAVE_ISOCLINE_LIB
hak_LDADD += ./libisocline.a
hak_DEPENDENCIES += ./libisocline.a
##hak_DEPENDENCIES += ./libisocline.a
hak_DEPENDENCIES += libisocline.a
endif
if ENABLE_HAKX
+1 -1
View File
@@ -101,7 +101,7 @@ host_triplet = @host@
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_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_HAKGO_TRUE@am__append_7 = hakgo
subdir = bin
+55 -49
View File
@@ -249,8 +249,8 @@ static int handle_logopt (hak_t* hak, const hak_bch_t* logstr)
}
fname.ptr = (hak_bch_t*)logstr;
hak_setoption (hak, HAK_LOG_TARGET_BCS, &fname);
hak_setoption (hak, HAK_LOG_MASK, &logmask);
hak_setoption (hak, HAK_OPT_LOG_TARGET_BCS, &fname);
hak_setoption (hak, HAK_OPT_LOG_MASK, &logmask);
return 0;
}
@@ -280,9 +280,9 @@ static int handle_dbgopt (hak_t* hak, const hak_bch_t* str)
}
while (cm);
hak_getoption(hak, HAK_TRAIT, &trait);
hak_getoption(hak, HAK_OPT_TRAIT, &trait);
trait |= dbgopt;
hak_setoption(hak, HAK_TRAIT, &trait);
hak_setoption(hak, HAK_OPT_TRAIT, &trait);
return 0;
}
#endif
@@ -303,19 +303,19 @@ typedef void(*signal_handler_t)(int);
#if defined(_WIN32) || defined(__DOS__) || defined(__OS2__)
static void handle_sigint (int sig)
{
if (g_hak) hak_abort (g_hak);
if (g_hak) hak_abort(g_hak);
}
#elif defined(macintosh)
/* TODO */
#elif defined(SA_SIGINFO)
static void handle_sigint (int sig, siginfo_t* siginfo, void* ctx)
{
if (g_hak) hak_abort (g_hak);
if (g_hak) hak_abort(g_hak);
}
#else
static void handle_sigint (int sig)
{
if (g_hak) hak_abort (g_hak);
if (g_hak) hak_abort(g_hak);
}
#endif
@@ -328,7 +328,7 @@ static void set_signal (int sig, signal_handler_t handler)
#else
struct sigaction sa;
memset (&sa, 0, sizeof(sa));
memset(&sa, 0, sizeof(sa));
/*sa.sa_handler = handler;*/
#if defined(SA_SIGINFO)
sa.sa_flags = SA_SIGINFO;
@@ -336,16 +336,16 @@ static void set_signal (int sig, signal_handler_t handler)
#else
sa.sa_handler = handler;
#endif
sigemptyset (&sa.sa_mask);
sigemptyset(&sa.sa_mask);
sigaction (sig, &sa, NULL);
sigaction(sig, &sa, NULL);
#endif
}
static void set_signal_to_default (int sig)
{
#if defined(_WIN32) || defined(__DOS__) || defined(__OS2__)
signal (sig, SIG_DFL);
signal(sig, SIG_DFL);
#elif defined(macintosh)
/* TODO: implement this */
#else
@@ -354,27 +354,27 @@ static void set_signal_to_default (int sig)
memset (&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
sa.sa_flags = 0;
sigemptyset (&sa.sa_mask);
sigemptyset(&sa.sa_mask);
sigaction (sig, &sa, NULL);
sigaction(sig, &sa, NULL);
#endif
}
static void set_signal_to_ignore (int sig)
{
#if defined(_WIN32) || defined(__DOS__) || defined(__OS2__)
signal (sig, SIG_IGN);
signal(sig, SIG_IGN);
#elif defined(macintosh)
/* TODO: implement this */
#else
struct sigaction sa;
memset (&sa, 0, sizeof(sa));
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
sigemptyset (&sa.sa_mask);
sigaction (sig, &sa, NULL);
sigaction(sig, &sa, NULL);
#endif
}
@@ -865,6 +865,7 @@ int main (int argc, char* argv[])
{ ":debug", '\0' },
#endif
{ ":heapsize", '\0' },
{ ":incdirs", 'I' },
{ ":log", 'l' },
{ "info", '\0' },
{ ":modlibdirs", '\0' },
@@ -873,7 +874,7 @@ int main (int argc, char* argv[])
};
static hak_bopt_t opt =
{
"l:v",
"I:l:v",
lopt
};
@@ -882,6 +883,7 @@ int main (int argc, char* argv[])
int verbose = 0;
int show_info = 0;
const char* modlibdirs = HAK_NULL;
const char* incdirs = HAK_NULL;
#if defined(HAK_BUILD_DEBUG)
const char* dbgopt = HAK_NULL;
@@ -895,10 +897,11 @@ int main (int argc, char* argv[])
print_usage:
fprintf(stderr, "Usage: %s [options] script-filename [output-filename]\n", argv[0]);
fprintf(stderr, "Options are:\n");
fprintf(stderr, " --info show build information\n");
fprintf(stderr, " -l, --log string specify the log file path and options\n");
fprintf(stderr, " --modlibdirs string specify directories to load modules from\n");
fprintf(stderr, " -v show verbose messages\n");
fprintf(stderr, " --info show build information\n");
fprintf(stderr, " -I, --incdirs string specify the list of include directories\n");
fprintf(stderr, " -l, --log string specify the log file path and options\n");
fprintf(stderr, " --modlibdirs string specify directories to load modules from\n");
fprintf(stderr, " -v show verbose messages\n");
return -1;
}
@@ -907,6 +910,10 @@ int main (int argc, char* argv[])
{
switch (c)
{
case 'I':
incdirs = opt.arg;
break;
case 'l':
logopt = opt.arg;
break;
@@ -979,12 +986,16 @@ int main (int argc, char* argv[])
{
hak_oow_t tab_size;
tab_size = 5000;
hak_setoption (hak, HAK_SYMTAB_SIZE, &tab_size);
tab_size = 5000;
hak_setoption (hak, HAK_SYSDIC_SIZE, &tab_size);
tab_size = 600; /* TODO: choose a better stack size or make this user specifiable */
hak_setoption (hak, HAK_PROCSTK_SIZE, &tab_size);
tab_size = HAK_DFL_SYMTAB_SIZE;
hak_setoption (hak, HAK_OPT_SYMTAB_SIZE, &tab_size);
tab_size = HAK_DFL_SYSDIC_SIZE;
hak_setoption (hak, HAK_OPT_SYSDIC_SIZE, &tab_size);
tab_size = HAK_DFL_PROCSTK_SIZE; /* TODO: choose a better stack size or make this user specifiable */
hak_setoption (hak, HAK_OPT_PROCSTK_SIZE, &tab_size);
tab_size = HAK_DFL_EXSTK_SIZE; /* TODO: choose a better stack size or make this user specifiable */
hak_setoption (hak, HAK_OPT_EXSTK_SIZE, &tab_size);
tab_size = HAK_DFL_CLSTK_SIZE; /* TODO: choose a better stack size or make this user specifiable */
hak_setoption (hak, HAK_OPT_CLSTK_SIZE, &tab_size);
}
{
@@ -993,34 +1004,29 @@ int main (int argc, char* argv[])
/*trait |= HAK_TRAIT_NOGC;*/
trait |= HAK_TRAIT_AWAIT_PROCS;
trait |= HAK_TRAIT_LANG_ENABLE_EOL;
hak_setoption (hak, HAK_TRAIT, &trait);
hak_setoption (hak, HAK_OPT_TRAIT, &trait);
}
if (incdirs)
{
/* the option is stored in both encodings, so the byte form from
* the command line goes in as is - no conversion needed here. */
if (hak_setoption(hak, HAK_OPT_INCDIRS_BCSTR, incdirs) <= -1)
{
hak_logbfmt(hak, HAK_LOG_STDERR,"ERROR: cannot set incdirs - [%d] %js\n", hak_geterrnum(hak), hak_geterrmsg(hak));
goto oops;
}
}
if (modlibdirs)
{
#if defined(HAK_OOCH_IS_UCH)
hak_ooch_t* tmp;
tmp = hak_dupbtoucstr(hak, modlibdirs, HAK_NULL);
if (HAK_UNLIKELY(!tmp))
{
hak_logbfmt(hak, HAK_LOG_STDERR,"ERROR: cannot duplicate modlibdirs - [%d] %js\n", hak_geterrnum(hak), hak_geterrmsg(hak));
goto oops;
}
if (hak_setoption(hak, HAK_MOD_LIBDIRS, tmp) <= -1)
{
hak_logbfmt(hak, HAK_LOG_STDERR,"ERROR: cannot set modlibdirs - [%d] %js\n", hak_geterrnum(hak), hak_geterrmsg(hak));
hak_freemem(hak, tmp);
goto oops;
}
hak_freemem(hak, tmp);
#else
if (hak_setoption(hak, HAK_MOD_LIBDIRS, modlibdirs) <= -1)
/* the option is stored in both encodings, so the byte form from
* the command line goes in as is - no conversion needed here. */
if (hak_setoption(hak, HAK_OPT_MODLIBDIRS_BCSTR, modlibdirs) <= -1)
{
hak_logbfmt(hak, HAK_LOG_STDERR,"ERROR: cannot set modlibdirs - [%d] %js\n", hak_geterrnum(hak), hak_geterrmsg(hak));
goto oops;
}
#endif
}
memset (&hakcb, 0, HAK_SIZEOF(hakcb));
@@ -1091,9 +1097,9 @@ int main (int argc, char* argv[])
// in the non-INTERACTIVE mode, the compiler generates MAKE_BLOCK for lambda functions.
{
hak_bitmask_t trait;
hak_getoption(hak, HAK_TRAIT, &trait);
hak_getoption(hak, HAK_OPT_TRAIT, &trait);
trait |= HAK_TRAIT_INTERACTIVE;
hak_setoption(hak, HAK_TRAIT, &trait);
hak_setoption(hak, HAK_OPT_TRAIT, &trait);
}
#endif
+217 -16
View File
@@ -4,8 +4,10 @@ import (
"flag"
"fmt"
"hak"
"io"
"os"
//"strings"
"strings"
"time"
)
/*
@@ -18,6 +20,9 @@ import (
`))
*/
/* 0 means no pre-allocated heap, as in bin/hak.c */
const DEFAULT_HEAPSIZE uint = 0
/* to be set in build time */
var BINDIR = "."
var SBINDIR = "."
@@ -25,10 +30,14 @@ var LIBDIR = "."
var SYSCONFDIR = "."
type Param struct {
log_file string
log_target string
log_mask hak.BitMask
input_file string
heapsize uint
modlibdirs string
incdirs string
verbose bool
show_info bool
fs_usage func()
}
@@ -36,6 +45,93 @@ func empty_usage() {
}
/* the filter names accepted after the comma in --log, mirroring the xtab
* table in handle_logopt() of bin/hak.c. 'and' marks the entries that clear
* bits instead of setting them. */
type log_filter struct {
name string
and bool
mask hak.BitMask
}
var log_filters = []log_filter{
{"", false, 0},
{"app", false, hak.LOG_APP},
{"compiler", false, hak.LOG_COMPILER},
{"vm", false, hak.LOG_VM},
{"mnemonic", false, hak.LOG_MNEMONIC},
{"gc", false, hak.LOG_GC},
{"ic", false, hak.LOG_IC},
{"primitive", false, hak.LOG_PRIMITIVE},
/* a specific level */
{"fatal", false, hak.LOG_FATAL},
{"error", false, hak.LOG_ERROR},
{"warn", false, hak.LOG_WARN},
{"info", false, hak.LOG_INFO},
{"debug", false, hak.LOG_DEBUG},
/* a specific level or higher */
{"fatal+", false, hak.LOG_FATAL},
{"error+", false, hak.LOG_FATAL | hak.LOG_ERROR},
{"warn+", false, hak.LOG_FATAL | hak.LOG_ERROR | hak.LOG_WARN},
{"info+", false, hak.LOG_FATAL | hak.LOG_ERROR | hak.LOG_WARN | hak.LOG_INFO},
{"debug+", false, hak.LOG_FATAL | hak.LOG_ERROR | hak.LOG_WARN | hak.LOG_INFO | hak.LOG_DEBUG},
/* a specific level or lower */
{"fatal-", false, hak.LOG_FATAL | hak.LOG_ERROR | hak.LOG_WARN | hak.LOG_INFO | hak.LOG_DEBUG},
{"error-", false, hak.LOG_ERROR | hak.LOG_WARN | hak.LOG_INFO | hak.LOG_DEBUG},
{"warn-", false, hak.LOG_WARN | hak.LOG_INFO | hak.LOG_DEBUG},
{"info-", false, hak.LOG_INFO | hak.LOG_DEBUG},
{"debug-", false, hak.LOG_DEBUG},
/* exclude a specific level */
{"-fatal", true, ^hak.LOG_FATAL},
{"-error", true, ^hak.LOG_ERROR},
{"-warn", true, ^hak.LOG_WARN},
{"-info", true, ^hak.LOG_INFO},
{"-debug", true, ^hak.LOG_DEBUG},
}
/* split "path,filter,filter" into a target and a log mask. with no comma the
* whole string is the target and every level and type is enabled. */
func parse_logopt(logstr string) (string, hak.BitMask, error) {
var comma int = strings.Index(logstr, ",")
var mask hak.BitMask
if comma < 0 {
return logstr, hak.LOG_ALL_LEVELS | hak.LOG_ALL_TYPES, nil
}
for _, f := range strings.Split(logstr[comma+1:], ",") {
var i int
for i = 0; i < len(log_filters); i++ {
if log_filters[i].name == f {
if log_filters[i].and {
mask &= log_filters[i].mask
} else {
mask |= log_filters[i].mask
}
break
}
}
if i >= len(log_filters) {
return "", 0, fmt.Errorf("unrecognized log filter - %s - in %s", f, logstr)
}
}
/* nothing selected in a category means everything in that category */
if (mask & hak.LOG_ALL_TYPES) == 0 {
mask |= hak.LOG_ALL_TYPES
}
if (mask & hak.LOG_ALL_LEVELS) == 0 {
mask |= hak.LOG_ALL_LEVELS
}
return logstr[0:comma], mask, nil
}
func handle_arguments(param *Param) error {
/*
var nargs int = len(os.Args)
@@ -64,34 +160,110 @@ func handle_arguments(param *Param) error {
var fs *flag.FlagSet
var heapsize *uint
var modlibdirs *string
var incdirs *string
var log *string
var verbose *bool
var show_info *bool
var err error
fs = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
heapsize = fs.Uint("heapsize", 0, "specify heap size")
modlibdirs = fs.String("modlibdirs", "", "specify module library directories")
log = fs.String("log", "", "specify log file")
/* the same set bin/hak.c accepts. the flag package treats -x and --x
* alike, so registering both spellings of a short option is enough to
* give -I and --incdirs the same destination. */
heapsize = fs.Uint("heapsize", DEFAULT_HEAPSIZE, "specify the heap size in bytes")
incdirs = fs.String("incdirs", "", "specify the list of include directories")
fs.StringVar(incdirs, "I", "", "specify the list of include directories")
log = fs.String("log", "", "specify the log file path and options")
fs.StringVar(log, "l", "", "specify the log file path and options")
modlibdirs = fs.String("modlibdirs", "", "specify directories to load modules from")
verbose = fs.Bool("v", false, "show verbose messages")
show_info = fs.Bool("info", false, "show build information")
param.fs_usage = fs.Usage
fs.Usage = empty_usage // i don't want fs.Parse() print the usage
fs.Usage = empty_usage // i don't want fs.Parse() print the usage
fs.SetOutput(io.Discard) // nor its own copy of the error, which we report ourselves
err = fs.Parse(os.Args[1:])
fs.Usage = param.fs_usage // restore it
fs.SetOutput(os.Stderr) // so the restored usage still prints
if err != nil {
return fmt.Errorf("command line error - %s", err.Error())
}
param.heapsize = *heapsize
param.incdirs = *incdirs
param.modlibdirs = *modlibdirs
param.verbose = *verbose
param.show_info = *show_info
if *log != "" {
param.log_target, param.log_mask, err = parse_logopt(*log)
if err != nil {
return err
}
}
/* --info answers on its own and needs no script */
if param.show_info {
return nil
}
if fs.NArg() < 1 {
return fmt.Errorf("no input file specified")
} else if fs.NArg() > 1 {
/* bin/hak.c also takes an optional output file as the second
* argument, but the go binding attaches the user data streams
* through handler objects rather than a path, so there is nothing
* to pass it to yet. */
return fmt.Errorf("too many input files specified")
}
param.input_file = fs.Arg(0);
param.log_file = *log // TODO: parse the option part (e.g. --log /dev/stderr,debug+)
param.heapsize = *heapsize // TODO: set this to hak
param.modlibdirs = *modlibdirs // TODO: set this to hak
return nil;
param.input_file = fs.Arg(0)
return nil
}
func start_ticker(x *hak.Hak) func() {
var ticker *time.Ticker
var ticker_stop chan bool
var ticker_done chan bool
var stopper func()
ticker = time.NewTicker(20 * time.Millisecond)
ticker_done = make(chan bool)
ticker_stop = make(chan bool)
go func() {
for {
select {
case <- ticker_stop:
goto done
case <- ticker.C:
x.RaiseTick()
}
}
done:
ticker.Stop()
ticker_done <- true
}()
x.RcvTick(true)
stopper = func() {
x.RcvTick(false)
ticker_stop <- true
<- ticker_done // wait for the ticker to stop
// if i don't close the the two channels below, the multiple calls to the
// returned stopper function wouldn't cause immediate panic for writing
// on a closed channel. but i would still close them as i don't want to
// cater for generic use of this function and this function wasn't
// written to be generic. i don't care to use any other more advanced
// mechanisms. the caller must ensure to call this stopper only once.
close(ticker_stop)
close(ticker_done)
}
return stopper
}
func main() {
@@ -99,6 +271,7 @@ func main() {
var x *hak.Hak = nil
var err error = nil
var param Param
var stop_ticker func()
var rfh hak.CciFileHandler
var sfh hak.UdiFileHandler
@@ -111,20 +284,39 @@ func main() {
os.Exit(1)
}
if param.show_info {
fmt.Println(hak.BuildInfo())
os.Exit(0)
}
x, err = hak.New()
if err != nil {
fmt.Printf("ERROR: failed to instantiate hak - %s\n", err.Error())
os.Exit(1)
}
if param.log_file != "" {
x.SetLogMask(^hak.BitMask(0))
x.SetLogTarget("/dev/stderr")
if param.log_target != "" {
/* honour both halves of --log. the previous code discarded the path
* and always logged everything to /dev/stderr. */
x.SetLogMask(param.log_mask)
err = x.SetLogTarget(param.log_target)
if err != nil {
fmt.Printf("ERROR: failed to set log target - %s\n", err.Error())
os.Exit(1)
}
}
if param.incdirs != "" {
x.SetIncDirs(param.incdirs)
}
if param.modlibdirs != "" {
x.SetModLibDirs(param.modlibdirs)
}
x.SetTrait(x.GetTrait() | hak.TRAIT_LANG_ENABLE_EOL)
err = x.Ignite(1000000)
err = x.Ignite(uintptr(param.heapsize))
if err != nil {
fmt.Printf("ERROR: failed to ignite - %s\n", err.Error())
goto oops
@@ -169,16 +361,25 @@ func main() {
goto oops
}
/* Decode() writes the bytecode mnemonics through the log, so it only
* produces anything when --log selects the mnemonic type. the log mask
* is no longer cleared afterwards - doing that silenced --log for the
* whole of Execute(). */
x.Decode()
x.SetLogMask(0)
stop_ticker = start_ticker(x)
err = x.Execute()
stop_ticker()
if err != nil {
//fmt.Printf("ERROR: %s[%d:%d] - %s\n", herr.File, herr.Line, herr.Colm, herr.Msg)
fmt.Printf("ERROR: %s\n", err.Error())
goto oops
}
if param.verbose {
fmt.Printf("EXECUTION OK - %s\n", param.input_file)
}
x.Close()
os.Exit(0)
Vendored
+46
View File
@@ -16590,6 +16590,45 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports \u escape sequences" >&5
printf %s "checking whether compiler supports \u escape sequences... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* define a macro that forces a compilation error if condition is false */
#define STATIC_ASSERT(expr) char assert_array[(expr) ? 1 : -1]
int
main (void)
{
/* true UTF-8 "\u00e9" is 3 bytes including the null terminator.
* GCC 2.95's "u00e9" is 6 bytes. */
STATIC_ASSERT(sizeof("\u00e9") == 3);
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_ESCAPE_U 1" >>confdefs.h
else case e in #(
e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
;;
esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __builtin_memset" >&5
printf %s "checking for __builtin_memset... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -16895,6 +16934,12 @@ if test "x$ac_cv_header_sys_devpoll_h" = xyes
then :
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
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
@@ -17151,6 +17196,7 @@ then :
fi
ac_fn_c_check_func "$LINENO" "posix_spawn" "ac_cv_func_posix_spawn"
if test "x$ac_cv_func_posix_spawn" = xyes
then :
+20 -1
View File
@@ -70,6 +70,24 @@ AC_SUBST(LIBM, $LIBM)
AX_PTHREAD()
dnl check some compiler features
AC_MSG_CHECKING([whether compiler supports \u escape sequences])
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([
/* define a macro that forces a compilation error if condition is false */
#define STATIC_ASSERT(expr) char assert_array[(expr) ? 1 : -1]
], [
/* true UTF-8 "\u00e9" is 3 bytes including the null terminator.
* GCC 2.95's "u00e9" is 6 bytes. */
STATIC_ASSERT(sizeof("\u00e9") == 3);
])], [
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_ESCAPE_U], [1], [Define to 1 if \u encodes properly])
], [
AC_MSG_RESULT([no])
]
)
dnl check some compiler builtins
AC_MSG_CHECKING([for __builtin_memset])
AC_LINK_IFELSE(
@@ -110,7 +128,7 @@ AC_PROG_EGREP
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([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([sys/ioctl.h net/if.h])
AC_CHECK_HEADERS([sys/wait.h sys/resource.h])
@@ -132,6 +150,7 @@ AC_CHECK_FUNCS([sigaction signal])
AC_CHECK_FUNCS([snprintf _vsnprintf _vsnwprintf strerror_r])
AC_CHECK_FUNCS([accept4 pipe2 epoll_create epoll_create1 kqueue kqueue1])
AC_CHECK_FUNCS([isatty mmap munmap])
AC_CHECK_FUNCS([posix_spawn getrlimit sysconf dirfd])
dnl dirfd may be provided as a macro rather than a function
AC_CHECK_DECLS([dirfd],[],[],[[#include <dirent.h>]])
+47 -13
View File
@@ -12,6 +12,7 @@ import (
"io"
"os"
"path"
"strings"
"path/filepath"
"reflect"
"sync"
@@ -96,6 +97,8 @@ func hak_go_cci_handler(c *C.hak_t, cmd C.hak_io_cmd_t, arg unsafe.Pointer) C.in
var (
ioarg *C.hak_io_cciarg_t
name string
raw string
try_incdirs bool
fd int
tptr unsafe.Pointer
tlen C.size_t
@@ -110,33 +113,64 @@ func hak_go_cci_handler(c *C.hak_t, cmd C.hak_io_cmd_t, arg unsafe.Pointer) C.in
// actual included stream
var includer_name string
name = string(ucstr_to_rune_slice(ioarg.name))
raw = string(ucstr_to_rune_slice(ioarg.name))
tptr = ioarg.includer.handle
tlen = *(*C.size_t)(unsafe.Pointer(uintptr(tptr) + unsafe.Sizeof(fd)))
includer_name = C.GoStringN((*C.char)(unsafe.Pointer(uintptr(tptr)+unsafe.Sizeof(fd)+unsafe.Sizeof(tlen))), C.int(tlen))
name = filepath.Join(path.Dir(includer_name), name)
name = filepath.Join(path.Dir(includer_name), raw)
// a name anchored by the author - absolute, or explicitly ./ or
// ../ - is meant to resolve against the includer alone. anything
// else may fall back to the include directories. this mirrors
// what open_cci_stream() does in lib/std.c.
try_incdirs = !filepath.IsAbs(raw) &&
!strings.HasPrefix(raw, "./") && !strings.HasPrefix(raw, "../")
}
// [NOTE] the open has to happen before the allocation below, because
// the include-directory search can settle on a different (and
// longer) path than the one first tried, and the block is
// sized from the name it ends up storing.
if ioarg.includer == nil {
fd = -1
} else {
fd, err = g.io.cci.Open(g, name)
if err != nil && try_incdirs {
// walk the colon-separated include directories, as the C
// reader does. an empty entry means the current directory.
for _, dir := range strings.Split(g.GetIncDirs(), ":") {
var cand string = filepath.Join(dir, raw)
var fd2 int
var err2 error
fd2, err2 = g.io.cci.Open(g, cand)
if err2 == nil {
// the stored name becomes the includer path for
// anything this file includes in turn, so it has to
// be the path that actually opened
fd, err, name = fd2, nil, cand
break
}
}
}
if err != nil {
g.set_errmsg(C.HAK_EIOERR, err.Error())
return -1
}
}
tlen = C.size_t(len(name)) // number of bytes in the string
tptr = C.hak_allocmem(c, C.size_t(unsafe.Sizeof(fd)) + C.size_t(unsafe.Sizeof(tlen)) + tlen)
if tptr == nil {
if ioarg.includer != nil {
g.io.cci.Close(fd)
}
g.set_errmsg(C.HAK_ESYSMEM, "cci name allocation failure")
return -1
}
if ioarg.includer == nil {
fd = -1
} else {
fd, err = g.io.cci.Open(g, name)
if err != nil {
g.set_errmsg(C.HAK_EIOERR, err.Error())
C.hak_freemem(c, tptr)
return -1
}
}
// | fd | length | name bytes of the length |
*(*int)(tptr) = fd;
*(*C.size_t)(unsafe.Pointer(uintptr(tptr)+unsafe.Sizeof(fd))) = tlen;
+102 -9
View File
@@ -76,6 +76,38 @@ type BitMask C.hak_bitmask_t
const TRAIT_LANG_ENABLE_EOL BitMask = C.HAK_TRAIT_LANG_ENABLE_EOL
/* log levels */
const (
LOG_DEBUG BitMask = C.HAK_LOG_DEBUG
LOG_INFO BitMask = C.HAK_LOG_INFO
LOG_WARN BitMask = C.HAK_LOG_WARN
LOG_ERROR BitMask = C.HAK_LOG_ERROR
LOG_FATAL BitMask = C.HAK_LOG_FATAL
)
/* log types */
const (
LOG_UNTYPED BitMask = C.HAK_LOG_UNTYPED
LOG_COMPILER BitMask = C.HAK_LOG_COMPILER
LOG_VM BitMask = C.HAK_LOG_VM
LOG_MNEMONIC BitMask = C.HAK_LOG_MNEMONIC
LOG_GC BitMask = C.HAK_LOG_GC
LOG_IC BitMask = C.HAK_LOG_IC
LOG_PRIMITIVE BitMask = C.HAK_LOG_PRIMITIVE
LOG_APP BitMask = C.HAK_LOG_APP
)
const (
LOG_ALL_LEVELS BitMask = C.HAK_LOG_ALL_LEVELS
LOG_ALL_TYPES BitMask = C.HAK_LOG_ALL_TYPES
)
/* BuildInfo answers how the underlying C library was configured. it mirrors
* what bin/hak.c prints for --info. */
func BuildInfo() string {
return fmt.Sprintf("Configured with: %s %s", C.HAK_CONFIGURE_CMD, C.HAK_CONFIGURE_ARGS)
}
func deregister_instance(g *Hak) {
if g.inst_no >= 0 {
inst_table.delete_instance(g.inst_no)
@@ -142,7 +174,7 @@ func (hak *Hak) GetTrait() BitMask {
var x C.int
var log_mask BitMask = 0
x = C.hak_getoption(hak.c, C.HAK_TRAIT, unsafe.Pointer(&log_mask))
x = C.hak_getoption(hak.c, C.HAK_OPT_TRAIT, unsafe.Pointer(&log_mask))
if x <= -1 {
// this must not happen
panic(fmt.Errorf("unable to get log mask - %s", hak.get_errmsg()))
@@ -154,7 +186,7 @@ func (hak *Hak) GetTrait() BitMask {
func (hak *Hak) SetTrait(log_mask BitMask) {
var x C.int
x = C.hak_setoption(hak.c, C.HAK_TRAIT, unsafe.Pointer(&log_mask))
x = C.hak_setoption(hak.c, C.HAK_OPT_TRAIT, unsafe.Pointer(&log_mask))
if x <= -1 {
// this must not happen
panic(fmt.Errorf("unable to set log mask - %s", hak.get_errmsg()))
@@ -165,7 +197,7 @@ func (hak *Hak) GetLogMask() BitMask {
var x C.int
var log_mask BitMask = 0
x = C.hak_getoption(hak.c, C.HAK_LOG_MASK, unsafe.Pointer(&log_mask))
x = C.hak_getoption(hak.c, C.HAK_OPT_LOG_MASK, unsafe.Pointer(&log_mask))
if x <= -1 {
// this must not happen
panic(fmt.Errorf("unable to get log mask - %s", hak.get_errmsg()))
@@ -177,7 +209,7 @@ func (hak *Hak) GetLogMask() BitMask {
func (hak *Hak) SetLogMask(log_mask BitMask) {
var x C.int
x = C.hak_setoption(hak.c, C.HAK_LOG_MASK, unsafe.Pointer(&log_mask))
x = C.hak_setoption(hak.c, C.HAK_OPT_LOG_MASK, unsafe.Pointer(&log_mask))
if x <= -1 {
// this must not happen
panic(fmt.Errorf("unable to set log mask - %s", hak.get_errmsg()))
@@ -188,27 +220,78 @@ func (hak *Hak) GetLogTarget() string {
var x C.int
var tgt *C.char
x = C.hak_getoption(hak.c, C.HAK_LOG_TARGET_BCSTR, unsafe.Pointer(&tgt))
x = C.hak_getoption(hak.c, C.HAK_OPT_LOG_TARGET_BCSTR, unsafe.Pointer(&tgt))
if x <= -1 {
// this must not happen
panic(fmt.Errorf("unable to set log target - %s", hak.get_errmsg()))
panic(fmt.Errorf("unable to get log target - %s", hak.get_errmsg()))
}
return C.GoString(tgt)
}
func (hak *Hak) SetLogTarget(target string) {
func (hak *Hak) SetLogTarget(target string) error {
var x C.int
var tgt *C.char
tgt = C.CString(target) // TODO: need error check?
defer C.free(unsafe.Pointer(tgt))
x = C.hak_setoption(hak.c, C.HAK_LOG_TARGET_BCSTR, unsafe.Pointer(tgt))
x = C.hak_setoption(hak.c, C.HAK_OPT_LOG_TARGET_BCSTR, unsafe.Pointer(tgt))
if x <= -1 { return hak.make_errinfo() }
return nil
}
func (hak *Hak) GetIncDirs() string {
var x C.int
var tgt *C.char
x = C.hak_getoption(hak.c, C.HAK_OPT_INCDIRS_BCSTR, unsafe.Pointer(&tgt))
if x <= -1 {
// this must not happen
panic(fmt.Errorf("unable to set log target - %s", hak.get_errmsg()))
panic(fmt.Errorf("unable to get include directories - %s", hak.get_errmsg()))
}
return C.GoString(tgt)
}
func (hak *Hak) SetIncDirs(target string) error {
var x C.int
var tgt *C.char
tgt = C.CString(target) // TODO: need error check?
defer C.free(unsafe.Pointer(tgt))
x = C.hak_setoption(hak.c, C.HAK_OPT_INCDIRS_BCSTR, unsafe.Pointer(tgt))
if x <= -1 { return hak.make_errinfo() }
return nil
}
func (hak *Hak) GetModLibDirs() string {
var x C.int
var tgt *C.char
x = C.hak_getoption(hak.c, C.HAK_OPT_MODLIBDIRS_BCSTR, unsafe.Pointer(&tgt))
if x <= -1 {
// this must not happen
panic(fmt.Errorf("unable to get module library directories - %s", hak.get_errmsg()))
}
return C.GoString(tgt)
}
func (hak *Hak) SetModLibDirs(target string) error {
var x C.int
var tgt *C.char
tgt = C.CString(target) // TODO: need error check?
defer C.free(unsafe.Pointer(tgt))
x = C.hak_setoption(hak.c, C.HAK_OPT_MODLIBDIRS_BCSTR, unsafe.Pointer(tgt))
if x <= -1 { return hak.make_errinfo() }
return nil
}
func (hak *Hak) Ignite(memsize uintptr) error {
@@ -399,6 +482,16 @@ func (hak *Hak) Decode() error {
return nil
}
func (hak *Hak) RcvTick(enabled bool) {
var i C.int
if enabled { i = 1 } else { i = 0 }
C.hak_rcvtick(hak.c, i)
}
func (hak *Hak) RaiseTick() {
C.hak_raisetick(hak.c)
}
func (hak *Hak) get_errmsg() string {
return C.GoString(C.hak_geterrbmsg(hak.c))
}
+77 -63
View File
@@ -1134,6 +1134,7 @@ static int push_ctlblk (hak_t* hak, const hak_loc_t* errloc, hak_ctlblk_type_t t
HAK_MEMSET(&hak->c->ctlblk.info[new_depth], 0, HAK_SIZEOF(hak->c->ctlblk.info[new_depth]));
hak->c->ctlblk.info[new_depth]._type = type;
/*hak->c->ctlblk.info[new_depth].in_catch = 0; not needed for memset above */
hak->c->ctlblk.depth = new_depth;
return 0;
}
@@ -2090,12 +2091,60 @@ static HAK_INLINE int emit_plus (hak_t* hak)
#endif
/* ========================================================================= */
static int emit_ctlblk_unwind (hak_t* hak, hak_cnode_t* src, int stop_at_loop)
{
hak_ooi_t i;
for (i = hak->c->ctlblk.depth; i > hak->c->funblk.info[hak->c->funblk.depth].ctlblk_base; --i)
{
switch (hak->c->ctlblk.info[i]._type)
{
case HAK_CTLBLK_TYPE_LOOP:
/* a loop block needs no unwinding instruction */
if (stop_at_loop) return 1; /* for break/continue inside loop */
break;
case HAK_CTLBLK_TYPE_TRY:
/* emit an instruction to exit from the try loop. when it is in the catch side. don't emit anything */
if (!hak->c->ctlblk.info[i].in_catch &&
emit_byte_instruction(hak, HAK_CODE_TRY_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
break;
case HAK_CTLBLK_TYPE_CLASS:
/* emit an instruction to exit from the class definition scope being defined */
if (emit_byte_instruction(hak, HAK_CODE_CLASS_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
break;
}
}
return 0;
}
static void update_try_ctlblk_for_catch (hak_t* hak)
{
hak_ooi_t i;
/* find the innermost try block info and mark that it's now in the catch block side */
for (i = hak->c->ctlblk.depth; i > hak->c->funblk.info[hak->c->funblk.depth].ctlblk_base; --i)
{
if (hak->c->ctlblk.info[i]._type == HAK_CTLBLK_TYPE_TRY)
{
hak->c->ctlblk.info[i].in_catch = 1;
return;
}
}
/* this must not happend. compile_catch() must not be invoked without
* going through the enclosing TRY block first. */
HAK_ASSERT(hak, !"internal error - this must never happen");
}
static int compile_break (hak_t* hak, hak_cnode_t* src)
{
/* (break) */
hak_cnode_t* cmd, * obj;
hak_ooi_t i;
int n;
HAK_ASSERT(hak, HAK_CNODE_IS_CONS(src));
HAK_ASSERT(hak, HAK_CNODE_IS_TYPED(HAK_CNODE_CONS_CAR(src), HAK_CNODE_BREAK));
@@ -2123,30 +2172,15 @@ static int compile_break (hak_t* hak, hak_cnode_t* src)
return -1;
}
for (i = hak->c->ctlblk.depth; i > hak->c->funblk.info[hak->c->funblk.depth].ctlblk_base; --i)
n = emit_ctlblk_unwind(hak, cmd, 1);
if (n <= -1) return -1;
if (n == 0)
{
switch (hak->c->ctlblk.info[i]._type)
{
case HAK_CTLBLK_TYPE_LOOP:
goto inside_loop;
case HAK_CTLBLK_TYPE_TRY:
/* emit an instruction to exit from the try loop. */
if (emit_byte_instruction(hak, HAK_CODE_TRY_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
break;
case HAK_CTLBLK_TYPE_CLASS:
/* emit an instruction to exit from the class definition scope being defined */
if (emit_byte_instruction(hak, HAK_CODE_CLASS_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
break;
}
hak_setsynerrbfmt(hak, HAK_SYNERR_BREAK, HAK_CNODE_GET_LOC(cmd),
"%.*js outside loop", HAK_CNODE_GET_TOKLEN(cmd), HAK_CNODE_GET_TOKPTR(cmd));
return -1;
}
hak_setsynerrbfmt(hak, HAK_SYNERR_BREAK, HAK_CNODE_GET_LOC(src),
"%.*js outside loop", HAK_CNODE_GET_TOKLEN(cmd), HAK_CNODE_GET_TOKPTR(cmd));
return -1;
inside_loop:
for (i = hak->c->cfs.top; i >= 0; --i)
{
const hak_cframe_t* tcf;
@@ -2217,6 +2251,7 @@ static int compile_continue (hak_t* hak, hak_cnode_t* src)
/* (continue) */
hak_cnode_t* cmd, * obj;
hak_ooi_t i;
int n;
HAK_ASSERT(hak, HAK_CNODE_IS_CONS(src));
HAK_ASSERT(hak, HAK_CNODE_IS_TYPED(HAK_CNODE_CONS_CAR(src), HAK_CNODE_CONTINUE));
@@ -2244,29 +2279,15 @@ static int compile_continue (hak_t* hak, hak_cnode_t* src)
return -1;
}
for (i = hak->c->ctlblk.depth; i > hak->c->funblk.info[hak->c->funblk.depth].ctlblk_base; --i)
n = emit_ctlblk_unwind(hak, cmd, 1);
if (n <= -1) return -1;
if (n == 0)
{
switch (hak->c->ctlblk.info[i]._type)
{
case HAK_CTLBLK_TYPE_LOOP:
goto inside_loop;
case HAK_CTLBLK_TYPE_TRY:
/*must emit an instruction to exit from the try loop.*/
if (emit_byte_instruction(hak, HAK_CODE_TRY_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
break;
case HAK_CTLBLK_TYPE_CLASS:
if (emit_byte_instruction(hak, HAK_CODE_CLASS_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
break;
}
hak_setsynerrbfmt(hak, HAK_SYNERR_BREAK, HAK_CNODE_GET_LOC(cmd),
"%.*js outside loop", HAK_CNODE_GET_TOKLEN(cmd), HAK_CNODE_GET_TOKPTR(cmd));
return -1;
}
hak_setsynerrbfmt(hak, HAK_SYNERR_BREAK, HAK_CNODE_GET_LOC(src),
"%.*js outside loop", HAK_CNODE_GET_TOKLEN(cmd), HAK_CNODE_GET_TOKPTR(cmd));
return -1;
inside_loop:
for (i = hak->c->cfs.top; i >= 0; --i)
{
const hak_cframe_t* tcf;
@@ -3968,7 +3989,6 @@ static int compile_return (hak_t* hak, hak_cnode_t* src, int ret_from_home)
hak_cnode_t* obj, * val;
hak_cframe_t* cf;
hak_funblk_info_t* fbi;
hak_ooi_t i;
HAK_ASSERT(hak, HAK_CNODE_IS_CONS(src));
HAK_ASSERT(hak, HAK_CNODE_IS_TYPED(HAK_CNODE_CONS_CAR(src), HAK_CNODE_RETURN) ||
@@ -3977,24 +3997,6 @@ static int compile_return (hak_t* hak, hak_cnode_t* src, int ret_from_home)
fbi = &hak->c->funblk.info[hak->c->funblk.depth];
obj = HAK_CNODE_CONS_CDR(src);
for (i = hak->c->ctlblk.depth; i > hak->c->funblk.info[hak->c->funblk.depth].ctlblk_base; --i)
{
switch (hak->c->ctlblk.info[i]._type)
{
case HAK_CTLBLK_TYPE_LOOP:
/* do nothing */
break;
case HAK_CTLBLK_TYPE_TRY:
if (emit_byte_instruction(hak, HAK_CODE_TRY_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
break;
case HAK_CTLBLK_TYPE_CLASS:
if (emit_byte_instruction(hak, HAK_CODE_CLASS_EXIT, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
break;
}
}
if (fbi->tmpr_nrvars > 0)
{
hak_cnode_t* tmp = HAK_CNODE_CONS_CAR(src);
@@ -4016,6 +4018,9 @@ static int compile_return (hak_t* hak, hak_cnode_t* src, int ret_from_home)
return -1;
}
/* emit unwinding expression just before emitting the actual return instruction */
if (emit_ctlblk_unwind(hak, src, 0) <= -1) return -1;
/* TODO: pop stack if this is not the first statement... */
if (emit_byte_instruction(hak, HAK_CODE_PUSH_RETURN_R, HAK_CNODE_GET_LOC(tmp)) <= -1) return -1;
POP_CFRAME(hak);
@@ -4502,6 +4507,7 @@ static HAK_INLINE int compile_catch (hak_t* hak)
/* produce an instruction to store the exception value to an exception variable pushed by the 'throw' instruction */
if (emit_variable_access(hak, VAR_ACCESS_POP, &vi, HAK_CNODE_GET_LOC(src)) <= -1) return -1;
update_try_ctlblk_for_catch(hak);
SWITCH_TOP_CFRAME(hak, COP_COMPILE_OBJECT_LIST, obj);
PUSH_SUBCFRAME(hak, COP_POST_CATCH, cmd);
@@ -5450,12 +5456,17 @@ static HAK_INLINE int compile_dsymbol (hak_t* hak, hak_cnode_t* obj)
val = hak->_nil;
break;
case HAK_PFBASE_CONST:
case HAK_PFBASE_CONST_SMOOI:
{
/* 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? */
/* 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;
val = hak->_nil;
val = HAK_SMOOI_TO_OOP(v);
break;
}
default:
hak_popvolat(hak);
@@ -5472,7 +5483,7 @@ static HAK_INLINE int compile_dsymbol (hak_t* hak, hak_cnode_t* obj)
/* make this dotted symbol special that it can't get changed
* to a different value */
HAK_OBJ_SET_FLAGS_KERNEL (sym, kernel_bits);
HAK_OBJ_SET_FLAGS_KERNEL(sym, kernel_bits);
}
if (add_literal(hak, cons, &index) <= -1 ||
@@ -6996,6 +7007,9 @@ static HAK_INLINE int emit_return (hak_t* hak)
HAK_ASSERT(hak, cf->opcode == COP_EMIT_RETURN);
HAK_ASSERT(hak, cf->operand != HAK_NULL);
/* emit unwinding expression just before emitting the actual return instruction */
if (emit_ctlblk_unwind(hak, cf->operand, 0) <= -1) return -1;
n = emit_byte_instruction(hak, (cf->u._return.from_home? HAK_CODE_RETURN_STACKTOP: HAK_CODE_RETURN_FROM_BLOCK), HAK_CNODE_GET_LOC(cf->operand));
POP_CFRAME(hak);
+148 -19
View File
@@ -180,6 +180,8 @@ static void terminate_all_processes (hak_t* hak);
ap->exsp = HAK_SMOOI_TO_OOP(exsp); \
} while (0)
/* normal stack top is the base of exstack */
#define HAK_EXSTACK_GET_BASE(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->st)
#define HAK_EXSTACK_GET_ST(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->exst)
#define HAK_EXSTACK_GET_SP(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->exsp)
@@ -235,6 +237,8 @@ static void terminate_all_processes (hak_t* hak);
#define HAK_CLSTACK_CHOP(hak, clsp_) ((hak)->processor->active->clsp = HAK_SMOOI_TO_OOP(clsp_))
/* exstack top is the base of clstack */
#define HAK_CLSTACK_GET_BASE(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->exst)
#define HAK_CLSTACK_GET_ST(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->clst)
#define HAK_CLSTACK_GET_SP(hak) HAK_OOP_TO_SMOOI(((hak)->processor->active)->clsp)
@@ -715,23 +719,23 @@ static hak_oop_process_t make_process (hak_t* hak, hak_oop_context_t c)
if (hak->proc_map_free_first <= -1 && prepare_to_alloc_pid(hak) <= -1) return HAK_NULL;
stksize = hak->option.dfl_procstk_size; /* stack */
exstksize = 128; /* exception stack size */ /* TODO: make it configurable */
clstksize = 64; /* class stack size */ /* TODO: make it configurable too */
exstksize = hak->option.dfl_exstk_size; /* exception stack size */
clstksize = hak->option.dfl_clstk_size; /* class stack size */
fstksize = stksize; /* frame stack size */
maxsize = (HAK_TYPE_MAX(hak_ooi_t) - HAK_PROCESS_NAMED_INSTVARS) / 4;
if (stksize > maxsize) stksize = maxsize;
else if (stksize < 192) stksize = 192;
else if (stksize < HAK_MIN_PROCSTK_SIZE) stksize = HAK_MIN_PROCSTK_SIZE;
if (exstksize > maxsize) exstksize = maxsize;
else if (exstksize < 128) exstksize = 128;
else if (exstksize < HAK_MIN_EXSTK_SIZE) exstksize = HAK_MIN_EXSTK_SIZE;
if (clstksize > maxsize) clstksize = maxsize;
else if (clstksize < 32) clstksize = 32;
else if (clstksize < HAK_MIN_CLSTK_SIZE) clstksize = HAK_MIN_CLSTK_SIZE;
if (fstksize > maxsize) fstksize = maxsize;
else if (fstksize < 1024) fstksize = 1024;
else if (fstksize < HAK_MIN_FSTK_SIZE) fstksize = HAK_MIN_FSTK_SIZE;
hak_pushvolat(hak, (hak_oop_t*)&c);
proc = (hak_oop_process_t)hak_instantiate(hak, hak->c_process, HAK_NULL, stksize + exstksize + clstksize + fstksize);
@@ -831,7 +835,7 @@ static void switch_to_process (hak_t* hak, hak_oop_process_t proc, int new_state
/* the new process must be in the runnable state */
HAK_ASSERT(hak, proc->state == HAK_SMOOI_TO_OOP(HAK_PROCESS_STATE_RUNNABLE) ||
proc->state == HAK_SMOOI_TO_OOP(HAK_PROCESS_STATE_WAITING));
proc->state == HAK_SMOOI_TO_OOP(HAK_PROCESS_STATE_WAITING));
sleep_active_process(hak, new_state_for_old_active);
wake_process(hak, proc);
@@ -1337,7 +1341,6 @@ static void yield_process (hak_t* hak, hak_oop_process_t proc)
}
}
static int async_signal_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
{
#if 0
@@ -1470,6 +1473,29 @@ static HAK_INLINE int can_await_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
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)
{
hak_oop_process_t proc;
@@ -1487,10 +1513,10 @@ static HAK_INLINE void await_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
/* it's already signaled */
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)
{
int sems_idx;
/* TODO: if i disallow individual wait on a semaphore in a group,
* this membership manipulation is redundant */
@@ -1552,6 +1578,7 @@ static HAK_INLINE hak_oop_t await_semaphore_group (hak_t* hak, hak_oop_semaphore
HAK_ASSERT(hak, count > 0);
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);
sems_idx = count > 0? HAK_SEMAPHORE_GROUP_SEMS_SIG: HAK_SEMAPHORE_GROUP_SEMS_UNSIG;
@@ -1778,6 +1805,7 @@ int hak_add_sem_to_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, hak_ooi_t
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;
hak->sem_io_map = tmp;
@@ -1805,6 +1833,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);
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_capa = new_capa;
}
@@ -1822,6 +1853,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].handle = io_handle;
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);
@@ -1861,6 +1894,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].mask = new_mask;
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++;
if (tuple_added)
@@ -1883,7 +1917,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)
{
hak_ooi_t index;
hak_ooi_t index, unawaited;
hak_ooi_t new_mask, io_handle, io_type;
int x;
@@ -1945,6 +1979,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);
}
/* ----------------------------------------------------------------------
* [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)
{
hak->sem_io_tuple[index].mask = new_mask;
@@ -1975,7 +2036,7 @@ static int delete_sem_from_sem_io_tuple (hak_t* hak, hak_oop_semaphore_t sem, in
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;
@@ -1998,6 +2059,8 @@ static void _signal_io_semaphore (hak_t* hak, hak_oop_semaphore_t sem)
switch_to_process_from_nil(hak, proc);
#endif
}
return proc;
}
static void signal_io_semaphore (hak_t* hak, hak_ooi_t io_handle, hak_ooi_t mask)
@@ -2016,7 +2079,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)) ||
(!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 +2092,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))
{
_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]++;
}
}
}
}
@@ -3253,13 +3326,21 @@ void hak_rcvtick (hak_t* hak, int enabled)
void hak_raisetick (hak_t* hak)
{
#if defined(HAK_ATOMIC_ADD_FETCH)
HAK_ATOMIC_ADD_FETCH(&hak->tick, 1, HAK_ATOMIC_RELAXED);
#else
hak->tick++;
#endif
}
void hak_raise_gtick (int unused)
{
/* this function is global and not bound to a specific instance. */
#if defined(HAK_ATOMIC_ADD_FETCH)
HAK_ATOMIC_ADD_FETCH(&gtick, 1, HAK_ATOMIC_RELAXED);
#else
gtick++;
#endif
}
/* ------------------------------------------------------------------------- */
@@ -4286,7 +4367,15 @@ static int execute (hak_t* hak)
case HAK_CODE_TRY_EXIT:
LOG_INST_0(hak, "try_exit");
/* TODO: stack underflow check? */
/* writing the condition this way would be more explicit.
* if (HAK_EXSTACK_GET_SP(hak) - HAK_EXSTACK_GET_BASE(hak) < 4)
* it's simpler to use HAK_EXSTACK_IS_EMPTY() base PUSH, POP, POP_TO all move
* exsp by exactly 4. */
if (HAK_EXSTACK_IS_EMPTY(hak))
{
hak_seterrbfmt(hak, HAK_ESTKUNDFLW, "exception stack underflow");
goto oops_with_errmsg_supplement;
}
HAK_EXSTACK_POP(hak);
break;
@@ -5550,8 +5639,13 @@ hak_pfrc_t hak_pf_process_resume (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
return HAK_PF_FAILURE;
}
/* [SPECIAL CASE]
* resume_process changes the the active process.
* calling this after resume_process() pollutes a wrong stack. place it here */
HAK_STACK_SETRET(hak, nargs, prc);
resume_process(hak, prc);
return HAK_PF_SUCCESS;
return HAK_PF_SUCCESS;
}
hak_pfrc_t hak_pf_process_suspend (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
@@ -5572,8 +5666,13 @@ hak_pfrc_t hak_pf_process_suspend (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
prc = hak->processor->active;
}
/* [SPECIAL CASE]
* suspend_process changes the the active process.
* calling this after suspend_process() pollutes a wrong stack. place it here */
HAK_STACK_SETRET(hak, nargs, prc);
suspend_process(hak, prc);
return HAK_PF_SUCCESS;
return HAK_PF_SUCCESS;
}
hak_pfrc_t hak_pf_process_terminate (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
@@ -5594,20 +5693,35 @@ hak_pfrc_t hak_pf_process_terminate (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs
prc = hak->processor->active;
}
/* [SPECIAL CASE]
* terminate_process changes the the active process.
* calling this after terminate_process() pollutes a wrong stack. place it here */
HAK_STACK_SETRET(hak, nargs, prc);
terminate_process(hak, prc);
return HAK_PF_SUCCESS;
}
hak_pfrc_t hak_pf_process_terminate_all (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
{
/* [SPECIAL CASE]
* terminate_all_processes changes the the active process.
* calling this after terminate_all_processes() pollutes a wrong stack. place it here */
HAK_STACK_SETRET(hak, nargs, hak->_nil);
terminate_all_processes(hak);
return HAK_PF_SUCCESS;
return HAK_PF_SUCCESS;
}
hak_pfrc_t hak_pf_process_yield (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
{
/* [SPECIAL CASE]
* yield_process changes the the active process.
* calling this after yield_process() pollutes a wrong stack. place it here */
HAK_STACK_SETRET(hak, nargs, hak->_nil);
yield_process(hak, hak->processor->active);
return HAK_PF_SUCCESS;
return HAK_PF_SUCCESS;
}
/* ------------------------------------------------------------------ */
@@ -5625,7 +5739,22 @@ hak_pfrc_t hak_pf_semaphore_new (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
return HAK_PF_FAILURE;
}
sem->count = HAK_SMOOI_TO_OOP(0);
if (nargs >= 1)
{
hak_oop_t tmp;
tmp = (hak_oop_semaphore_t)HAK_STACK_GETARG(hak, nargs, 0);
if (!HAK_OOP_IS_SMOOI(tmp))
{
hak_seterrbfmt(hak, HAK_EINVAL, "invalid semaphore count - %O", tmp);
return HAK_PF_FAILURE;
}
sem->count = tmp;
}
else
{
sem->count = HAK_SMOOI_TO_OOP(0);
}
/* TODO: sem->signal_action? */
/* other fields are all set to nil */
+6
View File
@@ -226,6 +226,9 @@
/* Define to 1 if you have the <errno.h> header file. */
#undef HAVE_ERRNO_H
/* Define to 1 if \u encodes properly */
#undef HAVE_ESCAPE_U
/* Define to 1 if you have the <execinfo.h> header file. */
#undef HAVE_EXECINFO_H
@@ -403,6 +406,9 @@
/* Define to 1 if you have the <sys/epoll.h> header file. */
#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. */
#undef HAVE_SYS_IOCTL_H
+186 -2
View File
@@ -1084,6 +1084,52 @@ typedef struct hak_t hak_t;
#define HAK_HAVE_BUILTIN_CLZLL
#endif
#if __has_builtin(__atomic_compare_exchange_n)
#define HAK_HAVE_ATOMIC_COMPARE_EXCHANGE_N
#endif
#if __has_builtin(__atomic_exchange_n)
#define HAK_HAVE_ATOMIC_EXCHANGE_N
#endif
#if __has_builtin(__atomic_fetch_add)
#define HAK_HAVE_ATOMIC_FETCH_ADD
#endif
#if __has_builtin(__atomic_add_fetch)
#define HAK_HAVE_ATOMIC_ADD_FETCH
#endif
#if __has_builtin(__atomic_fetch_and)
#define HAK_HAVE_ATOMIC_FETCH_AND
#endif
#if __has_builtin(__atomic_and_fetch)
#define HAK_HAVE_ATOMIC_AND_FETCH
#endif
#if __has_builtin(__atomic_fetch_or)
#define HAK_HAVE_ATOMIC_FETCH_OR
#endif
#if __has_builtin(__atomic_or_fetch)
#define HAK_HAVE_ATOMIC_OR_FETCH
#endif
#if __has_builtin(__atomic_fetch_sub)
#define HAK_HAVE_ATOMIC_FETCH_SUB
#endif
#if __has_builtin(__atomic_sub_fetch)
#define HAK_HAVE_ATOMIC_SUB_FETCH
#endif
#if __has_builtin(__atomic_fetch_xor)
#define HAK_HAVE_ATOMIC_FETCH_XOR
#endif
#if __has_builtin(__atomic_xor_fetch)
#define HAK_HAVE_ATOMIC_XOR_FETCH
#endif
#if __has_builtin(__atomic_load_n)
#define HAK_HAVE_ATOMIC_LOAD_N
#endif
#if __has_builtin(__atomic_store_n)
#define HAK_HAVE_ATOMIC_STORE_N
#endif
#if __has_builtin(__builtin_uadd_overflow)
#define HAK_HAVE_BUILTIN_UADD_OVERFLOW
#endif
@@ -1158,11 +1204,19 @@ typedef struct hak_t hak_t;
#endif
#elif defined(__GNUC__) && defined(__GNUC_MINOR__)
#if (__GNUC__ >= 4)
#define HAK_HAVE_SYNC_FETCH_AND_ADD
#define HAK_HAVE_SYNC_ADD_AND_FETCH
#define HAK_HAVE_SYNC_FETCH_AND_AND
#define HAK_HAVE_SYNC_AND_AND_FETCH
#define HAK_HAVE_SYNC_FETCH_AND_OR
#define HAK_HAVE_SYNC_OR_AND_FETCH
#define HAK_HAVE_SYNC_FETCH_AND_SUB
#define HAK_HAVE_SYNC_SUB_AND_FETCH
#define HAK_HAVE_SYNC_FETCH_AND_XOR
#define HAK_HAVE_SYNC_XOR_AND_FETCH
#define HAK_HAVE_SYNC_LOCK_TEST_AND_SET
#define HAK_HAVE_SYNC_LOCK_RELEASE
#define HAK_HAVE_SYNC_SYNCHRONIZE
#define HAK_HAVE_SYNC_BOOL_COMPARE_AND_SWAP
#define HAK_HAVE_SYNC_VAL_COMPARE_AND_SWAP
@@ -1194,6 +1248,19 @@ typedef struct hak_t hak_t;
#define HAK_HAVE_BUILTIN_SMULLL_OVERFLOW
#endif
#if (__GNUC__ >= 5) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
#define HAK_HAVE_ATOMIC_FETCH_ADD
#define HAK_HAVE_ATOMIC_ADD_FETCH
#define HAK_HAVE_ATOMIC_FETCH_AND
#define HAK_HAVE_ATOMIC_AND_FETCH
#define HAK_HAVE_ATOMIC_FETCH_OR
#define HAK_HAVE_ATOMIC_OR_FETCH
#define HAK_HAVE_ATOMIC_FETCH_SUB
#define HAK_HAVE_ATOMIC_SUB_FETCH
#define HAK_HAVE_ATOMIC_FETCH_XOR
#define HAK_HAVE_ATOMIC_XOR_FETCH
#endif
#if (__GNUC__ >= 5) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
/* 4.8.0 or later */
#define HAK_HAVE_BUILTIN_BSWAP16
@@ -1207,6 +1274,12 @@ typedef struct hak_t hak_t;
#endif
#if defined(__has_builtin)
# define HAK_HAS_BUILTIN(v) __has_builtin(v)
#else
# define HAK_HAS_BUILTIN(v) 0
#endif
#if defined(HAK_HAVE_BUILTIN_EXPECT)
# define HAK_LIKELY(x) (__builtin_expect(!!(x),1))
# define HAK_UNLIKELY(x) (__builtin_expect(!!(x),0))
@@ -1215,6 +1288,117 @@ typedef struct hak_t hak_t;
# define HAK_UNLIKELY(x) (x)
#endif
#if defined(HAK_HAVE_ATOMIC_EXCHANGE_N)
# define HAK_ATOMIC_EXCHANGE(ptr,val,mo) __atomic_exchange_n((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_LOCK_TEST_AND_SET)
# define HAK_ATOMIC_EXCHANGE(ptr,val,mo) __sync_lock_test_and_set((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_FETCH_ADD)
# define HAK_ATOMIC_FETCH_ADD(ptr,val,mo) __atomic_fetch_add((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_FETCH_AND_ADD)
# define HAK_ATOMIC_FETCH_ADD(ptr,val,mo) __sync_fetch_and_add((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_ADD_FETCH)
# define HAK_ATOMIC_ADD_FETCH(ptr,val,mo) __atomic_add_fetch((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_ADD_AND_FETCH)
# define HAK_ATOMIC_ADD_FETCH(ptr,val,mo) __sync_add_and_fetch((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_FETCH_AND)
# define HAK_ATOMIC_FETCH_AND(ptr,val,mo) __atomic_fetch_and((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_FETCH_AND_AND)
# define HAK_ATOMIC_FETCH_AND(ptr,val,mo) __sync_fetch_and_and((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_AND_FETCH)
# define HAK_ATOMIC_AND_FETCH(ptr,val,mo) __atomic_and_fetch((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_AND_AND_FETCH)
# define HAK_ATOMIC_AND_FETCH(ptr,val,mo) __sync_and_and_fetch((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_FETCH_OR)
# define HAK_ATOMIC_FETCH_OR(ptr,val,mo) __atomic_fetch_or((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_FETCH_AND_OR)
# define HAK_ATOMIC_FETCH_OR(ptr,val,mo) __sync_fetch_and_or((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_OR_FETCH)
# define HAK_ATOMIC_OR_FETCH(ptr,val,mo) __atomic_or_fetch((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_OR_AND_FETCH)
# define HAK_ATOMIC_OR_FETCH(ptr,val,mo) __sync_or_and_fetch((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_FETCH_SUB)
# define HAK_ATOMIC_FETCH_SUB(ptr,val,mo) __atomic_fetch_sub((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_FETCH_AND_SUB)
# define HAK_ATOMIC_FETCH_SUB(ptr,val,mo) __sync_fetch_and_sub((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_SUB_FETCH)
# define HAK_ATOMIC_SUB_FETCH(ptr,val,mo) __atomic_sub_fetch((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_SUB_AND_FETCH)
# define HAK_ATOMIC_SUB_FETCH(ptr,val,mo) __sync_sub_and_fetch((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_FETCH_XOR)
# define HAK_ATOMIC_FETCH_XOR(ptr,val,mo) __atomic_fetch_xor((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_FETCH_AND_XOR)
# define HAK_ATOMIC_FETCH_XOR(ptr,val,mo) __sync_fetch_and_xor((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_XOR_FETCH)
# define HAK_ATOMIC_XOR_FETCH(ptr,val,mo) __atomic_xor_fetch((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_XOR_AND_FETCH)
# define HAK_ATOMIC_XOR_FETCH(ptr,val,mo) __sync_xor_and_fetch((ptr),(val))
#endif
#if defined(HAK_HAVE_ATOMIC_LOAD_N)
# define HAK_ATOMIC_LOAD(ptr,mo) __atomic_load_n((ptr),(mo))
#elif defined(HAK_HAVE_SYNC_FETCH_AND_OR)
# define HAK_ATOMIC_LOAD(ptr,mo) __sync_fetch_and_or((ptr),0)
#endif
#if defined(HAK_HAVE_ATOMIC_STORE_N)
# define HAK_ATOMIC_STORE(ptr,val,mo) __atomic_store_n((ptr),(val),(mo))
#elif defined(HAK_HAVE_SYNC_LOCK_TEST_AND_SET) && defined(HAK_HAVE_SYNC_SYNCHRONIZE)
# define HAK_ATOMIC_STORE(ptr,val,mo) do { __sync_lock_test_and_set((ptr),(val)); __sync_synchronize(); } while(0)
#endif
#if defined(HAK_HAVE_ATOMIC_COMPARE_EXCHANGE_N)
# define HAK_ATOMIC_CAS_BOOL(ptr, expected_ptr, desired, memmod_succ, memmod_fail) \
__atomic_compare_exchange_n((ptr), (expected_ptr), (desired), 0, (memmod_succ), (memmod_fail))
# define HAK_ATOMIC_CAS_BOOL_YIELD_OLDVAL
#elif defined(HAK_HAVE_SYNC_BOOL_COMPARE_AND_SWAP)
# define HAK_ATOMIC_CAS_BOOL(ptr, expected_ptr, desired, memmod_succ, memmod_fail) \
__sync_bool_compare_and_swap((ptr), *(expected_ptr), (desired))
#endif
#if defined(__ATOMIC_RELAXED)
# define HAK_ATOMIC_RELAXED __ATOMIC_RELAXED
#else
# define HAK_ATOMIC_RELAXED 0
#endif
#if defined(__ATOMIC_ACQUIRE)
# define HAK_ATOMIC_ACQUIRE __ATOMIC_ACQUIRE
#else
# define HAK_ATOMIC_ACQUIRE 2
#endif
#if defined(__ATOMIC_RELEASE)
# define HAK_ATOMIC_RELEASE __ATOMIC_RELEASE
#else
# define HAK_ATOMIC_RELEASE 3
#endif
#if defined(__ATOMIC_ACQ_REL)
# define HAK_ATOMIC_ACQ_REL __ATOMIC_ACQ_REL
#else
# define HAK_ATOMIC_ACQ_REL 4
#endif
/* =========================================================================
* STATIC ASSERTION
* =========================================================================*/
-2
View File
@@ -157,8 +157,6 @@ struct hak_hnd_t
hak_hnd_t* next;
};
typedef struct hak_hndtab_t hak_hndtab_t;
/* ========================================================================= */
/* THE UNIFORM I/O CONTRACT */
/* ========================================================================= */
+12
View File
@@ -855,6 +855,7 @@ typedef enum hak_ctlblk_type_t hak_ctlblk_type_t;
struct hak_ctlblk_info_t
{
hak_ctlblk_type_t _type;
int in_catch; /* used for HAK_CTLBLK_TYPE_TRY only */
};
typedef struct hak_ctlblk_info_t hak_ctlblk_info_t;
@@ -2247,6 +2248,8 @@ hak_pfrc_t hak_pf_nqv (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_nql (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_nqk (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_object_new (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_process_current (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_process_fork (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_process_resume (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
@@ -2269,6 +2272,15 @@ hak_pfrc_t hak_pf_semaphore_group_add_semaphore (hak_t* hak, hak_mod_t* mod, hak
hak_pfrc_t hak_pf_semaphore_group_remove_semaphore (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_semaphore_group_wait (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
/* the signal primitives live in prim.c but are registered by the sys module,
* which reaches them as sys.sig-getfd, sys.sig-get, sys.sig-set, sys.sig-catch
* and sys.sig-uncatch */
hak_pfrc_t hak_pf_system_get_sigfd (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_system_get_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_system_set_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_system_catch_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
hak_pfrc_t hak_pf_system_uncatch_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs);
/* ========================================================================= */
/* std.c */
/* ========================================================================= */
+177 -35
View File
@@ -150,6 +150,8 @@ int hak_init (hak_t* hak, hak_mmgr_t* mmgr, const hak_vmprim_t* vmprim)
hak->option.dfl_symtab_size = HAK_DFL_SYMTAB_SIZE;
hak->option.dfl_sysdic_size = HAK_DFL_SYSDIC_SIZE;
hak->option.dfl_procstk_size = HAK_DFL_PROCSTK_SIZE;
hak->option.dfl_exstk_size = HAK_DFL_EXSTK_SIZE;
hak->option.dfl_clstk_size = HAK_DFL_CLSTK_SIZE;
#if defined(HAK_BUILD_DEBUG)
hak->option.karatsuba_cutoff = HAK_KARATSUBA_CUTOFF; /* this won't be used when NDEBUG is set */
#endif
@@ -237,14 +239,18 @@ void hak_fini (hak_t* hak)
hak_cb_t* cb;
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_fini(&hak->modtab);
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)
{
/* flush pending log messages just in case. */
@@ -363,9 +369,39 @@ void hak_fini (hak_t* hak)
hak->option.log_target_b = HAK_NULL;
}
/* destroy dynamically allocated options */
for (i = 0; i < HAK_COUNTOF(hak->option.mod); i++)
{
if (hak->option.mod[i].ptr) hak_freemem(hak, hak->option.mod[i].ptr);
if (hak->option.mod[i].ptr)
{
hak_freemem(hak, hak->option.mod[i].ptr);
hak->option.mod[i].ptr = HAK_NULL;
hak->option.mod[i].len = 0;
}
}
if (hak->option.modlibdirs_b)
{
hak_freemem(hak, hak->option.modlibdirs_b);
hak->option.modlibdirs_b = HAK_NULL;
}
if (hak->option.modlibdirs_u)
{
hak_freemem(hak, hak->option.modlibdirs_u);
hak->option.modlibdirs_u = HAK_NULL;
}
if (hak->option.incdirs_b)
{
hak_freemem(hak, hak->option.incdirs_b);
hak->option.incdirs_b = HAK_NULL;
}
if (hak->option.incdirs_u)
{
hak_freemem(hak, hak->option.incdirs_u);
hak->option.incdirs_u = HAK_NULL;
}
if (hak->inttostr.xbuf.ptr)
@@ -443,28 +479,74 @@ static int dup_str_opt (hak_t* hak, const hak_ooch_t* value, hak_oocs_t* tmp)
return 0;
}
/* Store a string option in both representations at once.
*
* The consumers of these options are byte oriented - dlopen() and fopen() -
* so the bch form is what gets used, and converting once here saves a
* conversion on every module load and every include attempt. The uch form is
* kept so getoption can answer in either encoding without allocating.
*
* Both conversions are done before either slot is replaced, so a failure
* leaves the previous value in place rather than half-updating it. */
static int set_dual_str_opt (hak_t* hak, const void* value, int value_is_bch, hak_bch_t** bp, hak_uch_t** up)
{
hak_bch_t* v_b;
hak_uch_t* v_u;
if (value_is_bch)
{
v_b = hak_dupbcstr(hak, (const hak_bch_t*)value, HAK_NULL);
if (HAK_UNLIKELY(!v_b)) return -1;
v_u = hak_dupbtoucstr(hak, (const hak_bch_t*)value, HAK_NULL);
if (HAK_UNLIKELY(!v_u))
{
hak_freemem(hak, v_b);
return -1;
}
}
else
{
v_u = hak_dupucstr(hak, (const hak_uch_t*)value, HAK_NULL);
if (HAK_UNLIKELY(!v_u)) return -1;
v_b = hak_duputobcstr(hak, (const hak_uch_t*)value, HAK_NULL);
if (HAK_UNLIKELY(!v_b))
{
hak_freemem(hak, v_u);
return -1;
}
}
if (*bp) hak_freemem(hak, *bp);
if (*up) hak_freemem(hak, *up);
*bp = v_b;
*up = v_u;
return 0;
}
int hak_setoption (hak_t* hak, hak_option_t id, const void* value)
{
hak_cb_t* cb;
switch (id)
{
case HAK_TRAIT:
case HAK_OPT_TRAIT:
hak->option.trait = *(const hak_bitmask_t*)value;
#if defined(HAK_BUILD_DEBUG)
hak->option.karatsuba_cutoff = ((hak->option.trait & HAK_TRAIT_DEBUG_BIGINT)? HAK_KARATSUBA_CUTOFF_DEBUG: HAK_KARATSUBA_CUTOFF);
#endif
break;
case HAK_LOG_MASK:
case HAK_OPT_LOG_MASK:
hak->option.log_mask = *(const hak_bitmask_t*)value;
break;
case HAK_LOG_MAXCAPA:
case HAK_OPT_LOG_MAXCAPA:
hak->option.log_maxcapa = *(hak_oow_t*)value;
break;
case HAK_LOG_TARGET_BCSTR:
case HAK_OPT_LOG_TARGET_BCSTR:
{
hak_bch_t* v1;
hak_uch_t* v2;
@@ -484,7 +566,7 @@ int hak_setoption (hak_t* hak, hak_option_t id, const void* value)
break;
}
case HAK_LOG_TARGET_UCSTR:
case HAK_OPT_LOG_TARGET_UCSTR:
{
hak_uch_t* v1;
hak_bch_t* v2;
@@ -504,7 +586,7 @@ int hak_setoption (hak_t* hak, hak_option_t id, const void* value)
break;
}
case HAK_LOG_TARGET_BCS:
case HAK_OPT_LOG_TARGET_BCS:
{
hak_bch_t* v1;
hak_uch_t* v2;
@@ -525,7 +607,7 @@ int hak_setoption (hak_t* hak, hak_option_t id, const void* value)
break;
}
case HAK_LOG_TARGET_UCS:
case HAK_OPT_LOG_TARGET_UCS:
{
hak_uch_t* v1;
hak_bch_t* v2;
@@ -546,7 +628,7 @@ int hak_setoption (hak_t* hak, hak_option_t id, const void* value)
break;
}
case HAK_SYMTAB_SIZE:
case HAK_OPT_SYMTAB_SIZE:
{
hak_oow_t w;
@@ -557,7 +639,7 @@ int hak_setoption (hak_t* hak, hak_option_t id, const void* value)
break;
}
case HAK_SYSDIC_SIZE:
case HAK_OPT_SYSDIC_SIZE:
{
hak_oow_t w;
@@ -568,7 +650,7 @@ int hak_setoption (hak_t* hak, hak_option_t id, const void* value)
break;
}
case HAK_PROCSTK_SIZE:
case HAK_OPT_PROCSTK_SIZE:
{
hak_oow_t w;
@@ -579,26 +661,63 @@ int hak_setoption (hak_t* hak, hak_option_t id, const void* value)
break;
}
case HAK_MOD_LIBDIRS:
case HAK_MOD_PREFIX:
case HAK_MOD_POSTFIX:
case HAK_OPT_EXSTK_SIZE:
{
hak_oow_t w;
w = *(hak_oow_t*)value;
if (w <= 0 || w > HAK_SMOOI_MAX) goto einval;
hak->option.dfl_exstk_size = *(hak_oow_t*)value;
break;
}
case HAK_OPT_CLSTK_SIZE:
{
hak_oow_t w;
w = *(hak_oow_t*)value;
if (w <= 0 || w > HAK_SMOOI_MAX) goto einval;
hak->option.dfl_clstk_size = *(hak_oow_t*)value;
break;
}
case HAK_OPT_MODLIBDIRS_BCSTR:
if (set_dual_str_opt(hak, value, 1, &hak->option.modlibdirs_b, &hak->option.modlibdirs_u) <= -1) return -1;
break;
case HAK_OPT_MODLIBDIRS_UCSTR:
if (set_dual_str_opt(hak, value, 0, &hak->option.modlibdirs_b, &hak->option.modlibdirs_u) <= -1) return -1;
break;
case HAK_OPT_MODPREFIX:
case HAK_OPT_MODPOSTFIX:
{
hak_oocs_t tmp;
int idx;
if (dup_str_opt(hak, (const hak_ooch_t*)value, &tmp) <= -1) return -1;
idx = id - HAK_MOD_LIBDIRS;
idx = id - HAK_OPT_MODPREFIX;
if (hak->option.mod[idx].ptr) hak_freemem(hak, hak->option.mod[idx].ptr);
hak->option.mod[idx] = tmp;
return 0;
}
case HAK_MOD_INCTX:
case HAK_OPT_MODINCTX:
hak->option.mod_inctx = *(void**)value;
break;
case HAK_OPT_INCDIRS_BCSTR:
if (set_dual_str_opt(hak, value, 1, &hak->option.incdirs_b, &hak->option.incdirs_u) <= -1) return -1;
break;
case HAK_OPT_INCDIRS_UCSTR:
if (set_dual_str_opt(hak, value, 0, &hak->option.incdirs_b, &hak->option.incdirs_u) <= -1) return -1;
break;
default:
goto einval;
}
@@ -619,57 +738,80 @@ int hak_getoption (hak_t* hak, hak_option_t id, void* value)
{
switch (id)
{
case HAK_TRAIT:
case HAK_OPT_TRAIT:
*(hak_bitmask_t*)value = hak->option.trait;
return 0;
case HAK_LOG_MASK:
case HAK_OPT_LOG_MASK:
*(hak_bitmask_t*)value = hak->option.log_mask;
return 0;
case HAK_LOG_MAXCAPA:
case HAK_OPT_LOG_MAXCAPA:
*(hak_oow_t*)value = hak->option.log_maxcapa;
return 0;
case HAK_LOG_TARGET_BCSTR:
case HAK_OPT_LOG_TARGET_BCSTR:
*(hak_bch_t**)value = hak->option.log_target_b;
return 0;
case HAK_LOG_TARGET_UCSTR:
case HAK_OPT_LOG_TARGET_UCSTR:
*(hak_uch_t**)value = hak->option.log_target_u;
return 0;
case HAK_LOG_TARGET_BCS:
case HAK_OPT_LOG_TARGET_BCS:
((hak_bcs_t*)value)->ptr = hak->option.log_target_b;
((hak_bcs_t*)value)->len = hak_count_bcstr(hak->option.log_target_b);
return 0;
case HAK_LOG_TARGET_UCS:
case HAK_OPT_LOG_TARGET_UCS:
((hak_ucs_t*)value)->ptr = hak->option.log_target_u;
((hak_ucs_t*)value)->len = hak_count_ucstr(hak->option.log_target_u);
return 0;
case HAK_SYMTAB_SIZE:
case HAK_OPT_SYMTAB_SIZE:
*(hak_oow_t*)value = hak->option.dfl_symtab_size;
return 0;
case HAK_SYSDIC_SIZE:
case HAK_OPT_SYSDIC_SIZE:
*(hak_oow_t*)value = hak->option.dfl_sysdic_size;
return 0;
case HAK_PROCSTK_SIZE:
case HAK_OPT_PROCSTK_SIZE:
*(hak_oow_t*)value = hak->option.dfl_procstk_size;
return 0;
case HAK_MOD_LIBDIRS:
case HAK_MOD_PREFIX:
case HAK_MOD_POSTFIX:
*(const hak_ooch_t**)value = hak->option.mod[id - HAK_MOD_LIBDIRS].ptr;
case HAK_OPT_EXSTK_SIZE:
*(hak_oow_t*)value = hak->option.dfl_exstk_size;
return 0;
case HAK_MOD_INCTX:
case HAK_OPT_CLSTK_SIZE:
*(hak_oow_t*)value = hak->option.dfl_clstk_size;
return 0;
case HAK_OPT_MODLIBDIRS_BCSTR:
*(const hak_bch_t**)value = hak->option.modlibdirs_b;
return 0;
case HAK_OPT_MODLIBDIRS_UCSTR:
*(const hak_uch_t**)value = hak->option.modlibdirs_u;
return 0;
case HAK_OPT_MODPREFIX:
case HAK_OPT_MODPOSTFIX:
*(const hak_ooch_t**)value = hak->option.mod[id - HAK_OPT_MODPREFIX].ptr;
return 0;
case HAK_OPT_MODINCTX:
*(void**)value = hak->option.mod_inctx;
return 0;
case HAK_OPT_INCDIRS_BCSTR:
*(const hak_bch_t**)value = hak->option.incdirs_b;
return 0;
case HAK_OPT_INCDIRS_UCSTR:
*(const hak_uch_t**)value = hak->option.incdirs_u;
return 0;
};
hak_seterrnum(hak, HAK_EINVAL);
+86 -30
View File
@@ -241,33 +241,50 @@ typedef hak_errbinf_t hak_errinf_t;
enum hak_option_t
{
HAK_TRAIT,
HAK_LOG_MASK,
HAK_LOG_MAXCAPA,
HAK_OPT_TRAIT,
HAK_OPT_LOG_MASK,
HAK_OPT_LOG_MAXCAPA,
HAK_LOG_TARGET_BCSTR,
HAK_LOG_TARGET_UCSTR,
HAK_LOG_TARGET_BCS,
HAK_LOG_TARGET_UCS,
HAK_OPT_LOG_TARGET_BCSTR,
HAK_OPT_LOG_TARGET_UCSTR,
HAK_OPT_LOG_TARGET_BCS,
HAK_OPT_LOG_TARGET_UCS,
#if defined(HAK_OOCH_IS_UCH)
# define HAK_LOG_TARGET HAK_LOG_TARGET_UCSTR
# define HAK_LOG_TARGET_OOCSTR HAK_LOG_TARGET_UCSTR
# define HAK_LOG_TARGET_OOCS HAK_LOG_TARGET_UCS
# define HAK_OPT_LOG_TARGET HAK_OPT_LOG_TARGET_UCSTR
# define HAK_OPT_LOG_TARGET_OOCSTR HAK_OPT_LOG_TARGET_UCSTR
# define HAK_OPT_LOG_TARGET_OOCS HAK_OPT_LOG_TARGET_UCS
#else
# define HAK_LOG_TARGET HAK_LOG_TARGET_BCSTR
# define HAK_LOG_TARGET_OOCSTR HAK_LOG_TARGET_BCSTR
# define HAK_LOG_TARGET_OOCS HAK_LOG_TARGET_BCS
# define HAK_OPT_LOG_TARGET HAK_OPT_LOG_TARGET_BCSTR
# define HAK_OPT_LOG_TARGET_OOCSTR HAK_OPT_LOG_TARGET_BCSTR
# define HAK_OPT_LOG_TARGET_OOCS HAK_OPT_LOG_TARGET_BCS
#endif
HAK_SYMTAB_SIZE, /* default system table size */
HAK_SYSDIC_SIZE, /* default system dictionary size */
HAK_PROCSTK_SIZE, /* default process stack size */
HAK_OPT_SYMTAB_SIZE, /* default system table size */
HAK_OPT_SYSDIC_SIZE, /* default system dictionary size */
HAK_OPT_PROCSTK_SIZE, /* default process stack size */
HAK_OPT_EXSTK_SIZE, /* default exception stack size */
HAK_OPT_CLSTK_SIZE, /* default class stack size */
HAK_MOD_LIBDIRS,
HAK_MOD_PREFIX,
HAK_MOD_POSTFIX,
HAK_OPT_MODLIBDIRS_BCSTR,
HAK_OPT_MODLIBDIRS_UCSTR,
#if defined(HAK_OOCH_IS_UCH)
# define HAK_OPT_MODLIBDIRS HAK_OPT_MODLIBDIRS_UCSTR
#else
# define HAK_OPT_MODLIBDIRS HAK_OPT_MODLIBDIRS_BCSTR
#endif
HAK_MOD_INCTX
HAK_OPT_MODPREFIX,
HAK_OPT_MODPOSTFIX,
HAK_OPT_MODINCTX,
HAK_OPT_INCDIRS_BCSTR,
HAK_OPT_INCDIRS_UCSTR
#if defined(HAK_OOCH_IS_UCH)
# define HAK_OPT_INCDIRS HAK_OPT_INCDIRS_UCSTR
#else
# define HAK_OPT_INCDIRS HAK_OPT_INCDIRS_BCSTR
#endif
};
typedef enum hak_option_t hak_option_t;
@@ -277,12 +294,28 @@ typedef enum hak_option_t hak_option_t;
enum hak_option_dflval_t
{
HAK_DFL_LOG_MAXCAPA = HAK_LOG_CAPA_ALIGN * 16,
HAK_DFL_SYMTAB_SIZE = 5000,
HAK_DFL_SYSDIC_SIZE = 5000,
HAK_DFL_PROCSTK_SIZE = 5000
#if defined(HAK_SMALL_MEMORY_FOOTPRINT)
HAK_DFL_SYMTAB_SIZE = 1024,
HAK_DFL_SYSDIC_SIZE = 1024
HAK_DFL_PROCSTK_SIZE = 1024, /* -> fstk 1024, the FSTK floor */
HAK_DFL_EXSTK_SIZE = 256, /* 4 slots/level x 59 levels = 236 */
HAK_DFL_CLSTK_SIZE = 16, /* = HAK_MIN_CLSTK_SIZE */
#else
HAK_DFL_SYMTAB_SIZE = 8192,
HAK_DFL_SYSDIC_SIZE = 8192,
HAK_DFL_PROCSTK_SIZE = 8192,
HAK_DFL_EXSTK_SIZE = 2048,
HAK_DFL_CLSTK_SIZE = 64
#endif
};
typedef enum hak_option_dflval_t hak_option_dflval_t;
#define HAK_MIN_PROCSTK_SIZE (192)
#define HAK_MIN_EXSTK_SIZE (32) /* 4 slots/try -> 8 nested trys */
#define HAK_MIN_CLSTK_SIZE (16) /* 1 slot/class -> 16 nested classes */
#define HAK_MIN_FSTK_SIZE (1024)
enum hak_trait_t
{
#if defined(HAK_BUILD_DEBUG)
@@ -857,6 +890,9 @@ struct hak_semaphore_t
} waiting; /* list of processes waiting on this semaphore */
/* [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 */
/* nil for normal. SmallInteger if associated with
@@ -1570,9 +1606,9 @@ typedef hak_pfrc_t (*hak_pfimpl_t) (
enum hak_pfbase_type_t
{
HAK_PFBASE_FUNC = 0,
HAK_PFBASE_VAR = 1,
HAK_PFBASE_CONST = 2
HAK_PFBASE_FUNC = 0,
HAK_PFBASE_VAR = 1,
HAK_PFBASE_CONST_SMOOI = 2
};
typedef enum hak_pfbase_type_t hak_pfbase_type_t;
@@ -1640,11 +1676,14 @@ struct 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
{
hak_oop_semaphore_t sem[2]; /* [0] input, [1] output */
hak_ooi_t handle; /* io handle */
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;
@@ -1758,9 +1797,22 @@ struct hak_t
hak_oow_t dfl_symtab_size;
hak_oow_t dfl_sysdic_size;
hak_oow_t dfl_procstk_size;
hak_oow_t dfl_exstk_size;
hak_oow_t dfl_clstk_size;
void* mod_inctx;
hak_oocs_t mod[3];
/* both representations are kept for the two options whose consumers
* are byte oriented: dl_open() feeds dlopen() and open_cci_stream()
* feeds fopen(), so the bch form is what actually gets used, while the
* uch form serves getoption and %js. converting once at set time beats
* converting on every module load and every include attempt. */
hak_bch_t* modlibdirs_b;
hak_uch_t* modlibdirs_u;
hak_bch_t* incdirs_b;
hak_uch_t* incdirs_u;
/* prefix and postfix only - indexed by (id - HAK_OPT_MODPREFIX) */
hak_oocs_t mod[2];
#if defined(HAK_BUILD_DEBUG)
/* set automatically when trait is set */
@@ -1915,11 +1967,11 @@ struct hak_t
hak_oob_t* active_code;
hak_ooi_t sp;
hak_ooi_t ip;
hak_uint8_t abort_req;
volatile hak_int8_t abort_req;
hak_uint8_t no_proc_switch; /* process switching disabled */
hak_uint8_t proc_switched; /* TODO: this is temporary. implement something else to skip immediate context switching */
hak_uint8_t rcv_tick; /* whether to receive tick or not */
hak_uint32_t tick; /* instance specific tick */
volatile hak_uint32_t tick; /* instance specific tick */
hak_uint32_t last_tick; /* last instance specific tick this instance acted on */
hak_uint32_t last_gtick; /* last global tick this instance acted on */
@@ -3543,7 +3595,11 @@ HAK_EXPORT void hak_assertfailed (
/* =========================================================================
* HELPERS
* ========================================================================= */
HAK_EXPORT void hak_start_ticker (
/**
* The hak_start_ticker() starts the ticker for process switching.
* It returns 1 on success, 0 if already started, -1 upon failure.
*/
HAK_EXPORT int hak_start_ticker (
void
);
+2 -2
View File
@@ -952,7 +952,7 @@ hak_json_t* hak_json_open (hak_mmgr_t* mmgr, hak_oow_t xtnsize, hak_json_prim_t*
/* the dummy hak is used for this json to perform primitive operations
* such as getting system time or logging. so the heap size doesn't
* need to be changed from the tiny value set above. */
hak_setoption(json->dummy_hak, HAK_LOG_MASK, &json->cfg.logmask);
hak_setoption(json->dummy_hak, HAK_OPT_LOG_MASK, &json->cfg.logmask);
hak_setcmgr(json->dummy_hak, json->cmgr);
@@ -987,7 +987,7 @@ int hak_json_setoption (hak_json_t* json, hak_json_option_t id, const void* valu
* existing hak instances inside worker threads won't get
* affected. new hak instances to be created later
* is supposed to use the new value */
hak_setoption(json->dummy_hak, HAK_LOG_MASK, value);
hak_setoption(json->dummy_hak, HAK_OPT_LOG_MASK, value);
}
return 0;
}
+4 -3
View File
@@ -470,6 +470,10 @@ hak_oop_t hak_makestringwithuchars (hak_t* hak, const hak_uch_t* ptr, hak_oow_t
hak_oop_t hak_makestringwithbchars (hak_t* hak, const hak_bch_t* ptr, hak_oow_t len)
{
#if defined(HAK_OOCH_IS_UCH)
hak_oow_t xlen;
hak_ooch_t* xptr;
#endif
/* you must provide the payload when calling this variant. it can't figure out
* the actual number of hak_ooch_t characters */
if (!ptr)
@@ -480,9 +484,6 @@ hak_oop_t hak_makestringwithbchars (hak_t* hak, const hak_bch_t* ptr, hak_oow_t
}
#if defined(HAK_OOCH_IS_UCH)
hak_oow_t xlen;
hak_ooch_t* xptr;
xptr = hak_dupbtooochars(hak, ptr, len, &xlen);
if (HAK_UNLIKELY(!xptr))
{
+17 -41
View File
@@ -1220,7 +1220,7 @@ static hak_pfrc_t pf_va_get (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
}
static hak_pfrc_t pf_object_new (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
hak_pfrc_t hak_pf_object_new (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
{
hak_oop_t obj;
hak_oop_t _class;
@@ -1256,7 +1256,7 @@ static hak_pfrc_t pf_object_new (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
/* ------------------------------------------------------------------------- */
static hak_pfrc_t pf_system_get_sigfd (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
hak_pfrc_t hak_pf_system_get_sigfd (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
{
hak_ooi_t fd;
hak_hnd_t* hnd;
@@ -1264,7 +1264,7 @@ static hak_pfrc_t pf_system_get_sigfd (hak_t* hak, hak_mod_t* mod, hak_ooi_t nar
fd = hak->vmprim.vm_getsigfd(hak);
/* hand back a system handle id rather than the descriptor itself, so that
* the result can be given to sem-signal-on-input - which resolves handle
* the result can be given to core.sem-signal-on-input - which resolves handle
* ids, not descriptors. wrapped HAK_HND_OPEN_KEEPOPEN because the VM owns
* this descriptor and manages its blocking mode; the table must never
* close it. wrapfd_once() keeps the id stable across calls. */
@@ -1275,7 +1275,7 @@ static hak_pfrc_t pf_system_get_sigfd (hak_t* hak, hak_mod_t* mod, hak_ooi_t nar
return HAK_PF_SUCCESS;
}
static hak_pfrc_t pf_system_get_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
hak_pfrc_t hak_pf_system_get_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
{
hak_uint8_t sig;
int n;
@@ -1289,12 +1289,12 @@ static hak_pfrc_t pf_system_get_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs
return HAK_PF_SUCCESS;
}
/* (system-catch-sig signo) - route an operating system signal into the
* signal descriptor, where hak code can wait for
* it with sem-signal-on-input
* (system-uncatch-sig signo) - release it again
/* (sys.sig-catch signo) - route an operating system signal into the signal
* descriptor, where hak code can wait for it with
* core.sem-signal-on-input
* (sys.sig-uncatch signo) - release it again
*
* Note the difference from system-set-sig, which does not touch the operating
* Note the difference from sys.sig-set, which does not touch the operating
* system at all: that one injects a number into the descriptor directly, as a
* way for hak code to post a synthetic signal to itself.
*/
@@ -1323,17 +1323,17 @@ static hak_pfrc_t __system_catch_sig (hak_t* hak, hak_ooi_t nargs, int enable)
return HAK_PF_SUCCESS;
}
static hak_pfrc_t pf_system_catch_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
hak_pfrc_t hak_pf_system_catch_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
{
return __system_catch_sig(hak, nargs, 1);
}
static hak_pfrc_t pf_system_uncatch_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
hak_pfrc_t hak_pf_system_uncatch_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
{
return __system_catch_sig(hak, nargs, 0);
}
static hak_pfrc_t pf_system_set_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
hak_pfrc_t hak_pf_system_set_sig (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
{
hak_oop_t tmp;
hak_uint8_t sig;
@@ -1366,11 +1366,10 @@ static pf_t builtin_prims[] =
{ 1, HAK_TYPE_MAX(hak_oow_t), pf_scanf, 5, { 's','c','a','n','f' } },
{ 1, HAK_TYPE_MAX(hak_oow_t), pf_sprintf, 7, { 's','p','r','i','n','t','f' } },
{ 0, 0, pf_system_get_sigfd, 16, { 's','y','s','t','e','m','-','g','e','t','-','s','i','g','f','d' } },
{ 0, 0, pf_system_get_sig, 14, { 's','y','s','t','e','m','-','g','e','t','-','s','i','g' } },
{ 1, 1, pf_system_set_sig, 14, { 's','y','s','t','e','m','-','s','e','t','-','s','i','g' } },
{ 1, 1, pf_system_catch_sig, 16, { 's','y','s','t','e','m','-','c','a','t','c','h','-','s','i','g' } },
{ 1, 1, pf_system_uncatch_sig, 18, { 's','y','s','t','e','m','-','u','n','c','a','t','c','h','-','s','i','g' } },
/* the signal primitives are registered by the sys module instead - see
* pfinfos[] in mod/sys.c. they are reached as sys.sig-getfd, sys.sig-get,
* sys.sig-set, sys.sig-catch and sys.sig-uncatch. the implementations stay
* here and are declared in lib/hak-prv.h. */
{ 0, 0, pf_gc, 2, { 'g','c' } },
@@ -1435,30 +1434,7 @@ static pf_t builtin_prims[] =
{ 0, 0, pf_va_context, 10, { 'v','a','-','c','o','n','t','e','x','t' } },
{ 0, 1, pf_va_count, 8, { 'v','a','-','c','o','u','n','t' } },
{ 1, 2, pf_va_get, 6, { 'v','a','-','g','e','t' } },
{ 1, 2, pf_object_new, 10, { 'o','b','j','e','c','t','-','n','e','w' } },
{ 0, 0, hak_pf_process_current, 15, { 'c','u','r','r','e','n','t','-','p','r','o','c','e','s','s'} },
{ 1, HAK_TYPE_MAX(hak_oow_t), hak_pf_process_fork, 4, { 'f','o','r','k'} },
{ 1, 1, hak_pf_process_resume, 6, { 'r','e','s','u','m','e' } },
{ 0, 1, hak_pf_process_suspend, 7, { 's','u','s','p','e','n','d' } },
{ 0, 1, hak_pf_process_terminate, 9, { 't','e','r','m','i','n','a','t','e' } },
{ 0, 0, hak_pf_process_terminate_all, 13, { 't','e','r','m','i','n','a','t','e','-','a','l','l' } },
{ 0, 0, hak_pf_process_yield, 5, { 'y','i','e','l','d'} },
{ 0, 0, hak_pf_semaphore_new, 7, { 's','e','m','-','n','e','w'} },
{ 1, 1, hak_pf_semaphore_wait, 8, { 's','e','m','-','w','a','i','t'} },
{ 1, 3, hak_pf_semaphore_signal, 10, { 's','e','m','-','s','i','g','n','a','l'} },
{ 2, 2, hak_pf_semaphore_signal_on_input, 19, { 's','e','m','-','s','i','g','n','a','l','-','o','n','-','i','n','p','u','t'} },
{ 2, 2, hak_pf_semaphore_signal_on_output, 20, { 's','e','m','-','s','i','g','n','a','l','-','o','n','-','o','u','t','p','u','t'} },
{ 1, 1, hak_pf_semaphore_unsignal, 12, { 's','e','m','-','u','n','s','i','g','n','a','l'} },
{ 0, 0, hak_pf_semaphore_group_new, 9, { 's','e','m','g','r','-','n','e','w'} },
{ 1, 2, hak_pf_semaphore_group_add_semaphore, 9, { 's','e','m','g','r','-','a','d','d'} },
{ 1, 2, hak_pf_semaphore_group_remove_semaphore, 12, { 's','e','m','g','r','-','r','e','m','o','v','e'} },
{ 1, 1, hak_pf_semaphore_group_wait, 10, { 's','e','m','g','r','-','w','a','i','t'} }
{ 1, 2, pf_va_get, 6, { 'v','a','-','g','e','t' } }
};
int hak_addbuiltinprims (hak_t* hak)
+2 -2
View File
@@ -300,7 +300,7 @@ static HAK_INLINE int is_delim_char (hak_ooci_t c)
return c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' ||
c == '|' || c == ',' || c == '.' || c == ':' || c == ';' ||
/* the first characters of tokens in delim_token_tab up to this point */
#if defined(HAK_OOCH_IS_UCH) && defined(HAK_LANG_ENABLE_WIDE_DELIM)
#if defined(HAK_OOCH_IS_UCH) && defined(HAK_LANG_ENABLE_WIDE_DELIM) && defined(HAVE_ESCAPE_U)
c == L'\u201C' || c == L'\u201D' || /* “ ” */
c == L'\u2018' || c == L'\u2019' || /* */
#endif
@@ -2665,7 +2665,7 @@ static int flx_start (hak_t* hak, hak_ooci_t c)
FEED_CONTINUE(hak, HAK_FLX_QUOTED_TOKEN); /* discard the quote itself. move on the the QUOTED_TOKEN state */
goto consumed;
#if defined(HAK_OOCH_IS_UCH) && defined(HAK_LANG_ENABLE_WIDE_DELIM)
#if defined(HAK_OOCH_IS_UCH) && defined(HAK_LANG_ENABLE_WIDE_DELIM) && defined(HAVE_ESCAPE_U)
case L'\u201C': /* “ ” */
init_flx_qt(FLX_QT(hak), HAK_TOK_STRLIT, HAK_SYNERR_STRLIT, L'\u201D', '\\', 0, HAK_TYPE_MAX(hak_oow_t), 0);
FEED_CONTINUE(hak, HAK_FLX_QUOTED_TOKEN); /* discard the quote itself. move on the the QUOTED_TOKEN state */
+453 -149
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -212,7 +212,7 @@ hak_client_t* hak_client_open (hak_mmgr_t* mmgr, hak_oow_t xtnsize, hak_client_p
/* the dummy hak is used for this client to perform primitive operations
* such as getting system time or logging. so the heap size doesn't
* need to be changed from the tiny value set above. */
hak_setoption (client->dummy_hak, HAK_LOG_MASK, &client->cfg.logmask);
hak_setoption (client->dummy_hak, HAK_OPT_LOG_MASK, &client->cfg.logmask);
hak_setcmgr (client->dummy_hak, client->_cmgr);
return client;
@@ -258,7 +258,7 @@ int hak_client_setoption (hak_client_t* client, hak_client_option_t id, const vo
* existing hak instances inside worker threads won't get
* affected. new hak instances to be created later
* is supposed to use the new value */
hak_setoption (client->dummy_hak, HAK_LOG_MASK, value);
hak_setoption (client->dummy_hak, HAK_OPT_LOG_MASK, value);
}
return 0;
}
+10 -10
View File
@@ -1177,14 +1177,14 @@ hak_server_t* hak_server_open (hak_mmgr_t* mmgr, hak_oow_t xtnsize, hak_server_p
/* the dummy hak is used for this server to perform primitive operations
* such as getting system time or logging. so the heap size doesn't
* need to be changed from the tiny value set above. */
hak_setoption (server->dummy_hak, HAK_LOG_MASK, &server->cfg.logmask);
hak_setoption (server->dummy_hak, HAK_OPT_LOG_MASK, &server->cfg.logmask);
hak_setcmgr (server->dummy_hak, hak_server_getcmgr(server));
hak_getoption (server->dummy_hak, HAK_TRAIT, &trait);
hak_getoption (server->dummy_hak, HAK_OPT_TRAIT, &trait);
#if defined(HAK_BUILD_DEBUG)
if (server->cfg.trait & HAK_SERVER_TRAIT_DEBUG_GC) trait |= HAK_TRAIT_DEBUG_GC;
if (server->cfg.trait & HAK_SERVER_TRAIT_DEBUG_BIGINT) trait |= HAK_TRAIT_DEBUG_BIGINT;
#endif
hak_setoption (server->dummy_hak, HAK_TRAIT, &trait);
hak_setoption (server->dummy_hak, HAK_OPT_TRAIT, &trait);
return server;
@@ -1517,17 +1517,17 @@ static int init_worker_hak (hak_server_worker_t* worker)
xtn = (worker_hak_xtn_t*)hak_getxtn(hak);
xtn->worker = worker;
hak_setoption(hak, HAK_MOD_INCTX, &server->cfg.module_inctx);
hak_setoption(hak, HAK_LOG_MASK, &server->cfg.logmask);
hak_setoption(hak, HAK_OPT_MODINCTX, &server->cfg.module_inctx);
hak_setoption(hak, HAK_OPT_LOG_MASK, &server->cfg.logmask);
hak_setcmgr(hak, hak_server_getcmgr(server));
hak_getoption(hak, HAK_TRAIT, &trait);
hak_getoption(hak, HAK_OPT_TRAIT, &trait);
#if defined(HAK_BUILD_DEBUG)
if (server->cfg.trait & HAK_SERVER_TRAIT_DEBUG_GC) trait |= HAK_TRAIT_DEBUG_GC;
if (server->cfg.trait & HAK_SERVER_TRAIT_DEBUG_BIGINT) trait |= HAK_TRAIT_DEBUG_BIGINT;
#endif
trait |= HAK_TRAIT_LANG_ENABLE_EOL;
hak_setoption(hak, HAK_TRAIT, &trait);
hak_setoption(hak, HAK_OPT_TRAIT, &trait);
HAK_MEMSET(&hakcb, 0, HAK_SIZEOF(hakcb));
/*hakcb.fini = fini_hak;
@@ -2050,12 +2050,12 @@ int hak_server_setoption (hak_server_t* server, hak_server_option_t id, const vo
* is supposed to use the new value */
hak_bitmask_t trait;
hak_getoption (server->dummy_hak, HAK_TRAIT, &trait);
hak_getoption (server->dummy_hak, HAK_OPT_TRAIT, &trait);
#if defined(HAK_BUILD_DEBUG)
if (server->cfg.trait & HAK_SERVER_TRAIT_DEBUG_GC) trait |= HAK_TRAIT_DEBUG_GC;
if (server->cfg.trait & HAK_SERVER_TRAIT_DEBUG_BIGINT) trait |= HAK_TRAIT_DEBUG_BIGINT;
#endif
hak_setoption (server->dummy_hak, HAK_TRAIT, &trait);
hak_setoption (server->dummy_hak, HAK_OPT_TRAIT, &trait);
}
return 0;
@@ -2067,7 +2067,7 @@ int hak_server_setoption (hak_server_t* server, hak_server_option_t id, const vo
* existing hak instances inside worker threads won't get
* affected. new hak instances to be created later
* is supposed to use the new value */
hak_setoption (server->dummy_hak, HAK_LOG_MASK, value);
hak_setoption (server->dummy_hak, HAK_OPT_LOG_MASK, value);
}
return 0;
+25
View File
@@ -605,21 +605,46 @@ static hak_pfinfo_t pfinfos[] =
{ "classRespondsTo", { HAK_PFBASE_FUNC, pf_core_class_responds_to, 2, 2 } },
{ "cons", { HAK_PFBASE_FUNC, pf_core_cons, 2, 2 } },
{ "current-process", { HAK_PFBASE_FUNC, hak_pf_process_current, 0, 0 } },
{ "eqk?", { HAK_PFBASE_FUNC, hak_pf_eqk, 2, 2 } },
{ "eql?", { HAK_PFBASE_FUNC, hak_pf_eql, 2, 2 } },
{ "eqv?", { HAK_PFBASE_FUNC, hak_pf_eqv, 2, 2 } },
{ "fork", { HAK_PFBASE_FUNC, hak_pf_process_fork, 1, HAK_TYPE_MAX(hak_oow_t) } },
{ "instRespondsTo", { HAK_PFBASE_FUNC, pf_core_inst_responds_to, 2, 2 } },
{ "nqk?", { HAK_PFBASE_FUNC, hak_pf_nqk, 2, 2 } },
{ "nql?", { HAK_PFBASE_FUNC, hak_pf_nql, 2, 2 } },
{ "nqv?", { HAK_PFBASE_FUNC, hak_pf_nqv, 2, 2 } },
{ "object-new", { HAK_PFBASE_FUNC, hak_pf_object_new, 1, 2 } },
{ "primAt", { HAK_PFBASE_FUNC, pf_core_prim_at, 2, 2 } },
{ "primAtPut", { HAK_PFBASE_FUNC, pf_core_prim_at_put, 3, 3 } },
{ "resume", { HAK_PFBASE_FUNC, hak_pf_process_resume, 1, 1 } },
{ "sem-new", { HAK_PFBASE_FUNC, hak_pf_semaphore_new, 0, 1 } },
{ "sem-signal", { HAK_PFBASE_FUNC, hak_pf_semaphore_signal, 1, 3 } },
{ "sem-signal-on-input", { HAK_PFBASE_FUNC, hak_pf_semaphore_signal_on_input, 2, 2 } },
{ "sem-signal-on-output", { HAK_PFBASE_FUNC, hak_pf_semaphore_signal_on_output, 2, 2 } },
{ "sem-unsignal", { HAK_PFBASE_FUNC, hak_pf_semaphore_unsignal, 1, 1 } },
{ "sem-wait", { HAK_PFBASE_FUNC, hak_pf_semaphore_wait, 1, 1 } },
{ "semgr-add", { HAK_PFBASE_FUNC, hak_pf_semaphore_group_add_semaphore, 1, 2 } },
{ "semgr-new", { HAK_PFBASE_FUNC, hak_pf_semaphore_group_new, 0, 0 } },
{ "semgr-remove", { HAK_PFBASE_FUNC, hak_pf_semaphore_group_remove_semaphore, 1, 2 } },
{ "semgr-wait", { HAK_PFBASE_FUNC, hak_pf_semaphore_group_wait, 1, 1 } },
{ "slice", { HAK_PFBASE_FUNC, pf_core_slice, 3, 3 } },
{ "smooiToChar", { HAK_PFBASE_FUNC, pf_core_smooi_to_char, 1, 1 } },
{ "sqrt", { HAK_PFBASE_FUNC, hak_pf_number_sqrt, 1, 1 } },
{ "suspend", { HAK_PFBASE_FUNC, hak_pf_process_suspend, 0, 1 } },
{ "terminate", { HAK_PFBASE_FUNC, hak_pf_process_terminate, 0, 1 } },
{ "terminate-all", { HAK_PFBASE_FUNC, hak_pf_process_terminate_all, 0, 0 } },
{ "yield", { HAK_PFBASE_FUNC, hak_pf_process_yield, 0, 0 } },
{ "~=", { HAK_PFBASE_FUNC, hak_pf_number_ne, 2, 2 } },
};
+93 -17
View File
@@ -31,10 +31,10 @@
#endif
#include "_sys.h"
#include <hak-hnd.h>
#include "../lib/hak-prv.h"
#include <hak-pio.h>
#include <hak-str.h>
#include <stdlib.h>
#include <signal.h>
#if !defined(_WIN32)
# include <sys/types.h>
@@ -42,7 +42,6 @@
# include <unistd.h>
# include <fcntl.h>
# include <errno.h>
# include <sys/syscall.h>
#endif
#if defined(HAVE_SYS_TIME_H)
@@ -174,7 +173,7 @@ static hak_pfrc_t pf_sys_random (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
* sys.read and sys.write follow the non-blocking contract: they return the
* byte count, 0 at end of file, or -1 when the handle would have blocked.
* -1 is an ordinary outcome - the caller is expected to wait on a semaphore
* bound with sem-signal-on-input/-output and try again. Only a genuine
* bound with core.sem-signal-on-input/-output and try again. Only a genuine
* failure raises.
* ------------------------------------------------------------------------ */
@@ -734,19 +733,96 @@ static hak_pfrc_t pf_sys_pclose (hak_t* hak, hak_mod_t* mod, hak_ooi_t nargs)
static hak_pfinfo_t pfinfos[] =
{
{ "close", { HAK_PFBASE_FUNC, pf_sys_close, 1, 1 } },
{ "open", { HAK_PFBASE_FUNC, pf_sys_open, 2, 3 } },
{ "pclose", { HAK_PFBASE_FUNC, pf_sys_pclose, 1, 1 } },
{ "pipe", { HAK_PFBASE_FUNC, pf_sys_pipe, 0, 0 } },
{ "pkill", { HAK_PFBASE_FUNC, pf_sys_pkill, 1, 1 } },
{ "popen", { HAK_PFBASE_FUNC, pf_sys_popen, 1, 2 } },
{ "pwait", { HAK_PFBASE_FUNC, pf_sys_pwait, 1, 1 } },
{ "random", { HAK_PFBASE_FUNC, pf_sys_random, 0, 0 } },
{ "read", { HAK_PFBASE_FUNC, pf_sys_read, 2, 4 } },
{ "srandom", { HAK_PFBASE_FUNC, pf_sys_srandom, 1, 1 } },
{ "stime", { HAK_PFBASE_FUNC, pf_sys_stime, 1, 1 } },
{ "time", { HAK_PFBASE_FUNC, pf_sys_time, 0, 0 } },
{ "write", { HAK_PFBASE_FUNC, pf_sys_write, 2, 4 } }
#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 } },
{ "open", { HAK_PFBASE_FUNC, pf_sys_open, 2, 3 } },
{ "pclose", { HAK_PFBASE_FUNC, pf_sys_pclose, 1, 1 } },
{ "pipe", { HAK_PFBASE_FUNC, pf_sys_pipe, 0, 0 } },
{ "pkill", { HAK_PFBASE_FUNC, pf_sys_pkill, 1, 1 } },
{ "popen", { HAK_PFBASE_FUNC, pf_sys_popen, 1, 2 } },
{ "pwait", { HAK_PFBASE_FUNC, pf_sys_pwait, 1, 1 } },
{ "random", { HAK_PFBASE_FUNC, pf_sys_random, 0, 0 } },
{ "read", { HAK_PFBASE_FUNC, pf_sys_read, 2, 4 } },
{ "sig-catch", { HAK_PFBASE_FUNC, hak_pf_system_catch_sig, 1, 1 } },
{ "sig-get", { HAK_PFBASE_FUNC, hak_pf_system_get_sig, 0, 0 } },
{ "sig-getfd", { HAK_PFBASE_FUNC, hak_pf_system_get_sigfd, 0, 0 } },
{ "sig-set", { HAK_PFBASE_FUNC, hak_pf_system_set_sig, 1, 1 } },
{ "sig-uncatch", { HAK_PFBASE_FUNC, hak_pf_system_uncatch_sig, 1, 1 } },
{ "srandom", { HAK_PFBASE_FUNC, pf_sys_srandom, 1, 1 } },
{ "stime", { HAK_PFBASE_FUNC, pf_sys_stime, 1, 1 } },
{ "time", { HAK_PFBASE_FUNC, pf_sys_time, 0, 0 } },
{ "write", { HAK_PFBASE_FUNC, pf_sys_write, 2, 4 } }
};
/* ------------------------------------------------------------------------ */
+3
View File
@@ -42,6 +42,9 @@ class FixedSizedCollection: IndexedCollection {
class[#varying] Array: FixedSizedCollection {
}
class[#byte #varying] ByteArray: FixedSizedCollection {
}
class[#char #varying] String: FixedSizedCollection {
fun[#class] initValue() {
##return '\0'
+76
View File
@@ -0,0 +1,76 @@
class Apex {
fun isNil?() { return false }
fun notNil?() { return true }
fun[#class] basicNew(size) {
return (core.basicNew self size)
}
fun[#class] respondsTo(mthname) {
return (core.classRespondsTo self mthname)
}
fun respondsTo(mthname) {
return (core.instRespondsTo self mthname)
}
fun primAt(pos) {
return (core.primAt self pos)
}
fun primtAtPut(pos value) {
return (core.primAtPut self pos value)
}
fun basicAt(pos) {
return (core.basicAt self pos)
}
fun basicAtPut(pos value) {
return (core.basicAtPut self pos value)
}
fun basicSize() {
return (core.basicSize self)
}
## TODO: ...
fun == (oprnd) { return (== self oprnd) }
fun != (oprnd) { return (!= self oprnd) }
## TODO: fun perform(name ...) {}
}
class[#uncopyable #varying #limited #final] Class: Apex (
_name
_mdic
_spec
_selfspec
_superclass
_nivars_super
_ibrand
_ivarnames
_cvarnames
) {
fun name() {
##return (core.className self)
return _class
}
fun instanceVariableNames() {
## TODO: this still returns nil as the acutal manipulation of the field has not been implemented
return _ivarnames
}
fun classVariableNames() {
## TODO: this still returns nil as the acutal manipulation of the field has not been implemented
return _cvarnames
}
}
class UndefinedObject: Apex {
fun isNil?() { return true }
fun notNil?() { return false }
}
class Object: Apex {
}
+73
View File
@@ -0,0 +1,73 @@
class[#uncopyable] Semaphore: Object(
_waiting_first
_wait_last
_count
_subtype
_index
_ftime_sec_or_handle
_ftime_nsec_or_type
_signal_action
_group
_grm_prev
_grm_next
) {
fun[#class] new() {
return (core.sem-new 0)
}
fun[#class] forMutex() {
return (core.sem-new 1)
}
fun signal() {
return (core.sem-signal self 0 0)
}
fun signalAfter(secs nsecs) {
return (core.sem-signal self secs nsecs)
}
fun signalOnInput(handle) {
return (core.sem-signal-on-input self handle)
}
fun signalOnOutput(handle) {
return (core.sem-signal-on-output self handle)
}
fun unsignal() {
return (core.sem-unsignal self)
}
fun wait() {
return (core.sem-wait self)
}
}
class[#uncopyable] SemaphoreGroup: Object(
_waiting_first
_waiting_last
_sem_unsig_first
_sem_unsig_last
_sem_sig_first
_sem_sig_last
_sem_io_count
_sem_count
) {
fun[#class] new() {
return (core.semgr-new)
}
fun add(sem) {
return (core.semgr-add self sem)
}
fun remove(sem) {
return (core.semgr-remove self sem)
}
fun wait() {
return (core.semgr-wait self)
}
}
+3 -79
View File
@@ -1,84 +1,8 @@
class Apex {
fun isNil?() { return false }
fun notNil?() { return true }
fun[#class] basicNew(size) {
return (core.basicNew self size)
}
fun[#class] respondsTo(mthname) {
return (core.classRespondsTo self mthname)
}
fun respondsTo(mthname) {
return (core.instRespondsTo self mthname)
}
fun primAt(pos) {
return (core.primAt self pos)
}
fun primtAtPut(pos value) {
return (core.primAtPut self pos value)
}
fun basicAt(pos) {
return (core.basicAt self pos)
}
fun basicAtPut(pos value) {
return (core.basicAtPut self pos value)
}
fun basicSize() {
return (core.basicSize self)
}
## TODO: ...
fun == (oprnd) { return (== self oprnd) }
fun != (oprnd) { return (!= self oprnd) }
## TODO: fun perform(name ...) {}
}
class[#uncopyable #varying #limited #final] Class: Apex (
_name
_mdic
_spec
_selfspec
_superclass
_nivars_super
_ibrand
_ivarnames
_cvarnames
) {
fun name() {
##return (core.className self)
return _class
}
fun instanceVariableNames() {
## TODO: this still returns nil as the acutal manipulation of the field has not been implemented
return _ivarnames
}
fun classVariableNames() {
## TODO: this still returns nil as the acutal manipulation of the field has not been implemented
return _cvarnames
}
}
class UndefinedObject: Apex {
fun isNil?() { return true }
fun notNil?() { return false }
}
class Object: Apex {
}
## ---------------------------------------------------------------------------------
$include "Object.hak"
$include "Magnitude.hak"
$include "Collection.hak"
$include "Semaphore.hak"
##$include "System.hak"
## ---------------------------------------------------------------------------------
+19 -19
View File
@@ -37,9 +37,9 @@ class ChildGroup(
sigsem ## the shared semaphore; only in shared mode
) {
fun[#ci] new() {
set sg (semgr-new)
set tmo (sem-new)
semgr-add sg tmo
set sg (core.semgr-new)
set tmo (core.sem-new)
core.semgr-add sg tmo
set capa 8
set kids (core.basicNew Array 8)
set nkids 0
@@ -107,16 +107,16 @@ class ChildGroup(
## semaphore on the signal descriptor for the whole group, once.
if (not self.shared) {
set shared true
system-catch-sig 17 ## SIGCHLD - define this as a constant...
set sigsem (sem-new)
semgr-add self.sg self.sigsem
sem-signal-on-input self.sigsem (system-get-sigfd)
sys.sig-catch sys.SIGCHLD
set sigsem (core.sem-new)
core.semgr-add self.sg self.sigsem
core.sem-signal-on-input self.sigsem (sys.sig-getfd)
}
} else {
## A handle of its own, so a wakeup identifies this child directly.
sem := (sem-new)
semgr-add self.sg sem
sem-signal-on-input sem xh
sem := (core.sem-new)
core.semgr-add self.sg sem
core.sem-signal-on-input sem xh
}
kid := (core.basicNew Array 6)
@@ -171,16 +171,16 @@ class ChildGroup(
if (not (nil? kid)) { return kid }
while true {
sem-signal self.tmo secs 0
w := (semgr-wait self.sg)
sem-unsignal self.tmo
core.sem-signal self.tmo secs 0
w := (core.semgr-wait self.sg)
core.sem-unsignal self.tmo
if (eqv? w self.tmo) { return nil }
if self.shared {
## the signal descriptor spoke: take the byte, then look at
## everyone, since one SIGCHLD may stand for several exits
system-get-sig
sys.sig-get
kid := (self:finished)
if (not (nil? kid)) { return kid }
## otherwise it was a SIGCHLD for a child of the host
@@ -188,7 +188,7 @@ class ChildGroup(
} else {
kid := (self:kid-of-sem w)
if (not (nil? kid)) {
sem-unsignal w
core.sem-unsignal w
return kid
}
}
@@ -210,8 +210,8 @@ class ChildGroup(
| sem |
sem := (core.basicAt kid 5)
if (not (nil? sem)) {
sem-unsignal sem
semgr-remove self.sg sem
core.sem-unsignal sem
core.semgr-remove self.sg sem
}
self:forget kid
sys.pclose (core.basicAt kid 0)
@@ -227,8 +227,8 @@ class ChildGroup(
i := (+ i 1)
}
if self.shared {
sem-unsignal self.sigsem
system-uncatch-sig 17
core.sem-unsignal self.sigsem
sys.sig-uncatch sys.SIGCHLD
set shared false
}
}
+10 -2
View File
@@ -5,7 +5,8 @@ AM_CPPFLAGS = \
-I$(abs_builddir)/../lib \
-I$(abs_srcdir) \
-I$(abs_srcdir)/../lib \
-I$(includedir)
-I$(includedir) \
-DHAK_TEST_MODLIBDIRS='"@abs_top_builddir@/mod:@abs_top_builddir@/mod/.libs"'
LDADD = ../lib/libhak.la
check_SCRIPTS = \
@@ -19,15 +20,20 @@ check_SCRIPTS = \
hnd-03.hak \
insta-01.hak \
insta-02.hak \
mux-01.hak \
mux-02.hak \
mux-03.hak \
prim-01.hak \
proc-01.hak \
proclib-01.hak \
ret-01.hak \
retvar-01.hak \
sig-01.hak \
sig-02.hak \
sysproc-01.hak \
sysproc-02.hak \
tick-01.hak \
try-01.hak \
va-01.hak \
var-01.hak \
var-02.hak \
@@ -47,11 +53,13 @@ check_ERRORS = \
check_PROGRAMS = \
t-001 \
t-002 \
t-003
t-003 \
t-004
t_001_SOURCES = t-001.c tap.h
t_002_SOURCES = t-002.c tap.h
t_003_SOURCES = t-003.c tap.h
t_004_SOURCES = t-004.c tap.h
##noinst_SCRIPTS = $(check_SCRIPTS)
EXTRA_DIST = $(check_SCRIPTS) $(check_ERRORS)
+33 -5
View File
@@ -89,7 +89,8 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
check_PROGRAMS = t-001$(EXEEXT) t-002$(EXEEXT) t-003$(EXEEXT)
check_PROGRAMS = t-001$(EXEEXT) t-002$(EXEEXT) t-003$(EXEEXT) \
t-004$(EXEEXT)
subdir = t
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_sign.m4 \
@@ -120,6 +121,10 @@ am_t_003_OBJECTS = t-003.$(OBJEXT)
t_003_OBJECTS = $(am_t_003_OBJECTS)
t_003_LDADD = $(LDADD)
t_003_DEPENDENCIES = ../lib/libhak.la
am_t_004_OBJECTS = t-004.$(OBJEXT)
t_004_OBJECTS = $(am_t_004_OBJECTS)
t_004_LDADD = $(LDADD)
t_004_DEPENDENCIES = ../lib/libhak.la
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
@@ -136,7 +141,7 @@ DEFAULT_INCLUDES =
depcomp = $(SHELL) $(top_srcdir)/ac/depcomp
am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/t-001.Po ./$(DEPDIR)/t-002.Po \
./$(DEPDIR)/t-003.Po
./$(DEPDIR)/t-003.Po ./$(DEPDIR)/t-004.Po
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -156,8 +161,10 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(t_001_SOURCES) $(t_002_SOURCES) $(t_003_SOURCES)
DIST_SOURCES = $(t_001_SOURCES) $(t_002_SOURCES) $(t_003_SOURCES)
SOURCES = $(t_001_SOURCES) $(t_002_SOURCES) $(t_003_SOURCES) \
$(t_004_SOURCES)
DIST_SOURCES = $(t_001_SOURCES) $(t_002_SOURCES) $(t_003_SOURCES) \
$(t_004_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
@@ -543,7 +550,8 @@ AM_CPPFLAGS = \
-I$(abs_builddir)/../lib \
-I$(abs_srcdir) \
-I$(abs_srcdir)/../lib \
-I$(includedir)
-I$(includedir) \
-DHAK_TEST_MODLIBDIRS='"@abs_top_builddir@/mod:@abs_top_builddir@/mod/.libs"'
LDADD = ../lib/libhak.la
check_SCRIPTS = \
@@ -557,15 +565,20 @@ check_SCRIPTS = \
hnd-03.hak \
insta-01.hak \
insta-02.hak \
mux-01.hak \
mux-02.hak \
mux-03.hak \
prim-01.hak \
proc-01.hak \
proclib-01.hak \
ret-01.hak \
retvar-01.hak \
sig-01.hak \
sig-02.hak \
sysproc-01.hak \
sysproc-02.hak \
tick-01.hak \
try-01.hak \
va-01.hak \
var-01.hak \
var-02.hak \
@@ -585,6 +598,7 @@ check_ERRORS = \
t_001_SOURCES = t-001.c tap.h
t_002_SOURCES = t-002.c tap.h
t_003_SOURCES = t-003.c tap.h
t_004_SOURCES = t-004.c tap.h
EXTRA_DIST = $(check_SCRIPTS) $(check_ERRORS)
TESTS = $(check_PROGRAMS) $(check_SCRIPTS) $(check_ERRORS)
TEST_EXTENSIONS = .hak .err
@@ -643,6 +657,10 @@ t-003$(EXEEXT): $(t_003_OBJECTS) $(t_003_DEPENDENCIES) $(EXTRA_t_003_DEPENDENCIE
@rm -f t-003$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(t_003_OBJECTS) $(t_003_LDADD) $(LIBS)
t-004$(EXEEXT): $(t_004_OBJECTS) $(t_004_DEPENDENCIES) $(EXTRA_t_004_DEPENDENCIES)
@rm -f t-004$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(t_004_OBJECTS) $(t_004_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -652,6 +670,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-001.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-002.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-003.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-004.Po@am__quote@ # am--include-marker
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
@@ -928,6 +947,13 @@ t-003.log: t-003$(EXEEXT)
--log-file $$b.log --trs-file $$b.trs \
$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
"$$tst" $(AM_TESTS_FD_REDIRECT)
t-004.log: t-004$(EXEEXT)
@p='t-004$(EXEEXT)'; \
b='t-004'; \
$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
--log-file $$b.log --trs-file $$b.trs \
$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
"$$tst" $(AM_TESTS_FD_REDIRECT)
.hak.log:
@p='$<'; \
$(am__set_b); \
@@ -1038,6 +1064,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/t-001.Po
-rm -f ./$(DEPDIR)/t-002.Po
-rm -f ./$(DEPDIR)/t-003.Po
-rm -f ./$(DEPDIR)/t-004.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -1086,6 +1113,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/t-001.Po
-rm -f ./$(DEPDIR)/t-002.Po
-rm -f ./$(DEPDIR)/t-003.Po
-rm -f ./$(DEPDIR)/t-004.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
+17 -17
View File
@@ -7,21 +7,21 @@ fun chk(ok msg) {
else { printf "ERROR: %s\n" msg }
}
iosem := (sem-new)
tmo := (sem-new)
sg := (semgr-new)
semgr-add sg iosem
semgr-add sg tmo
fin := (sem-new)
iosem := (core.sem-new)
tmo := (core.sem-new)
sg := (core.semgr-new)
core.semgr-add sg iosem
core.semgr-add sg tmo
fin := (core.sem-new)
## returns 1 when the handle became readable, 0 when the timeout won
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
core.sem-signal tmo secs 0
core.sem-signal-on-input iosem h
s := (core.semgr-wait sg)
core.sem-unsignal iosem
core.sem-unsignal tmo
if (eqv? s tmo) { return 0 } \
else { return 1 }
}
@@ -46,12 +46,12 @@ fun reader() {
n := (sys.read r buf)
if (>= n 0) {
got := n
sem-signal fin
core.sem-signal fin
return 0
}
if (= (waitin r 5) 0) {
got := -1
sem-signal fin
core.sem-signal fin
return 0
}
}
@@ -63,7 +63,7 @@ fun writer() {
## so these ticks only happen if the VM is still scheduling
while (< ticks 4) {
ticks := (+ ticks 1)
yield
core.yield
}
wb := (core.basicNew ByteArray 2)
core.basicAtPut wb 0 120
@@ -71,9 +71,9 @@ fun writer() {
sys.write w wb
}
fork reader
fork writer
sem-wait fin
core.fork reader
core.fork writer
core.sem-wait fin
chk (= ticks 4) "other coprocesses ran while the reader was blocked"
chk (= got 2) "the reader woke and read the data"
+7 -13
View File
@@ -2,13 +2,13 @@
## stops hak code naming a descriptor it never opened - including hak's own
## multiplexer, signal and io-thread descriptors, and anything the host
## application that embeds hak holds open.
s := (sem-new)
sem-signal-on-input s 0 ##ERROR: system handle 0
s := (core.sem-new)
core.sem-signal-on-input s 0 ##ERROR: system handle 0
---
s := (sem-new)
sem-signal-on-input s 4 ##ERROR: system handle 4
s := (core.sem-new)
core.sem-signal-on-input s 4 ##ERROR: system handle 4
---
@@ -23,9 +23,9 @@ sys.read r (core.basicNew ByteArray 4) ##ERROR: system handle 0
## a regular file is never accepted by the multiplexer: epoll refuses one
## outright, and poll() would report it permanently ready
f := (sys.open "/proc/version" "r")
s := (sem-new)
sem-signal-on-input s f ##ERROR: not of an acceptable kind
f := (sys.open "/etc/passwd" "r")
s := (core.sem-new)
core.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
sys.open "/nonexistent/definitely-not-here" "r" ##ERROR: open /nonexistent/definitely-not-here
+1 -1
View File
@@ -56,7 +56,7 @@ a := (b:get-a)
if (a != 4) {printf "ERROR: a must be 4\n" } \
else { printf "OK %d\n" a }
c := (object-new A)
c := (core.object-new A)
a := (c:get-a)
if (a != nil) {printf "ERROR: a must be nil\n" } \
else { printf "OK %O\n" a }
+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 := (core.sem-new)
tmo := (core.sem-new)
sg := (core.semgr-new)
core.semgr-add sg iosem
core.semgr-add sg tmo
## 1 when the handle became readable, 0 when the timer won instead
fun waitin(h secs) {
| s |
core.sem-signal tmo secs 0
core.sem-signal-on-input iosem h
s := (core.semgr-wait sg)
core.sem-unsignal iosem
core.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 := (core.semgr-new)
sa := (core.sem-new)
sb := (core.sem-new)
sc := (core.sem-new)
tmo := (core.sem-new)
core.semgr-add sg sa
core.semgr-add sg sb
core.semgr-add sg sc
core.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
core.sem-signal-on-input sa ha
core.sem-signal-on-input sb hb
core.sem-signal-on-input sc hc
core.sem-signal tmo 9 0
fun expect(want h name) {
| s buf got |
s := (core.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"
core.sem-unsignal sa
expect sb hb "the second child woke its own semaphore after compaction"
core.sem-unsignal sb
expect sc hc "the third child woke its own semaphore after compaction"
core.sem-unsignal sc
core.sem-unsignal tmo
sys.pclose (core.basicAt pa 0)
sys.pclose (core.basicAt pb 0)
sys.pclose (core.basicAt pc 0)
+67
View File
@@ -0,0 +1,67 @@
## both directions bound to one descriptor, then one of them dropped
##
## Binding an input and an output semaphore to the same handle gives the VM a
## tuple with two directions. Unbinding one of them leaves the other in place,
## so delete_sem_from_sem_io_tuple() modifies the registration rather than
## removing it - vm_muxmod() instead of vm_muxdel(). Nothing else in the suite
## takes that path, so the per-direction event purge behind it is otherwise
## never executed.
##
## A pipe's write end is used because it is always writable, so the output
## semaphore has something to report; the input side is registered but never
## fires, which is fine - what matters is that both directions are registered.
fun chk(ok msg) {
if ok { printf "OK: %s\n" msg } \
else { printf "ERROR: %s\n" msg }
}
sg := (core.semgr-new)
isem := (core.sem-new)
osem := (core.sem-new)
tmo := (core.sem-new)
core.semgr-add sg isem
core.semgr-add sg osem
core.semgr-add sg tmo
p := (sys.pipe)
r := (core.basicAt p 0)
w := (core.basicAt p 1)
## one descriptor, two directions - the tuple now carries both
core.sem-signal-on-input isem w
core.sem-signal-on-output osem w
## drop only the input direction. the output registration must survive, and so
## must any event already reported for it
core.sem-unsignal isem
core.sem-signal tmo 5 0
s := (core.semgr-wait sg)
chk (eqv? s osem) "the surviving direction still reports after a partial unbind"
core.sem-unsignal tmo
## put the input direction back while the output one is still registered. this
## is the add branch of the modify path - the mirror of the drop above - and it
## has to leave the output registration alone.
core.sem-signal-on-input isem w
## now drop the output direction instead, keeping input. a write end is never
## readable, so with input alone nothing can report and the timer must win. if
## the modify path had left the output registration behind, its writability
## would be reported here and the timer would lose.
core.sem-unsignal osem
core.sem-signal tmo 0 300000000
s := (core.semgr-wait sg)
chk (eqv? s tmo) "the dropped direction stops reporting after a partial unbind"
core.sem-unsignal tmo
## and the full unbind from that state really removes the registration
core.sem-unsignal isem
core.sem-signal tmo 0 300000000
s := (core.semgr-wait sg)
chk (eqv? s tmo) "nothing reports once both directions are unbound"
core.sem-unsignal tmo
sys.close r
sys.close w
+14 -14
View File
@@ -8,11 +8,11 @@ fun loop1() {
while (< k 100) {
printf "loop1 => %d\n" k
k := (+ k 2)
yield
core.yield
}
z1 := k
sem-signal s1
core.sem-signal s1
}
fun loop2() {
@@ -22,26 +22,26 @@ fun loop2() {
while (< k 100) {
printf "loop2 => %d\n" k
k := (+ k 2)
yield
core.yield
}
z2 := k
sem-signal s2
core.sem-signal s2
}
s1 := (sem-new)
s2 := (sem-new)
s1 := (core.sem-new)
s2 := (core.sem-new)
p1 := (fork loop1)
p2 := (fork loop2)
p1 := (core.fork loop1)
p2 := (core.fork loop2)
##suspend p1
##suspend p2
##resume p1
##resume p2
##core.suspend p1
##core.suspend p2
##core.resume p1
##core.resume p2
sem-wait s1
sem-wait s2
core.sem-wait s1
core.sem-wait s2
if (== z1 101) { printf "OK: z1 is %d\n" z1 } \
else { printf "ERROR: z1 is not 101 - %d\n" z1 }
+2 -2
View File
@@ -22,10 +22,10 @@ ticks := 0
fun ticker() {
while (< ticks 5) {
ticks := (+ ticks 1)
yield
core.yield
}
}
fork ticker
core.fork ticker
order := (core.basicNew Array 3)
n := 0
+25 -25
View File
@@ -27,23 +27,23 @@ sys.close (core.basicAt q 0)
sys.close (core.basicAt q 1)
## --- catch and uncatch are idempotent ---
chk (= (system-catch-sig 10) 10) "system-catch-sig returns the signal number"
chk (= (system-catch-sig 10) 10) "catching an already caught signal is fine"
chk (= (system-uncatch-sig 10) 10) "system-uncatch-sig returns the signal number"
chk (= (system-uncatch-sig 10) 10) "uncatching an uncaught signal is fine"
chk (= (sys.sig-catch sys.SIGUSR1) sys.SIGUSR1) "sys.sig-catch returns the signal number"
chk (= (sys.sig-catch sys.SIGUSR1) sys.SIGUSR1) "catching an already caught signal is fine"
chk (= (sys.sig-uncatch sys.SIGUSR1) sys.SIGUSR1) "sys.sig-uncatch returns the signal number"
chk (= (sys.sig-uncatch sys.SIGUSR1) sys.SIGUSR1) "uncatching an uncaught signal is fine"
## --- a real signal reaches hak code, without stalling the coprocesses ---
## SIGCHLD is used because a child exiting is something this test can arrange
## on its own, with no outside help.
system-catch-sig 17
sys.sig-catch sys.SIGCHLD
h := (system-get-sigfd)
s := (sem-new)
tmo := (sem-new)
sg := (semgr-new)
semgr-add sg s
semgr-add sg tmo
fin := (sem-new)
h := (sys.sig-getfd)
s := (core.sem-new)
tmo := (core.sem-new)
sg := (core.semgr-new)
core.semgr-add sg s
core.semgr-add sg tmo
fin := (core.sem-new)
ticks := 0
signo := -1
@@ -52,14 +52,14 @@ proc := (core.basicAt pr 0)
fun waiter() {
| w |
sem-signal tmo 20 0
sem-signal-on-input s h
w := (semgr-wait sg)
sem-unsignal s
sem-unsignal tmo
core.sem-signal tmo 20 0
core.sem-signal-on-input s h
w := (core.semgr-wait sg)
core.sem-unsignal s
core.sem-unsignal tmo
if (eqv? w tmo) { signo := -2 } \
else { signo := (system-get-sig) }
sem-signal fin
else { signo := (sys.sig-get) }
core.sem-signal fin
return 0
}
@@ -67,16 +67,16 @@ fun ticker() {
## the waiter is parked on the signal descriptor by now
while (< ticks 4) {
ticks := (+ ticks 1)
yield
core.yield
}
}
fork waiter
fork ticker
sem-wait fin
core.fork waiter
core.fork ticker
core.sem-wait fin
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"
sys.pclose proc
system-uncatch-sig 17
sys.sig-uncatch 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 (== (sys.sig-catch sys.SIGUSR1) sys.SIGUSR1) "SIGUSR1 is catchable"
chk (== (sys.sig-uncatch sys.SIGUSR1) sys.SIGUSR1) "SIGUSR1 is uncatchable again"
raised := false
try { sys.sig-catch sys.SIGKILL } catch (e) { raised := true }
chk raised "SIGKILL is refused"
+10 -10
View File
@@ -1,41 +1,41 @@
## signals that cannot be caught
system-catch-sig 9 ##ERROR: 9 not routable
sys.sig-catch sys.SIGKILL ##ERROR: not routable
---
system-catch-sig 19 ##ERROR: 19 not routable
sys.sig-catch sys.SIGSTOP ##ERROR: not routable
---
## signals that indicate a crash: turning one into a byte on a pipe and
## carrying on would hide the fault rather than report it
system-catch-sig 11 ##ERROR: 11 not routable
sys.sig-catch sys.SIGSEGV ##ERROR: not routable
---
system-catch-sig 7 ##ERROR: 7 not routable
sys.sig-catch sys.SIGBUS ##ERROR: not routable
---
system-catch-sig 8 ##ERROR: 8 not routable
sys.sig-catch sys.SIGFPE ##ERROR: not routable
---
system-catch-sig 4 ##ERROR: 4 not routable
sys.sig-catch sys.SIGILL ##ERROR: not routable
---
## the timer signal hak itself uses to switch processes
system-catch-sig 26 ##ERROR: 26 not routable
sys.sig-catch sys.SIGVTALRM ##ERROR: not routable
---
system-catch-sig 0 ##ERROR: 0 not routable
sys.sig-catch 0 ##ERROR: 0 not routable
---
system-catch-sig 9999 ##ERROR: 9999 not routable
sys.sig-catch 9999 ##ERROR: 9999 not routable
---
system-catch-sig "two" ##ERROR: number not a small integer
sys.sig-catch "two" ##ERROR: number not a small integer
+27 -13
View File
@@ -5,19 +5,19 @@ fun chk(ok msg) {
else { printf "ERROR: %s\n" msg }
}
iosem := (sem-new)
tmo := (sem-new)
sg := (semgr-new)
semgr-add sg iosem
semgr-add sg tmo
iosem := (core.sem-new)
tmo := (core.sem-new)
sg := (core.semgr-new)
core.semgr-add sg iosem
core.semgr-add sg tmo
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
core.sem-signal tmo secs 0
core.sem-signal-on-input iosem h
s := (core.semgr-wait sg)
core.sem-unsignal iosem
core.sem-unsignal tmo
if (eqv? s tmo) { return 0 } else { return 1 }
}
@@ -38,9 +38,9 @@ fun reap(proc) {
while (< k 300) {
n := (sys.pwait proc)
if (not (= n 256)) { return n }
sem-signal tmo 0 20000000
semgr-wait sg
sem-unsignal tmo
core.sem-signal tmo 0 20000000
core.semgr-wait sg
core.sem-unsignal tmo
k := (+ k 1)
}
return 256
@@ -98,3 +98,17 @@ p := (sys.popen "sleep 60" "r")
outh := (core.basicAt p 2)
sys.pclose (core.basicAt p 0)
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)
+16 -16
View File
@@ -9,12 +9,12 @@ fun chk(ok msg) {
else { printf "ERROR: %s\n" msg }
}
iosem := (sem-new)
tmo := (sem-new)
sg := (semgr-new)
semgr-add sg iosem
semgr-add sg tmo
fin := (sem-new)
iosem := (core.sem-new)
tmo := (core.sem-new)
sg := (core.semgr-new)
core.semgr-add sg iosem
core.semgr-add sg tmo
fin := (core.sem-new)
p := (sys.popen "sleep 1; exit 5" "r")
proc := (core.basicAt p 0)
@@ -30,14 +30,14 @@ else {
fun waiter() {
| s |
sem-signal tmo 20 0
sem-signal-on-input iosem xh
s := (semgr-wait sg)
sem-unsignal iosem
sem-unsignal tmo
core.sem-signal tmo 20 0
core.sem-signal-on-input iosem xh
s := (core.semgr-wait sg)
core.sem-unsignal iosem
core.sem-unsignal tmo
if (eqv? s tmo) { status := -1 } \
else { status := (sys.pwait proc) }
sem-signal fin
core.sem-signal fin
return 0
}
@@ -46,14 +46,14 @@ else {
## happen if the VM went on scheduling instead of blocking
while (< ticks 4) {
ticks := (+ ticks 1)
yield
core.yield
}
}
chk (integer? xh) "sys.popen hands back an exit handle"
fork waiter
fork ticker
sem-wait fin
core.fork waiter
core.fork ticker
core.sem-wait fin
chk (= ticks 4) "other coprocesses ran while the child was alive"
chk (= status 5) "the exit handle woke the waiter and the status was read"
+10 -7
View File
@@ -199,12 +199,6 @@ static void termreq_round_trips (void)
void* during[NTERMREQ];
void* after[NTERMREQ];
int i;
for (i = 0; i < NTERMREQ; i++)
{
if (disposition_of(TERMREQ[i], &before[i]) <= -1) return; /* no sigaction */
}
#if defined(SIGPIPE)
/* SIGPIPE is deliberately NOT in the set above. It is not a termination
* request - it is the opposite, a measure against being terminated - and
@@ -213,7 +207,16 @@ static void termreq_round_trips (void)
* So termreq must leave it exactly alone. */
void* pipe_before;
void* pipe_during;
int pipe_probed = (disposition_of(SIGPIPE, &pipe_before) >= 0);
int pipe_probed;
#endif
for (i = 0; i < NTERMREQ; i++)
{
if (disposition_of(TERMREQ[i], &before[i]) <= -1) return; /* no sigaction */
}
#if defined(SIGPIPE)
pipe_probed = (disposition_of(SIGPIPE, &pipe_before) >= 0);
#endif
hak_catch_termreq();
+24 -6
View File
@@ -80,12 +80,12 @@ static void state_contract (void)
static const char SRC[] =
"flag := 0\n"
"s := (sem-new)\n"
"fun setter() { flag := 1 ; sem-signal s }\n"
"p := (fork setter)\n"
"s := (core.sem-new)\n"
"fun setter() { flag := 1 ; core.sem-signal s }\n"
"p := (core.fork setter)\n"
"i := 0\n"
"while (< i 300000) { if (== flag 1) { break } ; i := (+ i 1) }\n"
"sem-wait s\n"
"core.sem-wait s\n"
"r := i\n";
static hak_oow_t bc_seen = 0;
@@ -105,6 +105,22 @@ static int on_cnode (hak_t* hak, hak_cnode_t* obj)
return hak_compile(hak, obj, 0);
}
/* The script below reaches the process and semaphore primitives through the
* core module, which is where they are registered. A build configured with
* --enable-static-module links that module in and resolves it unaided, but one
* without it has to load the module from the build tree, and a bare
* hak_openstd() has nowhere to look. HAK_TEST_MODLIBDIRS comes from
* t/Makefile.am and names the same directories run.sh passes to the script
* tests via --modlibdirs. */
static int set_modlibdirs (hak_t* hak)
{
#if defined(HAK_TEST_MODLIBDIRS)
return hak_setoption(hak, HAK_OPT_MODLIBDIRS_BCSTR, HAK_TEST_MODLIBDIRS);
#else
return 0;
#endif
}
static void preempts_a_spinner (void)
{
hak_t* hak;
@@ -117,9 +133,11 @@ static void preempts_a_spinner (void)
OK (hak != HAK_NULL, "instantiation");
if (!hak) return;
hak_getoption(hak, HAK_TRAIT, &trait);
hak_getoption(hak, HAK_OPT_TRAIT, &trait);
trait |= HAK_TRAIT_AWAIT_PROCS | HAK_TRAIT_LANG_ENABLE_EOL;
hak_setoption(hak, HAK_TRAIT, &trait);
hak_setoption(hak, HAK_OPT_TRAIT, &trait);
OK (set_modlibdirs(hak) == 0, "module search path");
memset (&cb, 0, sizeof(cb));
cb.vm_checkbc = cb_checkbc;
+131
View File
@@ -0,0 +1,131 @@
/* process stack overflow must be reported, not swallowed.
*
* HAK_STACK_PUSH() detects the overflow, sets HAK_ESTKOVRFLW and stores -1 in
* hak->abort_req; the interpreter loop then takes "goto oops" and answers a
* failure. That path is only reachable while abort_req is a signed type - when
* it was hak_uint8_t the -1 became 255, the "abort_req < 0" test could never
* be true, and the overflow fell through to the ordinary "abort_req > 0" break
* instead. hak_execute() then answered success and the script simply stopped
* running with nothing reported.
*
* This is checked here rather than in t/ as a script because neither script
* harness can express it: run.sh fails a test as soon as the output contains
* an ERROR: line, and err.sh insists the error be reported at the line its
* ##ERROR: marker sits on, whereas a stack overflow carries no source
* location and is reported at [0,0]. Only a C test can assert on the value
* hak_execute() answers, which is the part that regressed.
*
* The nesting depth is what drives the operand stack: evaluating
* (+ 1 (+ 1 ... 0)) has to hold every pending left operand at once. The
* process stack size is set explicitly here because there is no single
* default to rely on - HAK_DFL_PROCSTK_SIZE is 5000, bin/hak asks for 600,
* and exec.c clamps whatever it is given up to a floor of 192. */
#include <hak.h>
#include "tap.h"
#include <string.h>
#include <stdlib.h>
/* slots per nesting level are an implementation detail, so keep a wide margin
* on both sides of the limit rather than probing for the exact threshold */
#define STK_SLOTS 192
#define DEEP_DEPTH 2000
#define SHALLOW_DEPTH 10
static int on_cnode (hak_t* hak, hak_cnode_t* obj)
{
return hak_compile(hak, obj, 0);
}
/* build "x := (+ 1 (+ 1 ... 0))" nested to the given depth */
static char* make_src (int depth)
{
hak_oow_t capa;
char* buf;
char* p;
int i;
capa = (hak_oow_t)depth * 8 + 64;
buf = (char*)malloc(capa);
if (!buf) return HAK_NULL;
p = buf;
memcpy(p, "x := ", 5); p += 5;
for (i = 0; i < depth; i++) { memcpy(p, "(+ 1 ", 5); p += 5; }
*p++ = '0';
for (i = 0; i < depth; i++) *p++ = ')';
*p++ = '\n';
*p = '\0';
return buf;
}
/* returns 0 if the run completed, -1 if the fixture itself could not be built */
static int run_at_depth (int depth, int expect_overflow)
{
hak_t* hak;
hak_bitmask_t trait;
hak_oow_t stksize;
hak_oop_t retv;
char* src;
int errnum;
int rc = -1;
src = make_src(depth);
OK (src != HAK_NULL, "source built");
if (!src) return -1;
hak = hak_openstd(0, HAK_NULL);
OK (hak != HAK_NULL, "instantiation");
if (!hak) goto done;
hak_getoption(hak, HAK_OPT_TRAIT, &trait);
trait |= HAK_TRAIT_LANG_ENABLE_EOL;
hak_setoption(hak, HAK_OPT_TRAIT, &trait);
stksize = STK_SLOTS;
OK (hak_setoption(hak, HAK_OPT_PROCSTK_SIZE, &stksize) == 0, "process stack size");
OK (hak_ignite(hak, 0) == 0, "ignition");
OK (hak_addbuiltinprims(hak) == 0, "builtin primitives");
OK (hak_attachcciostdwithbcstr(hak, HAK_NULL) == 0, "source input stream");
OK (hak_attachudiostdwithbcstr(hak, "", "") == 0, "user data streams");
OK (hak_beginfeed(hak, on_cnode) == 0, "begin feed");
OK (hak_feedbchars(hak, src, strlen(src)) == 0, "feed the script");
OK (hak_endfeed(hak) == 0, "end feed");
retv = hak_execute(hak);
errnum = (int)hak_geterrnum(hak);
if (expect_overflow)
{
printf("# depth=%d retv=%s errnum=%d (%s)\n", depth,
retv? "value": "HAK_NULL", errnum,
retv? "-": hak_geterrbmsg(hak));
/* the assertion that regressed: a swallowed overflow answers a value */
OK (retv == HAK_NULL, "an overflowing script fails rather than answering a value");
OK (errnum == HAK_ESTKOVRFLW, "the failure is reported as HAK_ESTKOVRFLW");
}
else
{
if (!retv) printf("# depth=%d unexpected failure: [%d] %s\n", depth, errnum, hak_geterrbmsg(hak));
OK (retv != HAK_NULL, "a script within the stack limit still runs");
}
rc = 0;
hak_close(hak);
done:
free(src);
return rc;
}
int main (int argc, char* argv[])
{
no_plan();
/* the control comes first: if this one failed the deep case would pass
* for the wrong reason, since any failure at all answers HAK_NULL */
run_at_depth(SHALLOW_DEPTH, 0);
run_at_depth(DEEP_DEPTH, 1);
return exit_status();
}
+2 -2
View File
@@ -21,7 +21,7 @@ fun setter() { flag := 1 }
## setter is runnable from here on, but it cannot run while this process holds
## the CPU - and this process never calls yield.
p := (fork setter)
p := (core.fork setter)
i := 0
while (< i 3000000) {
@@ -38,7 +38,7 @@ chk (== flag 1) "a process that never yields is preempted so another can run"
## process immediately: then setter would have run before the loop started and
## i would be ~0, proving nothing about preemption. One tick is 20ms, which is
## a great many iterations.
chk (> i 1000) "the switch came from the ticker, not from an eager fork"
chk (> i 1000) "the switch came from the ticker, not from an eager core.fork"
## The preempted process must be resumed, not abandoned - reaching here at all
## means the scheduler came back to it.
+202
View File
@@ -0,0 +1,202 @@
## Two related defects in how 'return' interacts with try/catch. Both are
## fixed; this checks both, because each hid the other while writing the test.
##
## 1. compile_return() emitted TRY_EXIT/CLASS_EXIT before the return value was
## compiled, so the value expression ran with every enclosing handler in the
## function already popped - not just the innermost one. The unwinding now
## comes from emit_return(), after the value is on the operand stack.
## Only the syntactic form used to matter: try { x := (f) } caught what f
## threw while try { return (f) } did not, so both forms appear below.
##
## 2. A 'return', 'break' or 'continue' inside a catch body emitted a TRY_EXIT
## for the very try whose handler it sat in - but 'throw' has already
## unwound that frame, so the second pop drove the exception stack below its
## base. HAK_EXSTACK_IS_EMPTY tests 'exsp <= st', so an underflowed stack
## reads as empty from then on and EVERY later try in the process reported
## 'exception not handled'. The catch body is now compiled with its try
## block marked in_catch, which suppresses the instruction.
##
## That corruption was never visible in the function that caused it, so the
## assertions that actually guard it are the ones exercising a try AFTER a
## catch that returned, broke or continued.
fun chk(ok msg) {
if ok { printf "OK: %s\n" msg } \
else { printf "ERROR: %s\n" msg }
}
fun boom() { throw 99 }
## ------------------------------------------------------------------
## 1. a throw inside a return value
## ------------------------------------------------------------------
fun c1() {
try { return (boom) } catch (e) { return (+ e 1) }
}
chk (= (c1) 100) "a throw in a return value is caught by the enclosing try"
## the catch need not return for the handler to run
seen := 0
fun c2() {
try { return (boom) } catch (e) { seen := e }
return 7
}
chk (= (c2) 7) "execution resumes after the try when the catch falls through"
chk (= seen 99) "the catch body ran"
fun c3() {
try { return (boom) } catch (e) { }
return 8
}
chk (= (c3) 8) "an empty catch body still handles the throw"
## nesting: the innermost handler wins and the outer one stays out of it.
## the old code unwound every enclosing try, so neither fired.
inner := 0
outer := 0
fun c4() {
try {
try { return (boom) } catch (e) { inner := (+ inner 1) }
} catch (e2) { outer := (+ outer 1) }
return 9
}
chk (= (c4) 9) "a nested try returns normally after handling"
chk (= inner 1) "the innermost try catches"
chk (= outer 0) "the outer try is left out of it"
## across a call boundary the callee's own handler wins; the old code let the
## throw escape to the caller's handler instead
g_seen := 0
h_seen := 0
fun g() { try { return (boom) } catch (e) { g_seen := 1 ; return -1 } }
fun h() { try { return (g) } catch (e) { h_seen := 1 ; return -2 } }
chk (= (h) -1) "the callee handles its own throw"
chk (= g_seen 1) "the callee's catch ran"
chk (= h_seen 0) "the caller's catch did not"
## the form that always worked must keep working
fun c5() {
| v |
v := 0
try { v := (boom) } catch (e) { v := e }
return v
}
chk (= (c5) 99) "a throw in an assignment is still caught"
## ------------------------------------------------------------------
## 2. leaving a catch body by return, break or continue
##
## each case is followed by a fresh try: that is where an underflowed
## exception stack shows up, never in the function that caused it.
## ------------------------------------------------------------------
## a try that must still work after the cases above, which all returned from
## inside a catch
fun still1() {
| v |
v := 0
try { v := (boom) } catch (e) { v := 21 }
return v
}
chk (= (still1) 21) "a try still works after earlier catch bodies returned"
## break out of a loop from inside a catch
bj := 0
while (< bj 9) {
bj := (+ bj 1)
try { y := (boom) } catch (e) { break }
}
chk (= bj 1) "break inside a catch leaves the loop"
fun still2() {
| v |
v := 0
try { v := (boom) } catch (e) { v := 22 }
return v
}
chk (= (still2) 22) "a try still works after a break from inside a catch"
## continue to the next iteration from inside a catch
ci := 0
ch := 0
while (< ci 3) {
ci := (+ ci 1)
try { z := (boom) } catch (e) { ch := (+ ch 1) ; continue }
ch := 100
}
chk (= ci 3) "continue inside a catch keeps iterating"
chk (= ch 3) "continue inside a catch skips the rest of the loop body"
fun still3() {
| v |
v := 0
try { v := (boom) } catch (e) { v := 23 }
return v
}
chk (= (still3) 23) "a try still works after a continue from inside a catch"
## the in_catch mark must suppress only the block whose handler we are in. the
## return below sits in the inner catch, so the inner frame is already gone,
## but the outer try is still live and has to be unwound on the way out. a
## frame left behind there would misdirect the next throw instead.
fun nest2() {
try {
try { w := (boom) } catch (e) { return 11 }
} catch (e2) { }
return 12
}
chk (= (nest2) 11) "a return from an inner catch still unwinds the outer try"
fun still4() {
| v |
v := 0
try { v := (boom) } catch (e) { v := 24 }
return v
}
chk (= (still4) 24) "a try still works after returning from a nested catch"
## ------------------------------------------------------------------
## 3. break and continue in a try BODY still unwind
##
## these share emit_ctlblk_unwind with return, and here the frame is live and
## must be popped. a leaked frame per iteration would exhaust the exception
## stack long before these loops finish.
## ------------------------------------------------------------------
i := 0
n := 0
while (< i 2000) {
i := (+ i 1)
try { if (= (mod i 2) 0) { continue } ; n := (+ n 1) } catch (e) { n := -1 }
}
chk (= n 1000) "continue inside a try body unwinds without leaking a handler"
i := 0
while (< i 2000) {
i := (+ i 1)
try { if (= i 7) { break } } catch (e) { }
}
chk (= i 7) "break inside a try body unwinds and leaves the loop"
## ------------------------------------------------------------------
## 4. a handler stays live across a whole call chain
## ------------------------------------------------------------------
hits := 0
caught := 0
fun rec(k) {
if (<= k 0) { throw 55 }
try { return (rec (- k 1)) } catch (e) { hits := (+ hits 1) ; caught := e ; return e }
}
chk (= (rec 20) 55) "recursion with a try per frame propagates the thrown value"
chk (= caught 55) "the thrown value arrives intact"
chk (= hits 1) "only the frame nearest the throw handles it"
fun still5() {
| v |
v := 0
try { v := (boom) } catch (e) { v := 25 }
return v
}
chk (= (still5) 25) "a try still works after deep try-per-frame recursion"