Compare commits
28
Commits
c21623e5a5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d17f1e708 | ||
|
|
f99a8c87f0 | ||
|
|
7be5d0fe61 | ||
|
|
baa01b22e3 | ||
|
|
58d1234020 | ||
|
|
bcdc7d8d36 | ||
|
|
1a8cc6ef43 | ||
|
|
15b42bc269 | ||
|
|
992b1344a8 | ||
|
|
27115ca792 | ||
|
|
05b7e3f728 | ||
|
|
e63dc2c2a0 | ||
|
|
92caa7e8db | ||
|
|
4b4238dc50 | ||
|
|
175e666954 | ||
|
|
586094ae8f | ||
|
|
de8c567a87 | ||
|
|
10051c7851 | ||
|
|
df07bd7eb2 | ||
|
|
e5ce0304f8 | ||
|
|
58d3eaa562 | ||
|
|
fdda6998f2 | ||
|
|
2cb4ddf0b3 | ||
|
|
f77ec1049d | ||
|
|
897e7477bc | ||
|
|
95246943e6 | ||
|
|
d5b4a1290f | ||
|
|
04e826a6db |
+2
-1
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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>]])
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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(>ick, 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 */
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
* =========================================================================*/
|
||||
|
||||
@@ -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 */
|
||||
/* ========================================================================= */
|
||||
|
||||
@@ -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 */
|
||||
/* ========================================================================= */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
+2
-2
@@ -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
@@ -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
@@ -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 } },
|
||||
};
|
||||
|
||||
|
||||
@@ -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 } }
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -11,21 +11,24 @@
|
||||
* the only way to reach hak_raisetick() - it has no script-level binding, and
|
||||
* driving it through a running VM cannot isolate it from the global tick.
|
||||
*
|
||||
* What this does NOT cover is the scheduler acting on a pending tick.
|
||||
* t/tick-01.hak covers that for the global half. */
|
||||
* The second half then drives it end to end: a script forks a process and
|
||||
* spins without ever yielding, and hak_raisetick() is called from the
|
||||
* vm_checkbc callback - ordinary context, where an embedder would call it.
|
||||
* The OS ticker is deliberately never started, so gtick never moves and the
|
||||
* global half plays no part; only the per-instance tick can break the spin.
|
||||
* t/tick-01.hak covers the global half. */
|
||||
|
||||
#include <hak.h>
|
||||
#include "tap.h"
|
||||
#include <string.h>
|
||||
|
||||
int main (int argc, char* argv[])
|
||||
static void state_contract (void)
|
||||
{
|
||||
hak_t* hak;
|
||||
|
||||
no_plan();
|
||||
|
||||
hak = hak_openstd(0, HAK_NULL);
|
||||
OK (hak != HAK_NULL, "instantiation");
|
||||
if (!hak) return exit_status();
|
||||
if (!hak) return;
|
||||
|
||||
/* enabling reception seeds the watermark, so an instance does not act on
|
||||
* ticks raised before it was listening */
|
||||
@@ -60,5 +63,115 @@ int main (int argc, char* argv[])
|
||||
OK (hak->rcv_tick == 1, "reception is on");
|
||||
|
||||
hak_close(hak);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* The spinner breaks out as soon as the forked process sets the flag, so a
|
||||
* count below the bound means the tick was delivered and acted on.
|
||||
*
|
||||
* hak_execute() answers the value of whichever process finishes LAST, so the
|
||||
* sem-wait after the loop is load-bearing: it makes the main process finish
|
||||
* last in both outcomes, and the returned value is therefore always its own
|
||||
* counter rather than the forked process's. Without it, a run in which nothing
|
||||
* preempted the spinner would answer the setter's value and read as a pass. */
|
||||
#define SPIN_BOUND 300000
|
||||
#define BC_BEFORE_RAISE 20000
|
||||
|
||||
static const char SRC[] =
|
||||
"flag := 0\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"
|
||||
"core.sem-wait s\n"
|
||||
"r := i\n";
|
||||
|
||||
static hak_oow_t bc_seen = 0;
|
||||
static int raised = 0;
|
||||
|
||||
static void cb_checkbc (hak_t* hak, hak_oob_t bcode)
|
||||
{
|
||||
if (!raised && ++bc_seen >= BC_BEFORE_RAISE)
|
||||
{
|
||||
raised = 1;
|
||||
hak_raisetick(hak);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
hak_cb_t cb;
|
||||
hak_bitmask_t trait;
|
||||
hak_oop_t retv;
|
||||
hak_ooi_t iters = -1;
|
||||
|
||||
hak = hak_openstd(0, HAK_NULL);
|
||||
OK (hak != HAK_NULL, "instantiation");
|
||||
if (!hak) return;
|
||||
|
||||
hak_getoption(hak, HAK_OPT_TRAIT, &trait);
|
||||
trait |= HAK_TRAIT_AWAIT_PROCS | HAK_TRAIT_LANG_ENABLE_EOL;
|
||||
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;
|
||||
OK (hak_regcb(hak, &cb) != HAK_NULL, "callback registration");
|
||||
|
||||
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");
|
||||
|
||||
hak_rcvtick(hak, 1); /* without this every tick is ignored */
|
||||
|
||||
retv = hak_execute(hak);
|
||||
if (!retv) printf("# execute failed: [%d] %s\n", (int)hak_geterrnum(hak), hak_geterrbmsg(hak));
|
||||
OK (retv != HAK_NULL, "execution");
|
||||
if (retv && HAK_OOP_IS_SMOOI(retv)) iters = HAK_OOP_TO_SMOOI(retv);
|
||||
|
||||
printf("# raised=%d iterations=%ld (bound %d)\n", raised, (long)iters, SPIN_BOUND);
|
||||
|
||||
OK (raised == 1, "the callback reached the raise");
|
||||
OK (iters >= 0 && iters < SPIN_BOUND,
|
||||
"hak_raisetick preempts a process that never yields");
|
||||
OK (iters > 100, "the switch came from the tick, not from an eager fork");
|
||||
|
||||
hak_close(hak);
|
||||
}
|
||||
|
||||
int main (int argc, char* argv[])
|
||||
{
|
||||
no_plan();
|
||||
state_contract();
|
||||
preempts_a_spinner();
|
||||
return exit_status();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
Reference in New Issue
Block a user