diff --git a/base/builtin/env.c b/base/builtin/env.c index f0fa24485..bc79df8d8 100644 --- a/base/builtin/env.c +++ b/base/builtin/env.c @@ -37,14 +37,19 @@ extern char rts_exit; extern int return_val; -// Env ///////////////////////////////////////////////////////////////////////// +// Stdio /////////////////////////////////////////////////////////////////////// -$R B_EnvD_stdout_writeG_local (B_Env self, $Cont c$cont, B_str s) { +$R B_StdioD_outG_local (B_Stdio self, $Cont c$cont, B_str s) { printf("%s", s->str); return $R_CONT(c$cont, B_None); } -$R B_EnvD_set_stdinG_local (B_Env self, $Cont c$cont, B_bool canonical, B_bool echo) { +$R B_StdioD_errG_local (B_Stdio self, $Cont c$cont, B_str s) { + fprintf(stderr, "%s", s->str); + return $R_CONT(c$cont, B_None); +} + +$R B_StdioD_set_stdinG_local (B_Stdio self, $Cont c$cont, B_bool canonical, B_bool echo) { #if defined(_WIN32) || defined(_WIN64) #else struct termios attr; @@ -84,7 +89,7 @@ void read_stdin(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { } } -$R B_EnvD__on_stdin_bytesG_local (B_Env self, $Cont c$cont, $action cb) { +$R B_StdioD__on_stdin_bytesG_local (B_Stdio self, $Cont c$cont, $action cb) { // This should be the only call in env that does IO stuff, so it is safe to // pin affinity here (and not earlier).. pin_actor_affinity(); @@ -95,6 +100,9 @@ void read_stdin(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { return $R_CONT(c$cont, B_None); } + +// Env ///////////////////////////////////////////////////////////////////////// + $R B_EnvD_exitG_local (B_Env self, $Cont c$cont, B_int n) { return_val = fromB_int(n); rts_shutdown(); @@ -102,15 +110,9 @@ void read_stdin(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { } -B_Env B_EnvG_newactor(B_WorldCap wc, B_SysCap sc, B_list args) { +B_Env B_EnvG_newactor() { B_Env $tmp = $NEWACTOR(B_Env); - $tmp->cap = wc; - $tmp->args = args; - $tmp->syscap = sc; - $tmp->auth = $tmp->cap; - $tmp->argv = $tmp->args; $tmp->$affinity = 0; // hard-coded to special worker on the main thread - serialize_state_shortcut(($Actor)$tmp); return $tmp; } @@ -138,3 +140,14 @@ B_NoneType B_WorldCapD___init__ (B_WorldCap self) { return B_None; } + +B_EnvCap B_EnvCapG_new() { + B_EnvCap $tmp = acton_malloc(sizeof(struct B_EnvCap)); + $tmp->$class = &B_EnvCapG_methods; + // B_EnvCapG_methods.__init__($tmp); + return $tmp; +} + +B_NoneType B_EnvCapD___init__ (B_EnvCap self) { + return B_None; +} diff --git a/base/builtin/env.h b/base/builtin/env.h index 990fee396..e28f087be 100644 --- a/base/builtin/env.h +++ b/base/builtin/env.h @@ -1,9 +1,12 @@ #pragma once -B_Env B_EnvG_newactor (B_WorldCap, B_SysCap, B_list); +B_Env B_EnvG_newactor (); B_SysCap B_SysCapG_new(); B_NoneType B_SysCapD___init__ (B_SysCap self); B_WorldCap B_WorldCapG_new(); B_NoneType B_WorldCapD___init__ (B_WorldCap self); + +B_EnvCap B_EnvCapG_new(); +B_NoneType B_EnvCapD___init__ (B_EnvCap self); diff --git a/base/rts/rts.c b/base/rts/rts.c index 1ac8d2d4f..48be95cf9 100644 --- a/base/rts/rts.c +++ b/base/rts/rts.c @@ -719,6 +719,7 @@ struct $Cont $Fail$instance = { &$FailG_methods }; //////////////////////////////////////////////////////////////////////////////////////// + $R $InitRootD___call__ ($Cont $this, $WORD val) { typedef $R(*ROOT__init__t)($Actor, $Cont, B_Env); // Assumed type of the ROOT actor's __init__ method return ((ROOT__init__t)root_actor->$class->__init__)(root_actor, ($Cont)val, env_actor); @@ -739,6 +740,56 @@ struct $ContG_class $InitRootG_methods = { struct $Cont $InitRoot$cont = { &$InitRootG_methods }; + +$R $StartRootD___call__ ($Cont $this, $WORD val) { + // Env.__init__ has completed here; now preserve Env and start root init. + serialize_state_shortcut(($Actor)env_actor); + $ASYNC(root_actor, &$InitRoot$cont); + return $R_DONE(val); +} + +struct $ContG_class $StartRootG_methods = { + "$StartRoot", + UNASSIGNED, + NULL, + $ContD___init__, + $ContD___serialize__, + $ContD___deserialize__, + $ContD___bool__, + $ContD___str__, + $ContD___str__, + $StartRootD___call__ +}; +struct $Cont $StartRoot$cont = { + &$StartRootG_methods +}; + +$R $InitEnvD___call__ ($Cont $this, $WORD val) { + typedef $R(*ENV__init__t)(B_Env, $Cont, B_WorldCap, B_SysCap, B_list, B_int, B_EnvCap); + B_tuple init = (B_tuple)val; + B_WorldCap wc = (B_WorldCap)init->components[0]; + B_SysCap sc = (B_SysCap)init->components[1]; + B_list args = (B_list)init->components[2]; + B_int nr_wthreads = (B_int)init->components[3]; + B_EnvCap envcap = (B_EnvCap)init->components[4]; + return ((ENV__init__t)env_actor->$class->__init__)(env_actor, &$StartRoot$cont, wc, sc, args, nr_wthreads, envcap); +} + +struct $ContG_class $InitEnvG_methods = { + "$InitEnv", + UNASSIGNED, + NULL, + $ContD___init__, + $ContD___serialize__, + $ContD___deserialize__, + $ContD___bool__, + $ContD___str__, + $ContD___str__, + $InitEnvD___call__ +}; +struct $Cont $InitEnv$cont = { + &$InitEnvG_methods +}; //////////////////////////////////////////////////////////////////////////////////////// #ifdef ACTON_DB @@ -1402,25 +1453,32 @@ void BOOTSTRAP(int argc, char *argv[]) { for (int i=0; i< argc; i++) wit->$class->append(wit,args,to$str(argv[i])); - env_actor = B_EnvG_newactor(B_WorldCapG_new(), B_SysCapG_new(), args); - env_actor->nr_wthreads = toB_int(num_wthreads); - + env_actor = B_EnvG_newactor(); root_actor = $ROOT(); // Assumed to return $NEWACTOR(X) for the selected root actor X + + // Bootstrap targets Env first. $InitEnv runs Env.__init__ with + // $StartRoot$cont, which schedules root init after Env is initialized. + B_tuple env_init = $NEWTUPLE(5, + B_WorldCapG_new(), + B_SysCapG_new(), + args, + toB_int(num_wthreads), + B_EnvCapG_new()); time_t now = current_time(); - B_Msg m = B_MsgG_newXX(root_actor, &$InitRoot$cont, now, &$Done$instance); + B_Msg m = B_MsgG_newXX(($Actor)env_actor, &$InitEnv$cont, now, env_init); #ifdef ACTON_DB if (db) { int ret = 0, minority_status = 0; while(!rts_exit) { - ret = remote_enqueue_in_txn(($WORD*)&m->$globkey, 1, NULL, 0, MSG_QUEUE, (WORD)root_actor->$globkey, &minority_status, NULL, db); - rtsd_printf(" # enqueue bootstrap msg %ld to root actor queue %ld returns %d, minority_status %d", m->$globkey, root_actor->$globkey, ret, minority_status); - if(!handle_status_and_schema_mismatch(ret, minority_status, root_actor->$globkey)) + ret = remote_enqueue_in_txn(($WORD*)&m->$globkey, 1, NULL, 0, MSG_QUEUE, (WORD)env_actor->$globkey, &minority_status, NULL, db); + rtsd_printf(" # enqueue bootstrap msg %ld to env actor queue %ld returns %d, minority_status %d", m->$globkey, env_actor->$globkey, ret, minority_status); + if(!handle_status_and_schema_mismatch(ret, minority_status, env_actor->$globkey)) break; } } #endif - if (ENQ_msg(m, root_actor)) { - ENQ_ready(root_actor); + if (ENQ_msg(m, ($Actor)env_actor)) { + ENQ_ready(($Actor)env_actor); } } @@ -1774,6 +1832,8 @@ void $register_rts () { $register(&$DoneG_methods); $register(&$InitRootG_methods); $register(&B_EnvG_methods); + $register(&$StartRootG_methods); + $register(&$InitEnvG_methods); } //////////////////////////////////////////////////////////////////////////////////////// diff --git a/base/src/__builtin__.act b/base/src/__builtin__.act index 2c328f917..1597c71f3 100644 --- a/base/src/__builtin__.act +++ b/base/src/__builtin__.act @@ -1122,13 +1122,12 @@ class SysCap(): """ pass -actor Env (wc: WorldCap, sc: SysCap, args: list[str]): - cap = wc - auth = wc - syscap = sc - argv = args - nr_wthreads: int = 0 +class EnvCap(): + """EnvCap authorizes construction of Env-owned service actors. + """ + pass +actor EnvVar(envcap: EnvCap): action def getenv(name: str) -> ?str: """Get the value of an environment variable""" res = getenvb(name.encode()) @@ -1156,7 +1155,11 @@ actor Env (wc: WorldCap, sc: SysCap, args: list[str]): """Unset an environment variable""" NotImplemented - action def stdout_write(s: str) -> None: +actor Stdio(envcap: EnvCap, vars: EnvVar): + action def out(s: str) -> None: + NotImplemented + + action def err(s: str) -> None: NotImplemented action def stdin_install(on_stdin: ?action(str) -> None, encoding: ?str=None, on_error: ?action(str, bytes) -> None, on_stdin_bytes: ?action(bytes) -> None) -> None: @@ -1174,7 +1177,7 @@ actor Env (wc: WorldCap, sc: SysCap, args: list[str]): # Default to utf-8 if we're unable to discover the encoding encoding = "utf-8" # Read encoding from the LANG environment variable - lang_env = getenv("LANG") + lang_env = vars.getenv("LANG") if lang_env is not None: try: encoding = lang_env.split(".")[1].lower() @@ -1195,5 +1198,54 @@ actor Env (wc: WorldCap, sc: SysCap, args: list[str]): action def _on_stdin_bytes(cb: action(bytes) -> None) -> None: NotImplemented +def _new_vars(envcap: EnvCap): + return EnvVar(envcap) + +actor Env (wc: WorldCap, sc: SysCap, args: list[str], wthreads: int, envcap: EnvCap): + cap = wc + auth = wc + syscap = sc + argv = args + nr_wthreads = wthreads + vars = _new_vars(envcap) + + action def getenv(name: str) -> ?str: + """Get the value of an environment variable""" + return vars.getenv(name) + + action def getenvb(name: bytes) -> ?bytes: + """Get the value of an environment variable""" + return vars.getenvb(name) + + action def setenv(n: str, v: str) -> None: + """Set the value of an environment variable""" + vars.setenv(n, v) + + action def setenvb(n: bytes, v: bytes) -> None: + """Set the value of an environment variable""" + vars.setenvb(n, v) + + action def unsetenv(n: str) -> None: + """Unset an environment variable""" + vars.unsetenv(n) + + action def unsetenvb(n: bytes) -> None: + """Unset an environment variable""" + vars.unsetenvb(n) + + stdio = Stdio(envcap, vars) + + action def stdout_write(s: str) -> None: + stdio.out(s) + + action def stdin_install(on_stdin: ?action(str) -> None, encoding: ?str=None, on_error: ?action(str, bytes) -> None, on_stdin_bytes: ?action(bytes) -> None) -> None: + stdio.stdin_install(on_stdin, encoding, on_error, on_stdin_bytes) + + action def set_stdin(canonical: ?bool, echo: ?bool) -> None: + stdio.set_stdin(canonical, echo) + + action def is_tty() -> bool: + return stdio.is_tty() + action def exit(n: int): NotImplemented diff --git a/base/src/__builtin__.ext.c b/base/src/__builtin__.ext.c index 353e44773..22dba533f 100644 --- a/base/src/__builtin__.ext.c +++ b/base/src/__builtin__.ext.c @@ -29,8 +29,8 @@ B_str B_type(B_value a) { return to$str("None"); } -$R B_EnvD_getenvbG_local (B_Env self, $Cont C_cont, B_bytes name) { - // uv_os_getenv is not threadsafe but our Env actor forces serial execution +$R B_EnvVarD_getenvbG_local (B_EnvVar self, $Cont C_cont, B_bytes name) { + // uv_os_getenv is not threadsafe but our EnvVar actor forces serial execution // Try to use a small fixed size buffer size_t len = 256; @@ -55,7 +55,7 @@ B_str B_type(B_value a) { return $R_CONT(C_cont, to$bytes(value)); } -$R B_EnvD_setenvbG_local (B_Env self, $Cont C_cont, B_bytes name, B_bytes value) { +$R B_EnvVarD_setenvbG_local (B_EnvVar self, $Cont C_cont, B_bytes name, B_bytes value) { const char* env_var = fromB_bytes(name); const char* env_val = fromB_bytes(value); int r = uv_os_setenv(env_var, env_val); @@ -65,7 +65,7 @@ B_str B_type(B_value a) { return $R_CONT(C_cont, B_None); } -$R B_EnvD_unsetenvbG_local (B_Env self, $Cont C_cont, B_bytes name) { +$R B_EnvVarD_unsetenvbG_local (B_EnvVar self, $Cont C_cont, B_bytes name) { const char* env_var = fromB_bytes(name); int r = uv_os_unsetenv(env_var); if (r < 0) { @@ -75,7 +75,7 @@ B_str B_type(B_value a) { } // action def is_tty() -> bool: -$R B_EnvD_is_ttyG_local (B_Env self, $Cont C_cont) { +$R B_StdioD_is_ttyG_local (B_Stdio self, $Cont C_cont) { return $R_CONT(C_cont, toB_bool(isatty(1))); } diff --git a/compiler/acton/test.hs b/compiler/acton/test.hs index bee2518de..136f938db 100644 --- a/compiler/acton/test.hs +++ b/compiler/acton/test.hs @@ -1129,6 +1129,9 @@ rtsTests = (returnCode, cmdOut, cmdErr) <- runThing "--rts-wthreads" "../../test/rts/argv7.act" assertEqual "RTS wthreads error retCode" (ExitFailure 1) returnCode assertEqual "RTS wthreads error cmdErr" "ERROR: --rts-wthreads requires an argument.\n" cmdErr + + , testCase "Env init starts root actor with initialized Env and service actors" $ do + testBuildAndRun "" "--rts-wthreads=7" ExitSuccess False "../../test/rts/env_init.act" ] stdlibTests = diff --git a/test/core_lang_auto/envcap__bf.act b/test/core_lang_auto/envcap__bf.act new file mode 100644 index 000000000..f0415b3c3 --- /dev/null +++ b/test/core_lang_auto/envcap__bf.act @@ -0,0 +1,4 @@ +# It should not be possible to forge EnvCap to construct Env-owned services. + +actor main(env): + stdio = Stdio(EnvCap(), env.vars) diff --git a/test/rts/env_init.act b/test/rts/env_init.act new file mode 100644 index 000000000..af4594e78 --- /dev/null +++ b/test/rts/env_init.act @@ -0,0 +1,14 @@ +actor main(env): + # main only runs if the Env bootstrap continuation starts root init. + env.vars.setenv("ACTON_ENV_INIT_TEST", "ok") + env.stdio.out("") + env.stdio.err("") + if env.nr_wthreads != 7: + env.exit(1) + elif env.vars.getenv("ACTON_ENV_INIT_TEST") != "ok": + env.exit(1) + elif env.getenv("ACTON_ENV_INIT_TEST") != "ok": + env.exit(1) + else: + env.vars.unsetenv("ACTON_ENV_INIT_TEST") + env.exit(0)