libtock-rs Exit implementation: one function with parameter or two functions

24 views
Skip to first unread message

Johnathan Van Why

unread,
May 19, 2021, 7:11:44 PM5/19/21
to Tock Embedded OS Development Discussion
Hi all,

The Tock 2.0 TRD specifies 2 types of Exit syscall: exit-terminate and exit-restart. Both variants take a single argument, a completion code to pass to the kernel. I'd like input on how to structure the Exit API in libtock-rs.

Idea 1: Single function

We can implement Exit as a single function that takes an ExitType argument:

enum ExitType { Terminate = 0, Restart = 1 }
fn exit(exit_type: ExitType, completion_code: u32) -> !;

Idea 2: Separate functions

Because these are considered different types of Exit syscall, the API that most directly represents the TRD uses two methods:

fn exit_terminate(completion_code: u32) -> !;
fn exit_restart(completion_code: u32) -> !;

Comparison

The advantage of having a single function for both Exit calls is that methods that might call Exit can have an ExitType argument:

fn might_exit(exit_type: ExitType) {
    let should_exit = /* stuff */;
    if should_exit {
        TockSyscalls::exit(exit_type, 0);
    }
}

whereas with two functions might_exit() would have to conditionally invoke the correct function. However, I don't expect this to be particularly useful.

The disadvantage of having a single function is it will be awkward to extend if a new Exit call is added. In particular, if an Exit call with a different signature is added, then we either have to overhaul the API (breaking backwards compatibility) or we end up with a weird API:

fn exit(exit_type: ExitType, completion_code: u32) -> !;
fn exit_fallible(code: u32) -> ErrorCode;  // New Exit call

Idea 2 should easily extend to support new Exit calls. It is also more consistent with Yield and Memop, which uses separate functions for each invocation type.

Question

Do you have a preference for one design over the other?

Thanks for any help,
Johnathan

Jett ✈ Rink

unread,
May 19, 2021, 7:14:35 PM5/19/21
to Johnathan Van Why, Tock Embedded OS Development Discussion
I have a slight preference for the single function with two parameters.

--
You received this message because you are subscribed to the Google Groups "Tock Embedded OS Development Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tock-dev+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/tock-dev/CAJqTQ1ikM7UVyWAz09PUmc19%3DP81C9G%3D7_bbcp_vR8Lm%2Bq-4Eg%40mail.gmail.com.

Hudson Randal Ayers

unread,
May 19, 2021, 7:26:37 PM5/19/21
to Johnathan Van Why, Jett ✈ Rink, Tock Embedded OS Development Discussion
I have a slight preference for two functions, for consistency with yield.

From: 'Jett ✈ Rink' via Tock Embedded OS Development Discussion <tock...@googlegroups.com>
Sent: Wednesday, May 19, 2021 4:14 PM
To: Johnathan Van Why <jrva...@google.com>
Cc: Tock Embedded OS Development Discussion <tock...@googlegroups.com>
Subject: Re: [tock-dev] libtock-rs Exit implementation: one function with parameter or two functions
 

Philip Levis

unread,
May 19, 2021, 11:07:16 PM5/19/21
to Hudson Randal Ayers, Johnathan Van Why, Jett ✈ Rink, Tock Embedded OS Development Discussion
I also prefer two functions, for consistency with yield and for the reason you mention. The cases in which you might want to invoke either exit seem small. I generally find have different functions, rather than functions keyed on parameters, easier to conceptually manage. E.g., the standard file system calls are much simpler to use than ioctl.

Phil


———————
Philip Levis (he/him)
Associate Professor, Computer Science and Electrical Engineering
Faculty Director, lab64 Maker Space
Stanford University
http://csl.stanford.edu/~pal

Amit Levy

unread,
May 20, 2021, 12:31:17 PM5/20/21
to Johnathan Van Why, Tock Embedded OS Development Discussion
"'Johnathan Van Why' via Tock Embedded OS Development Discussion"
<tock...@googlegroups.com> writes:

