Hello !
I've been writing some code today, dumping data in files on the hard disk. I knew that gambit, and many (if not all) languages buffers data before enventually writing it to the disk. What puzzled me today, is that it actually never got written !
Here is the code :
(let ((o (open-output-file "/tmp/flushed-data"))) (display "whirl" o) (display "\n" o))
I thought that when the gambit process cleanly finishes, it would flush the port, though it didn't. I had to append a (close-output-port o) in order to have my data dumped.
So, is that an undocumented feature, do I have to do the display in the IO monad ? Or is it just fully compliant with the "do what you want with the little constraint in the RnRS" ?
This behaviours happens as well with interpreted code as woth compiled code. Isn't the OS (or the libc behind gambit) supposed to flush when the process terminates ?
Should I stick to CALL-WITH-OUPUT-FILE or always close ports ?
Adrien
Afficher les réponses par date
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On 31-May-07, at 6:26 PM, Adrien Pierard wrote:
Here is the code :
(let ((o (open-output-file "/tmp/flushed-data"))) (display "whirl" o) (display "\n" o))
I thought that when the gambit process cleanly finishes, it would flush the port, though it didn't. I had to append a (close-output-port o) in order to have my data dumped.
You have to call (close-output-port o), otherwise the runtime system does not know that you are done with port "o". All buffering is done at the Scheme level, so your calls to "display" are merely filling some internal Scheme buffers (Scheme strings and u8vectors). The OS is not aware of these buffers. It is when you do a (force-output- port o) or (close-output-port o) or when the internal buffer is full that the data is sent to the OS.
If you dislike this, you can always disable the buffering, or add a finalizer that closes the port, for example:
(define open-output-file (let ((orig-open-output-file open-output-file)) (lambda (filename) (let ((port (orig-open-output-file filename))) (make-will port close-output-port) port))))
(let ((o (open-output-file "out"))) (display "foo" o) (display "bar\n" o))
(##gc) ;; GC will call finalizer
Marc
Adrien Pierard wrote:
do I have to do the display in the IO monad ?
You usually want to be sure that your IO actions are completing as planned or take a recovery path if they don't. Completing includes (flushing and) closing the file, so that you get to know from the OS whether the content still fits onto the filesystem, your quota is used up, the disk failed, the network with the volume just vanished... You have to describe to the Scheme system how to deal with those situations (e.g. provide code). The Gambit IO functions are calling your current-exception-handler for this purpose. If you want the Scheme system to flush/close your files at other moments (asynchronically), you still have to tell it which code to run; make-will being Marc's suggestion (I think you have to reinstall your current-exception-handler there (using with-exception-catcher)). Usually it's more straight-forward to deal with writing/flushing/closing (all including possible error handling) in sequence than doing the last step(s) asynchronically.
Christian.