caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: skaller <skaller@users.sourceforge.net>
To: Jonathan Roewen <jonathan.roewen@gmail.com>
Cc: Nathaniel Gray <n8gray@gmail.com>,
	Caml Mailing List <caml-list@yquem.inria.fr>
Subject: Re: [Caml-list] Re: Select on channels (again)
Date: Tue, 22 Aug 2006 18:15:07 +1000	[thread overview]
Message-ID: <1156234507.5707.47.camel@rosella.wigram> (raw)
In-Reply-To: <ad8cfe7e0608212341l148f194cj36b433827a9bcef8@mail.gmail.com>

On Tue, 2006-08-22 at 18:41 +1200, Jonathan Roewen wrote:
> > It sounds simple but doesn't work.  If select tells you a file
> > descriptor doesn't have data waiting you can't be sure there isn't
> > still data in the corresponding channel's buffer.  See the thread that
> > I referenced for a good discussion of why this is annoying.  For one
> > thing, it makes it impossible to use Marshal.from_channel without
> > potentially blocking.
> 
> Either one of us is misunderstanding the other....

You missed the first half of the discussion:

> Instead of using Pervasives.open_xxx, use Unix.openfile which returns
> Unix.file_descr, and also doesn't use internal ocaml buffering.
> 
> Then, presumably, Unix.select would do what you expect, and then you
> can use Unix.in_channel_of_descr to get an ocaml in_channel to read
> from.
> 
> And if I'm misunderstanding you, then perhaps the problem isn't really
> Unix.select...

The problem is that this defeats the use of all the formatting
and buffering functions that work on buffered I/O channels.

What's required is something that tells:

(a) there is some data in the buffer OR
(b) there is some data on the descriptor

so that in either case some progress can be made.

Unfortunately .. there's a reason this makes no sense:

For raw byte streams .. you can just use the file descriptors
already with select.

Otherwise, there's no way to predict if an input will block,
whether or not there is data in the buffer, and whether
or not the file descriptor is ready, because the input
operation can read some data THEN block.

The same argument applies to output.

Therefore .. there is no choice but to replace all the
buffering anyhow, and in general the whole programming
paradigm needs to be replaced.

Felix demux system already does this I think, for
both read/write n bytes, and for read/write a line.
More difficult cases should be handled by in-core
formatting eg:

	print_string (string_of_int i)

is correct and

	print_int i

is wrong. The former cannot block on formatting, the latter can.
(assuming nonblocking line I/O is available).

You're stuck between a rock and hard place here :)

The read/write functions of a system are designed to
provide control inversion: data coming in or going out
is naturally interrupt (callback) driven, but it is
inconvenient to program with callbacks (I would say
it more strongly -- it is *untenable* to use callbacks).

Therefore the scheduler provides blocking I/O, and switches
out programmatic demands for I/O, effecting control inversion.

You can try to work around this with non-blocking I/O,
but it is really a hack because doing so is tantamount
to writing your own scheduler to provide control inversion,
in other words, inventing your own operating system.
It is even worse if you use event notifications to avoid
polling (I mean, it is even more complex).

In general the only really sound solution is indeed to 
provide a full scale operating system abstraction layer,
which requires the underlying programming language computational
model be designed to work with it.

Several systems can work this way: Felix and Haskell both
have continuations, which seem to be the pre-requisite.
MLton may also cope with this.

The Ocaml computational model doesn't provide the required
resources natively, although of course they could be implemented
in Ocaml .. but then you would be programming with, for example,
suitable monadic combinators, rather than arbitrary raw Ocaml code.

Just so it is clear: given two sockets, you want to read integers
off them. You can do this with two threads, both of which block.
Or you can block, and invoke a callback when one conversion
finally completes.

The two techniques are control inverse. The only difference
is that the thread model uses OS control inversion and the
callback model uses hand written control inversion.

BOTH techniques suck. The only way to do this properly is the
way Felix does it: you write threaded code, but language
control inverts it into callback driven code systematically,
and provides its own OS abstraction layer: this gives you
the responsiveness and performance of user space callback
driven code, but the illusion of using threads.

You will note this is not a magical silver bullet: it
only works because the user code handles more specialised
cases than a general purpose OS can handle well: if one tried
to do this with full generality you'd just end up with yet
another low performance operating system. IMHO the key here
is that application specific information .. perhaps embodied
in the type system .. can be used by the user program and
language translator, but not the underlying OS.

Just to see, in Felix you'd do it something like:

	var ich = mk_schannel[int]();

	spawn_sthread { 
		forever { 
 			var x : int;
			read_int (sock1, &x);
			write ich, x;
		}
	}

	spawn_sthread { 
		forever { 
 			var x : int;
			read_int (sock2, &x);
			write ich, x;
		}
	}

	forever {
		var x:int;
		read (ich, &x);
		print x; endl;
	}

The two 'threads' spawned here are NOT pre-emptive threads.
They're actually continuations, which are resumed by
the underlying demux library notification mechanism
starting them up again based on epoll/poll/kqueue/select etc.
The interaction along the channel 'ich' is entirely synchronous.

Ocaml can do this now using Event module .. but it only works
across pthread boundaries.

Strangely .. the Ocaml VM system does this stuff for the
bytecode interpreter already, interleaving bytecode to
emulate threads, and forwarding blocking operations
so the emulated threads block .. but the actual pre-emptive
thread (process) does not.


-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


  reply	other threads:[~2006-08-22  8:15 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-08-15  0:46 Nathaniel Gray
2006-08-21 22:47 ` Nathaniel Gray
2006-08-22  0:42   ` [Caml-list] " Jonathan Roewen
2006-08-22  6:27     ` Nathaniel Gray
2006-08-22  6:41       ` Jonathan Roewen
2006-08-22  8:15         ` skaller [this message]
2006-08-22 21:15           ` Mike Lin
2006-08-23  5:12         ` Nathaniel Gray
2006-08-22  8:10       ` Olivier Andrieu
2006-08-23  5:27         ` Nathaniel Gray
2006-08-22  8:21       ` Jacques Garrigue
2006-08-23  5:16         ` Nathaniel Gray
2006-08-23  6:35           ` skaller
2006-08-23 19:31             ` Nathaniel Gray
2006-08-24  5:37               ` skaller
2006-08-24 19:06                 ` Nathaniel Gray
2006-08-25  1:55                   ` skaller
2006-08-25 22:19                     ` Nathaniel Gray
2006-08-23  8:29 Christoph Bauer
2006-08-23 17:35 ` Robert Roessler
2006-08-24  8:18 ` Robert Roessler

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1156234507.5707.47.camel@rosella.wigram \
    --to=skaller@users.sourceforge.net \
    --cc=caml-list@yquem.inria.fr \
    --cc=jonathan.roewen@gmail.com \
    --cc=n8gray@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).