Split `libtock_unittest::fake::Kernel` into multiple pieces.
jrvanwhy opened this issue · comments
libtock_unittest::fake::Kernel
serves at least 3 roles:
- It is stored in thread-local storage and contains the data used by the fake kernel.
- It is the handle used by unit tests to control the fake kernel's lifetime (well,
Rc<fake::Kernel>
is) and to interact with the kernel directly (e.g. add drivers or examine the syscall log) - It implements
libtock_platform::Syscalls
.
This results in some awkward code. yield_wait
can invoke an upcall that calls command
, which means the implementation of libtock_platform::Syscalls
has to be reentrant. That reentrance is easier to comprehend when you think of fake::Kernel
as a data-only type, but the API it exposes to unit tests is an object-oriented API.
The reentrance gets more awkward when you add upcalls:
- A system call is invoked
- The system call routes the call to the corresponding
fake::Driver
(using the thread-localfake::Kernel
instance). - The
fake::Driver
queues anUpcall
. - The
Upcall
instance fetches the thread-localfake::Kernel
to add itself to the upcall queue.
Instead, I think fake::Kernel
should be split into 3 pieces:
kernel::Data
is the data stored in thread-local storage. It is stored inside aRefCell
, and borrowed byfake::Kernel
,fake::Syscalls
, andUpcall
only as long as necessary to handle an operation. It is not visible to code outsidelibtock_unittest
.fake::Kernel
is the handle used by the unit test to access the kernel. It would be a zero-sized type, and would manipulate the thread-localkernel::Data
.fake::Syscalls
implementslibtock_platform::Syscalls
.
I'm not performing this refactoring yet because I don't want to slow down my feature development. Instead, I'm opening this issue to remind myself to perform this refactoring later.