Between successive calls to umask/1, the process may be swapped out. If another process queries the umask, beam may end up in an inconsistent state. The issue is that querying the umask is destructive: it's done by setting the umask to 0, then re-setting it back to the original umask. A better fix would be to put the logic in the NIF where the sequence of events is atomic. However, it's more fun to experiment with putting the lock in the Erlang code. There are 2 potential problems: 1. If another process registers 'perc_umask', umask/0 will spin forever. 2. If something (another NIF, ...) changes the umask between calls, umask/0 will crash, releasing the lock. This will kill the calling process which may not be expected. To provoke the race condition: -module(umask). -export([stress/1]). stress(N) -> [ spawn(fun() -> set_mask() end) || _ <- lists:seq(1,N) ]. set_mask() -> false = 0 == perc:umask(), set_mask().
The perc calls into the C functions aren't documented yet. The arguments may change from an integer (the fd) to a NIF resource in the future.
Add signalfd/2 to pass in already opened file descriptors; for example, if the caller wants to change the signal mask.
One read may return multiple signals. Convert each signal into an individual message. The code does not handle partial signals. If partial reads are possible, the gen_server will need to maintain the partial data in its state. Running the tests, the VM is sometimes printing out the signal. The behaviour of signals is mysterious. Presumably the VM has its own signal handler that is also firing. Whether this will affect the state of the VM is an open isssue.
On Linux (and hopefully other OS'es that support signalfd), support reliable signal handling using signalfd(2).
Map of name to numbers taken from a Linux system. If the numbers vary by system, the header macros will have to call a function which sets the values based on os. Dither still on whether to use functions or macros for defining constants.