I've written some OCaml bindings for CZMQ based on the guide at http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php, which seem to work pretty well. For example here's zstr_send:
CAMLprim value caml_zstr_send(value socket_val, value string_val) { CAMLparam2 (socket_val, string_val); void *sock = CAML_CZMQ_zsocket_val(socket_val); char *string = String_val(string_val); int rc = zstr_send(sock, string); CAMLreturn (Val_int(rc)); }
I can send and receive messages using these bindings in most of my code just fine. However, I have a scenario where I would like to do sends and receives inside of a signal handler, to the end of doing message passing in the background of some other code. Take this simplified example:
open ZMQ exception SocketBindFailure let bg_ctx = zctx_new ();; let pub_sock = zsocket_new bg_ctx ZMQ_PUB;; let handler _ = print_endline "enter handler"; print_endline (string_of_int (zstr_send pub_sock "hello")); print_endline "end handler"; ;; let () = (try ( (* bind pub socket *) let rc = zsocket_bind pub_sock "tcp://*:5556" in if (rc < 0) then ( raise SocketBindFailure ); Sys.set_signal Sys.sigalrm (Sys.Signal_handle handler); ignore (Unix.setitimer Unix.ITIMER_REAL { Unix.it_interval = 0.01 ; Unix.it_value = 0.01 }); (* do some work *) ) with | SocketBindFailure -> raise SocketBindFailure) ;;
From the toplevel, this fails with the output:
enter handler 0 end handler Fatal error: exception Sys_blocked_io
C code similar to the OCaml above works just fine. What is OCaml adding to the equation that is causing this exception?
There are two potential problems:
Inside a signal handler, you can only call asynchronous signal safe functions. Most functions are not async signal safe.
The reason for the restriction is that a function could be called in the middle of the same function's execution. Thus, internal state could be corrupted. Very few functions are async signal safe, and anything that allocates memory dynamically is not. In OCaml, many allocations happen "behind the scenes", so it is likely that your code is not async signal safe.
In your case, you are calling a function that writes to standard output. In C, this is never async signal safe, with one exception: the primitive write()
function. This is the raw system call (which operates on a file descriptor) and is async signal safe for the simple reason that the kernel itself doesn't care you are in a signal handler, and will have fully cleaned up before returning control to you.
Calling an unsafe function from a signal handler, when the signal was asynchronous (the case here) and itself interrupted an unsafe function is undefined behavior in C. This means anything could happen -- including your program working correctly, but also including segmentation faults or other errors, as well as allowing an attacker to execute arbitrary code. This is normally associated with low-level languages like C, and is something that normally does not occur in OCaml.
OCaml uses a neat trick: when a signal is received for which a handler has been set in OCaml, it defers executing the handler until a safepoint. The result is that in a handler it is safe to set an unboxed quantity into a ref
variable. However, other functions like print
are possibly not reentrant, since they may have internal state. In general, within a signal handler you should try to avoid doing more than setting a flag and promptly returning. In OCaml, the flag should be a 31- or 63- bit integer, or a boolean, because these are unboxed. In C, the flag must be either volatile sig_atomic_t
or (I am not sure about this) a C11 atomic type.
@TheCodeArtist gives the other possible reason for the error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With