erlang/otp — pull request #379 by pmembrey (2014-05-26 11:24:47+00:00)
Title: Add manual tick support

The EI interface library provides functions that read messages from a network socket and block until a full message is received. There are companion functions (_tmo) that allow for a timeout to be provided. Both sets of functions automatically handle *TICK* requests from the connected Erlang node. However in multi-threaded applications, this may not be desirable.

For example, you may have a C application that in the main thread, blocks on the Erlang socket and waits for messages and has a number of threads "doing work". When these threads need to send data to the Erlang node, they write directly to the socket. This can cause problems when more than one thread tries to write at any given time. The usual solution for this is to use a mutex to protect the socket.

This works well, until you receive a heartbeat (TICK) message from the Erlang node. The ei_receive calls internally respond to the heartbeat and as they write to the socket directly, without mutex protection, data corruption is highly likely.

As the calls block, it is not possible to wrap a mutex around the receive call on the off chance that the message received might be a heart beat request. It is possible to wrap an ei_receive call that has a timeout with a mutex, but this is highly undesirable. First, it will still prevent the application from writing even though there is no need to wait. Second, it's quite possible for the timeout to trigger during a message being read and having the read fail causing the cluster connection to fail.

A simple solution to this problem is to provide a set of receive functions that do not answer heartbeat requests. When these functions are used, the application must specifically reply to those requests itself. This means that the receive function will never send data (in keeping with its name) and as such, does not need to be protected by a mutex. Replying to the heartbeat with mutex protection is straight forward:

const int got = ei_xreceive_msg_wt(fd, &emsg, &xbuff); // Wait indefinitely
if (got == ERL_TICK) {
// Send TOCK - with mutex protection
pthread_mutex_lock(&mutexSocket);
ei_send_tock(fd);
pthread_mutex_unlock(&mutexSocket);
}

This update has been tested with a production application and has been running for several days without issue. Without this patch (where I was using the timeout approach), the C application could not remain connected for more than a few seconds.

The previous API has been maintained and it should be completely backwards compatible. I have tried it without any issues.

This is my first PR for the OTP project so thanks in advance for your patience. I am more than happy to make changes or updates based on guidance - just let me know what needs doing.

Lastly thanks for all your hard work :)