You might be able to mimic fork on Windows. I recall that Winnie-AFL has a forklib for doing just that.
I also put together some of the experiments I did and created the subprocess.f. At the moment, only the synchronous version is implemented but that may give some ideas and provide a simple sandbox for testing purposes.
While thinking a bit more about the async version, it could be interesting to have the possibility to provide a callback function that would be called when the process ends
call runasync(..., callback)
...
subroutine callback(exit_state)
integer, intent(in) :: exit_state
if (exit_state /= 0) print*, "Oops, something went wrong"
end subroutine
As for the stdin and stdout/stderr, one could also use procedures as arguments. This may provide an easy way for piping and facilitate the use of a logger to redirect to output to something else than the console. If you are familiar with C#, you can have a look at CliWrap. There API is written in an expressive object oriented style that is easy to follow.