eregontp / gmail.com wrote:
> This is interesting work, I am curious to see how it will work out.

Thanks for the interest.

> This looks similar to what Crystal has [1].

Right.  But actually I would use MRI 1.8 green threads as
a reference point.  The key difference between this and 1.8
is this is tickless (or timer-less); so more predictable.

To me, there are only two types threads available to userland:

1) OS kernel knows about them (native thread)
2) OS kernel has no idea about them (fiber/green thread/goroutine)

> Does Kernel#puts potentially yields to another auto-Fiber?
> I think that would be very counter-intuitive, but it would be tempting if $stdout is a pipe or socket.

Yes, potentially.  However, it requires setting IO#nonblock=true
on $stdout (or whatever $> points to), which is rare...

Non-blocking stdout is rare since likely causes headaches if using
system() to run other programs or having 3rd-party libs which
write to stdout.

> Will a read from a socket always yield to the next fiber,
> or can it proceed immediately if the socket is ready?

It only yields on EAGAIN/EWOULDBLOCK when rb_wait_for_single_fd
is called.  It will never yield if there is always data.

AFAIK, Ruby io.c+ext/socket/* does not use rb_wait_for_single_fd
until it encounters EAGAIN/EWOULDBLOCK.  (I would consider it a
performance bug if it did)

> If not, then scheduling is non-deterministic,
> even when communicating with a deterministic server.

(sorry, double negatives are confusing to me to parse and use).

If a socket can always read/write without encountering
EAGAIN/EWOULDBLOCK, the Fiber may run forever.  This will starve
other Fibers, so it is up to the programmer to yield explicitly.

We should add Fiber.pass (like Thread.pass) to aid users with
this.  This will protect HTTP/1.1 servers from DoS via request
pipelining.

So I guess scheduling is non-deterministic; but actual use
can be deterministic since the programmer should know when
to yield/pass explicitly?

> It seems that the Crystal approach has some issues for terminating correctly.
> However, if I understand in your model there is an implicit wait for all auto-fibers until termination at the program end?
>
> This makes more sense to me for cooperative threading.

No implicit waiting for termination.  Fibers can be forgotten
and dropped at program end; just like threads.  I think this is
a necessary condition for supporting fork or exec.

Users must use Fiber#join or Fiber#value to ensure termination;
(same as Thread#join / Thread#value)

> The description from Crystal mentions:
> "Crystal uses green threads, called fibers, to achieve concurrency.
> Fibers communicate with each other using channels, as in Go or Clojure, without having to turn to shared memory or locks."
> The part about shared memory and locks is a lie though, these fibers do share memory and
> atomicity is broken at every possible call that could invoke some IO-like operation.
> 
> This is also true for auto-fibers, which is a form of shared-memory concurrency,
> and every yielding point will effectively need to assume
> any other auto-fiber could have run in between and modified some global state
> (unless the yielding order is very clear such as in a small program,
> but in larger programs it becomes extremely difficult to know the fiber schedule).
> 
> [1] https://crystal-lang.org/docs/guides/concurrency.html

Yes.  Programmers must be careful about shared memory; but
ruby-core can promote+improve APIs like Queue/SizedQueue to use
as communications channels.  This should reduce the use of (and
dangers associated with) shared memory.

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>