It is needed to port language runtime with coroutine support. Guard pages are required to handle stack overflow (runtime system can handle stack overflows and resume execution, stack memory is allocated with page probing to prevent bypassing guard page).
Kernel use create_area_etc (usage when creating thread stack), that is not available from userland.
Also it will be nice to have ability to change thread stack area in kernel.
You can just do it “manually”, i.e. by mmap’ing an area before or after the stack area with PROT_NONE, which should work on all POSIX OSes, right?
You can use mprotect() with PROT_NONE to protect a memory page (including one that’s in your stack area) and you can use pthread_attr_setstack to set a manually allocated stack for a specific thread (only when creating it, not after it has started running).
I think it should be possible with these two and mmap to:
Reserve some address space for your stack
Use pthread_attr_setstack to point the thread to it
Use mprotect to setup your guard page
Use mmap to populate the stack as needed with actual memory
So, indeed this should be pretty close to what’s available on any POSIX OS.
This creates an address space reservation that you can latter fill in by calling mmap again with PROT_READ | PROT_WRITE and removing the MAP_NORESERVE flag
Also what B_STACK_AREA protection means? Is it needed for custom stacks? I tried to set it, it was displayed in SystemManager, but it seems to have no effect.
In src/system/kernel/vm/vm.cpp it seems to be used only for ensuring a minimum size for the area of two pages (the minimum required to have an usable page and a guard page).
It is probably best for languages to just implement their own coroutine-switching functions, or use a library like libco for it (one of the backends uses POSIX setjmp/longjmp and thus should be architecture-independent.)
It is currently not possible to change thread stack range (see thread_infostack_base/stack_end) and stack area for running thread. Program may want to exit and delete coroutine that it using default thread stack. Windows fiber API support registering stack virtual memory and stack range in TEB so it behave exactly same as default thread stack and stacks can be deleted including default one.