It seems to me that the Right(tm) way to do this is to have a single
function that takes arguments at the "low-level-library" level
(i.e., next to the "command" and "subscribe" implementations) and have
two functions that wrap it. Best of both worlds!

(In general, I have a preference in user space libraries for _first_
wrapping assembly in functions with very minimal abstraction, and
providing abstraction on top of those low-level wrappers. Makes it
easier to find "all the cases where the `command` syscall is invoked".)

Philip Levis

unread,
May 20, 2021, 12:37:12 PM5/20/21
to Amit Levy, Johnathan Van Why, Tock Embedded OS Development Discussion
You have more faith in compiler optimization than I do! From libtock-c:

void tock_exit(uint32_t completion_code) {
register uint32_t r0 asm ("r0") = 0; // Terminate
register uint32_t r1 asm ("r1") = completion_code;
asm volatile (
"svc 6"
:
: "r" (r0), "r" (r1)
: "memory");
__builtin_unreachable();
}


void tock_restart(uint32_t completion_code) {
register uint32_t r0 asm ("r0") = 1; // Restart
register uint32_t r1 asm ("r1") = completion_code;
asm volatile (
"svc 6"
:
: "r" (r0), "r" (r1)
: "memory");
__builtin_unreachable();
> --
> You received this message because you are subscribed to the Google Groups "Tock Embedded OS Development Discussion" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to tock-dev+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/tock-dev/87lf89z7si.fsf%40amitlevy.com.

———————

Johnathan Van Why

unread,
May 20, 2021, 2:00:30 PM5/20/21
to Amit Levy, Tock Embedded OS Development Discussion
Let's call Amit's idea "idea 3". I think it would look like the following:

enum ExitType { Terminate = 0, Restart = 1 }

// Exit function for use cases with a dynamically-determined exit type. exit() is
// more likely to change in the future than exit_terminate() or exit_restart(),
// so prefer those functions when you statically know exit_type.
fn exit(exit_type: ExitType, completion_code: u32) -> !;

fn exit_terminate(completion_code: u32) -> !;
fn exit_restart(completion_code: u32) -> !;

Does that seem like a better approach than either solution on its own?

Johnathan Van Why

unread,
May 20, 2021, 2:08:03 PM5/20/21
to Philip Levis, Amit Levy, Tock Embedded OS Development Discussion
Under the ARM and RISC-V C ABIs, tock_exit() and tock_restart() take the completion_code in the register the TRD calls R0. Those functions will then need to move that value into R1 and set R0 to a constant. On the other hand, if you have a single function with prototype

void exit(uint32_t exit_type, uint32_t completion_code)

then exit_type is passed via R0 and completion_code is passed via R1, so exit doesn't need to do any shuffling.

In practice, compiler optimization (namely inlining and register allocation) will remove the extra overhead of the register shuffling. Therefore, having two separate functions relies on compiler optimization more than a single function.

Philip Levis

unread,
May 20, 2021, 2:23:25 PM5/20/21
to Johnathan Van Why, Amit Levy, Tock Embedded OS Development Discussion
Sorry, I was too glib and terse. By relying on compiler optimization, I meant relying on the fact that the underlying 2-argument function would be inlined; the cost of the mov r1, r0 registers reshuffle is smaller than a function call.

Phil

Johnathan Van Why

unread,
May 20, 2021, 2:30:41 PM5/20/21
to Philip Levis, Amit Levy, Tock Embedded OS Development Discussion
I was just poking fun at the idea of "not trusting the compiler to inline tiny functions" in a Rust codebase. If rustc couldn't do simple inlining, then most of Rust's "zero cost abstractions" would be very expensive.

Philip Levis

unread,
May 20, 2021, 2:35:44 PM5/20/21
to Johnathan Van Why, Amit Levy, Tock Embedded OS Development Discussion
:) True! My poking at Rust codegen a little recently does support this 100%. Someday my knee-jerk mistrust of the compiler (ingrained from years of dealing with gcc) will subside!

Phil

Reply all
Reply to author
Forward
0 new messages