Dear Marc and list,
(
There are times when extracting the file descriptor for instance from a
Gambit TCP port is extremely useful. One example of this would be when
using OpenSSL, where OpenSSL reads and writes directly to and from the
socket, thus saving us from a round of buffering and thereby increasing
performance. There ought to be loads and loads of other C libraries where
this is of great use also.
Also, it ought to be useful for doing lowlevel configuration of fd:s and
sockets using OS API:s, like turning on or off keepalives, etc.
Gambit's IO system scales excellently for C code to do socket reads and
writes, in the sense that Gambit not interferes with it with any background
buffering or alike. Gambit can even 'multiplex' sockets managed in C thanks
to that the user can make Gambit include selected Gambit TCP ports in its
IO scheduler's select() calls, still without doing any reads or writes due
to buffering, using the |##device-port-wait-for-input!| (and
|##device-port-wait-for-output!|, currently in a pull request to the Gambit
repo) procedures.
Currently, extracting the socket FD requires a quite ugly hack that relies
on the user code having access to os_io.h. This is ugly both considering
that compiled Gambit distributions don't include os_io.h (by good reasons,
as it's an internal file), considering that os_io.h may change and also
#include does not have a reference to Gambit's installation directory so
the hack would not be future-proof ever.
Fd:s never change during the course of the lifetime of a Gambit port
object, so this kind of use is clearly good design.
)
What about adding support for Gambit port file descriptor retrieval by
adding to Gambit a procedure (##os-device-get-fd port) => socket-fd-int ?
Even if this is not like the 'recommended official way' of doing IO in
Gambit, I think it's of great value that it is in the box, and again, it
does work perfectly already, just that a ugly hack needs to be used to make
it go around in this moment, and this would be completely remedied by the
addition of such a procedure.
As regards how to implement it, my current best understanding is this would
be done by something like:
* Adding to lib/_kernel.scm row 4124 (here bc. this is where all of
Gambit's internal c-lambda:s are and C code is required to deliver this):
(define-prim ##os-device-get-fd
(c-lambda (scheme-object) scheme-object
"___os_device_get_fd"))
* Adding to lib/os_io.h row 680:
extern ___SCMOBJ ___os_device_get_fd
___P((___SCMOBJ dev),
());
* Adding to lib/os_io.c row 7500 (possibly the 'logics' code in this
procedure would be split out and put at row 1405 or so):
___SCMOBJ ___os_device_get_fd
___P((___SCMOBJ dev),
(dev)
___SCMOBJ dev;)
{
___device *d = ___CAST(___device*,___FIELD(dev,___FOREIGN_PTR));
switch ___device_kind(d) {
case ___FILE_DEVICE_KIND:
___device_file *d = ___CAST(___device_file*,d);
#ifdef USE_POSIX
return d->fd;
#elif USE_WIN32
return d->handle;
#else
return ___UNIMPL_ERR;
#endif
case ___TTY_DEVICE_KIND:
___device_tty *d = ___CAST(___device_tty*,d);
return d->t;
case ___SERIAL_DEVICE_KIND:
___device_serial *d = ___CAST(___device_serial*,d);
return d->h;
case ___TCP_CLIENT_DEVICE_KIND:
___device_tcp_client *d = ___CAST(___device_tcp_client*,d);
return d->s;
case ___TCP_SERVER_DEVICE_KIND:
___device_tcp_server *d = ___CAST(___device_tcp_server*,d);
return d->s;
else:
return ___FAL;
}
}
I did not try to compile the code above, though I believe it would compile
and work with only minor modifications.
The returned value currently is not converted to a Scheme object so will
cause Gambit to crash. I did not add ___FIX() here as the returned value
could just be bigger than ___FIX_MAX .
Is there any straightforward way to do unsigned int (or ___U64) to
___SCMOBJconversion? I don't find
___UINT_to_SCMOBJ's (or ___U64_to_SCMOBJ's) definition anywhere.
Thoughts?
Best regards,
Mikael