I struggle to create a Racket wrapper for FoundationDB C client. The Chez code rely on foreign-procedure
's __collect_safe
is there an equivalent available in Racket?
I overlooked this question originally, but just to record an answer here that was given in Discord: #:blocking? #t
for _fun
or _cprocedure
is the same functionality as __collect_safe
in the caller direction.
Thanks for the reply.
#blocking: #t
is useful in cases where the C procedure does not return, or returns after a long while. With cisco chez, running the code without it, crashed chez.
Ref: 3.6 Function Types
To expand a bit about my use case:
FoundationDB C library, every word from the documentation of #:blocking
is useful. The blocking procedure must be run in its own OS thread, called the network thread, that is in that thread that the heavy client logic happens. Indeed, the network thread is a thick client, it does more than serializing a remote procedure call (RPC) to a server process inside the distributed database, and more than keeping up with transaction states, and cache. I can't develop the details because I do not know them, the network protocol, and wire format are not documented, and rewriting the bits of the C library is not recommended by the maintainers. I started this paragraph, to explain that I investigated that, and I think it would be better for clients to have only racket code, and avoid #:blocking #t
but so far, I did not start that work because it is a lot of work that is not recommended. An alternative to #:blocking #t
, and the low-level drivers is to wrap the C library using a dedicated event loop in its own thread, and then pass the event loop fd
to racket's event loop, but that looks like a maintenance burden, and operational burden.
You might want to look at how the db
library handles this for sqlite3-connect
and odbc-connect
/odbc-driver-connect
with #:use-place
as 'place
, 'thread
, or #t
. As the docs explain:
Wire-based connections communicate using ports, which do not cause other Racket threads to block. In contrast, an FFI call causes all Racket threads to block until it completes, so FFI-based connections can degrade the interactivity of a Racket program, particularly if long-running queries are performed using the connection.
This problem can be avoided by creating the FFI-based connection in a separate place using the
#:use-place
keyword argument. Such a connection will not block all Racket threads during queries; the disadvantage is the cost of creating and communicating with a separate place. On Racket CS, another solution is to execute queries in a separate OS thread; this solution may have lower time and memory overhead than the separate place.
The core of the implementation is in db/private/generic/ffi-common
for OS threads and db/private-generic/place-client
and db/private-generic/place-server
for places.