
Green Threads Explained - terminalcommand
https://c9x.me/articles/gthreads/intro.html
======
theprotocol
I enjoyed the expedient writing style. The author did a great job of
expressing the essential knowledge without too much verbosity. It's very clear
what the skeleton of the project consists of, and the known unknowns are
labeled (i.e. "this is how this works, you can read more later [here]").

Some may find this directness unempathetic, but I consider it as the opposite:
the writing seems precisely aware of exactly what the reader wants to know at
any given time and what questions he or she would be likely to have on their
mind.

~~~
afandian
Curious about which bit someone might find unempathetic? It all seems pretty
straightforward to me.

~~~
theprotocol
I just meant it subjectively. I assume some might not like this expedient
style as a matter of taste, because most publications/articles/tutorials are
not written so concisely.

------
stygiansonic
Related: Early (almost prehistoric by software timeline standards) versions of
Java used a green thread implementation, but this was dropped in Java 1.1 in
favour of the current native OS thread model. Some background:

[https://softwareengineering.stackexchange.com/questions/1203...](https://softwareengineering.stackexchange.com/questions/120384/why-
not-green-threads)

~~~
marcosdumay
Java had a quite lousy implementation of green threads, that hurt performance
more than enabled parallelism. (What isn't explainable by it being early,
since there were earlier green threads implementation that didn't suck. Looks
like it just wasn't a priority for Java developers.)

There's nothing anywhere restricting green threads to a single OS thread. Most
modern runtimes will automatically multiplex the green threads into as many OS
threads as your computer can run.

~~~
dragonwriter
> Java had a quite lousy implementation of green threads, that hurt
> performance more than enabled parallelism

1:N green threads (which Java had) aren't intended for parallelism and provide
none. They provide concurrency only.

M:N green threads (e.g., Erlang processes) provide parallelism.

~~~
DamonHD
Java had a mixed model where it could use LWPs or kernel threads, and switched
over around Solaris 8 because things like stat() were still blocking and had
no async counterpart, so were difficult to deal with.

I dug up this fossil the other day, trying to get a fix from Sun for this very
issue:

[http://d.hd.org/JavaThreadsCommentToSun19980820.html](http://d.hd.org/JavaThreadsCommentToSun19980820.html)

~~~
ridiculous_fish
Is this solved in Go? A while back Go required programmers to use rate
limiters, otherwise it would spawn unlimited kernel threads for blocking
syscalls.

~~~
4ad
It's capped at 10000 threads (configurable). Note that Go uses asynchronous
I/O where possible.

------
api
It's occurred to me that asynchronous programming is essentially just very
lightweight green threads with the added benefit of scheduling tied directly
to I/O.

Synchronous programming is typically much more natural. It's too bad no
languages have opted to abstract this away. Of course I suppose coroutines are
kind of that.

~~~
reddit_clone
Erlang does that. The logic inside erlang processes look synchronous but
outwardly its all async.

For example you can send a message to a different process, even running on a
different machine and wait for the reply right there in the next line. It
doesn't hold any OS threads.

~~~
he0001
Intriguing thought, async to what?

If a program needs the input before continuing doesn't it need to wait and
therefore hold the program flow and therefore stop, even in erlang?

~~~
reddit_clone
>If a program needs the input before continuing doesn't it need to wait

Erlang applications are developed using message passing Actors which are
implemented as very light weight processes/green-threads.

So your process that sent a message can wait for the reply synchronously. It
doesn't hold anything up in the overall application.

You can have hundred of thousands of these processes running in a node.

Do check it out. It is very liberating. Why it is not vastly more popular is a
mystery to me.

~~~
he0001
While it's possible to have thousands of these processes running, it's
completely up to the problem domain for it to be useful. If it wouldn't be
bound by the problem domain you wouldn't need to write actors either.

------
spc476
I recently did a similar thing [1]. The major difference I did was to store
the registers on the stack instead of a separate structure. The stack _is_ the
thread structure.

I also have a version for x86-32 bit and x86-64 bit (and they work under Linux
and Mac OS-X). The assembly code isn't much, but it took a while to work out
just what was needed and no more.

[1]
[http://boston.conman.org/2017/02/27.1](http://boston.conman.org/2017/02/27.1)

------
ndesaulniers
In the code listing on the 4th page [0], why is MaxGThreads and StackSize
wrapped in an enum? I would use an enum when I might want some values to be
mutually exclusive. Seems like these values aren't even used in the same
context? I would use static const ints.

[0]
[https://c9x.me/articles/gthreads/code0.html](https://c9x.me/articles/gthreads/code0.html)

~~~
caf
Because in C (as opposed to C++), a static const int is not a compile-time
constant (so you can't use it in places like switch case labels).

------
jerryr
Interesting. I hadn't heard the "green thread" term before. Does anyone happen
to know how this compares to Windows' "fiber" mechanism? I can't recall
whether fibers actually run in user land.

~~~
roblabla
A Fiber is a cooperative multitasking strategy. As such, a fiber _must_ yield
at some point to allow other fibers to do their work.

Green threading is just the idea of doing the scheduling of threads in user-
space. It can be preemptive or cooperative. Fibers and green threading aren't
mutually exclusive, afaik.

~~~
baruch
Are there actual examples of green threads that are not cooperative?

All the implementations I've seen are cooperative.

~~~
chrisseaton
Haskell has green (N to M) threads that are pre-emptive. As does Go (but it
calls them coroutines). Erlang (but it calls them processes) I'm not sure
about. Maybe it only re-schedules on message sends and receives?

~~~
derefr
Erlang does a different thing called "reduction-counting": the active process
in a scheduler-thread gets a budget of virtual CPU cycles (reductions/"reds"),
tracked in a VM register, and part of the implementation of each op in the
VM's ISA is to reduce that reduction-count by an amount corresponding to the
estimated time-cost of the op. Then, the implementation of the _call_ and
_ret_ ops in the ISA both check the reduction-counter, and sleep the process
(scheduling it to resume at the new call-site) if it has expended all its
reds.

(If you're wondering, this works to achieve soft-realtime guarantees because,
in Erlang, as in Prolog, loops are implemented _in terms of_ recursive tail-
calls. So any O(N) function is guaranteed to hit a call or ret op after O(1)
time.)

If you're writing an Erlang extension in C (a "NIF"), though, and your NIF
code will be above O(1), then you have to ensure that you call into the
runtime reduction-checker yourself to ensure nonblocking behavior. In that
sense, Erlang is "cooperative under the covers"—you explicitly decide where to
(offer to) yield. It's just that the Erlang HLL papers over this by having one
of its most foundational primitives do such an explicit yield-check.

~~~
timow1337
If loops are implemented as tail calls, doesn't the call opcode get optimized
away thus preventing the reduction checker from being run?

~~~
jacquesm
It marks the point just before the call as a yield. That yield remains after
optimization.

