It's now possible to run pipelines of using pthreads:

////////////////////////
proc psrc (cho:opchannel[int]) { 
  for var i in 0 upto 9 do write (cho,i); done 
  write (cho,-1);
}

proc psink (chi:ipchannel[int]) { 
  var x = read chi;
  while x >= 0 do print$ " "+ str x; x = read chi; done 
  println$ "-PPP -------------";
}

proc pxduce(chi: ipchannel[int], cho: opchannel[int]) {
  write (cho, 99);
  while true do var x = read chi; write (cho, x);  done
}

spawn_pthread (psrc |-> pxduce |-> pxduce |-> psink);
////////////////////////

However, the semantics of pthread/pchannels differs from fibres/schannels.
The parallel pipes here work, and produce correctly ordered results.
But the program doesn't terminate.

Why? Because Felix pthreads are detached, and pxduce is an infinite loop
which ends up starving, that is, it hangs on a channel waiting for
data that never comes.

Although there's no (automatic) way to join Felix detached pthreads
Felix mainline driver does it. Since one of the threads is an infinite loop,
Felix refuses to terminate.

This fixes it:

proc pxduce(chi: ipchannel[int], cho: opchannel[int]) {
  write (cho, 99);
  var x = read chi;
  while x >= 0 do write (cho, x);  x = read chi; done
  write (cho, -1);
}

Generally Felix provides fthread/pthread and schannel/pchannel operations
with syntactic and library symmetry: the same code is well formed swapping
between cooperative and pre-emptive multi-tasking, with minor text edits.

However the semantics is utterly distinct.

Fthreads should be programmed to suicide when they lock up,
which is done by treating channels with Need To Know security.
That is, make sure only procedures needing to communicate
on channels know their secret names. Spipes are a specifically
designed kind of Witchcraft that hides the secret names of channels
to ensure pipelines collapse when they have no work to do.

You can use this model with pthreads and pchannels too, up to
program termination where it fails, because Felix itself secretly
grabs the names of the pthreads. 

Felix has a Need To Know: pthreads launched with spawn_pthread
share a garbage collector. Because Felix uses a world-stop naive
collector, it has to ensure all pthreads have suspended before 
sweeping can start. It can only do this if it knows all the pthreads.
Note that the conservative stack-sweeping phase of the collector
scans all pthread stacks: all the pthreads (except the one doing
the collection) have to be frozen for this to work.

In addition, experience showed that it is a bit messy for the main
program to return with detached threads still operating. In particular
different operating systems treat this differently.

Therefore,  Felix suspends program termination until all
pthreads have died. This also means then where "myprogram"
is your whole program (or at least the tail of it):

myprogram(); 

and

spawn_pthread myprogram;

behave identically.   In other words, this is a "tail spawn"
like a tail call; semantics ensure that a tail call has the
same behaviour as a tail spawn.

Why does Felix have detached threads instead of joinable ones?
===================================================

Its very important to observe that fork/join is NOT a proper abstraction.
It looks like it should be the core primitive but it doesn't actually work
properly. This is surprising because most people would think it is
fundamental.

Clearly, detached threads are weaker than fork/join because there
is no way to join them. You need  additional infrastructure such as
pchannels to do that. Note that being "weaker" also means they're
more flexible. In particular with pchannels you can easily
implement fork/join.

The problem with fork/join as presented in Posix is that the implementation
is fundamentally unsound. When you spawn a pthread in Posix you get
a handle to the thread you can wait on. You can even get some status
data in some versions.

The fact we have to hold some kind of specific data type as a handle
immediately sets off alarm bells! The collapse of the concept is proven
easily then: thread A launches thread B, collects a handle H to it,
then waits on H ... 

DO you see it? This is not a proper model of a fork/join because
one of the threads is a master and the other a slave. The only way
to correctly fork/join here is for the main thread to spawn TWO
threads and wait on both.

Felix RTL does indeed provide joinable threads!
However unless you reach under the counter you will not be able
to explicitly spawn them. Instead something like:

        fork
                p1;
                p2;
                p3;
        join



--
john skaller
skal...@users.sourceforge.net
http://felix-lang.org




------------------------------------------------------------------------------
LogMeIn Rescue: Anywhere, Anytime Remote support for IT. Free Trial
Remotely access PCs and mobile devices and provide instant support
Improve your efficiency, and focus on delivering more value-add services
Discover what IT Professionals Know. Rescue delivers
http://p.sf.net/sfu/logmein_12329d2d
_______________________________________________
Felix-language mailing list
Felix-language@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to