Version française
Home     About     Download     Resources     Contact us    
Browse thread
[Caml-list] Flush behavior of baseic I/O class type
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: skaller <skaller@u...>
Subject: Re: [Caml-list] Flush behavior of baseic I/O class type
On Sun, 2004-10-17 at 01:52, Yamagata Yoriyuki wrote:

> I think three possibility.
>   1) Output as far as possible, and leave the rest.
>   2) raise an exception
>   3) undefined.
> 
> What is your opinion?

Hmmm, well having read all the other replies so far,
perhaps I can contribute to the confusion with
a slighly different approach.

Your specification says '... the call of flush writes .. '
but that's an imperative description.

Instead, you could use a declarative description,
that is, specify the pre- and post- conditions.

For example:

pre-condition: the buffer contains whole characters
post-condition: the buffer is empty and its data is
scheduled for transmission

With this specification:

(1) if you find yourself in the middle of a multi-byte
character, the post condition is not guarranteed
because the pre-condition is not met. The behaviour
may vary depending on the kind of device, communication
path, time of day, and version of the program.

(2) The words 'scheduled for transmission' imply
flush can return before actual transmission.
You have to say that, because you cannot possibly
ensure the transmission on a high level operating
system. On Linux for example there is no way
to ensure disk writes end up on the media.
Just try writing to a floppy. On RH9 the only
way to physically write the floppy is to
unmount it.

It's quite possible that flushing a buffer works
by unlinking the memory block from the IO object,
and adding it to a queue 'to be written', and then
sometime later another thread writes it.

It's *also* possible you have a block device that
cannot support partial writing: you have to write
256 bytes, period, end of story. In this case,
flush will not be able to write everything, period,
end of story .. it isn't acceptable to write a padded
block out except at the end of the file. This may
be the case for a tape drive, for example.

In this case the write is still scheduled, meaning
the level of abstraction the current class deals
with has lost control of the situation and delegated
it to another layer .. but no assurance that the
physical transmission will occur is made, unless
subsequent operations on those lower levels
are done correctly. However, the class managing
the buffer has still carried out its job by delegating
to a lower level.

As an example -- you have an object designed to
physically write, and a program that flushes
at the end of each line. But the problem is you 
are getting a huge bill from your ISP, by sending
twice the number of TCP/IP packets you would need to,
because most are only half full.

So you just add a buffering class into the chain,
which captures physical write from its owner
and buffers them. The upchain class is still satisfying
its post-condition. The downchain class doesn't write,
even when flush is called. It's *designed* that way.
As far as it is concerned, once stuff is given
to it that stuff is *already* scheduled for transmission
by definition, and so flush on it does nothing -- the post
condition is already satisfied.[Caveat: its buffer
must be 'empty' after flush. But what that means is
also an abstraction.. it probably doesn't have floating
buffers, but uses a single fixed one .. in which case
the 'floating buffer it would have had' is deemed
flushed at all times anyhow .. :]

(3) When you have blocking IO, nothing changes.
Flush should block, if it is necessary to satisfy
the post-condition.

The bottom line is that 'scheduled for transmission'
is itself an abstract phrase. What it means depends
on the class. Each class should define this phrase,
so the programmer can choose an appropriate class
for their needs.

Finally, I'd like to give a 'real world' and critical
example. C++ has three kinds of device:

(1) ostream
(2) FILE*
(3) Unix file handle

Typically, ostreams and FILE* both write via Unix file handle.
So what if you use the ostream 'cout' and the FILE* 'stdout'
and the Unix file handle '1' all in the same program??

Well, the ISO Standard in *this* case requires that
interleaved calls to cout and stdout (but not 1) work
'properly' (So they're required to share the same buffer).
[AFAIC remember :] 

This is not so in general for other objects such
as a disk file or communication port. It's not even
required for streams on the same disk file --
they can use different buffers "just don't do that" :)


-- 
John Skaller, mailto:skaller@users.sf.net
voice: 061-2-9660-0850, 
snail: PO BOX 401 Glebe NSW 2037 Australia
Checkout the Felix programming language http://felix.sf.net



-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners