Even OS threads have had a lot of work, with glibc clone() even being used for goroutines in some systems.
Green threads are cooperative multi threading and the traditional style doesn't support multiple cores well but are nice if core local context switching is what you hit.
Coroutines are often about allowing programmers to use familiar loop constructs, while await often allows familiar blocking style programming.
They all have tradeoffs.
Modern COBOL compilers with Concurrent loops often uses polyhedral compilation, which tends to have better cache locality and can often be much faster than any of the above even using pthreads.
The epoll(2) API was created because poll(2) performance problems. Traditional polling system calls such as poll(2) and select(2) simply didn't scale.
Event queue dramatically improved performance when they were introduced, and really have nothing to do with userland treading.
It is all horses for courses, and while cooperative multi threading has a use case, it wasn't wasted effort to move from polling to event queuing.
And perhaps look into the deincrementing index registers of the IBM 705. The modern explanation is that Fortran indexes start at one, but historically they were limits, not starting address IMHO.
I am not sure that flang found a way to free local memory when it went out of scope, But if you think about the implications it may help.
GFORTRAN uses cray pointers, which also track a pointee
Good luck with your language.