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
* 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);
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 ___SCMOBJ conversion? I don't find ___UINT_to_SCMOBJ's (or ___U64_to_SCMOBJ's) definition anywhere.
Thoughts?
Best regards,
Mikael