Product SiteDocumentation Site

4. The TCR library

The TCR library implements a parallel select(2)/poll(2) state machine in a static pool of threads. It creates one system-level thread per CPU/core. On top of that, the TCR library uses makecontext(3) and swapcontext(3)[1] to create user-level thread switching to provide a linear programming model to the library user.
The TCR library does not transparently hide sleeping system calls from the programmer; the programmer has to explicitly use the library's means to do user space thread switching. While libtcr uses an event loop internally, it does not expose this to its users. It also frees the user from dealing with callbacks.
All tc_ calls that create new tc_threads are as cheap as a memory allocation. That allocation is necessary to provide stack space for the newly created thread.

4.1. Blocking IO functions

File descriptors that should be used by libtcr's event loop have to be registered with the tc_register_fd(3) call. That call sets the file descriptor into non blocking mode. When a potentially blocking system call is used on that file descriptor and it fails with EAGAIN, the tc_wait_fd(3) call can be used to wait until the FD is ready again.
ssize_t tc_read(struct tc_fd *t, void *buf, ssize_t buflen)
{
	ssize_t ret, len = buflen;
	enum tc_rv rv;

	while (1) {
		ret = read(tc_fd(t), buf, len);
		if (ret > 0) {
			buf += ret;
			len -= ret;
			if (len == 0)
				return buflen;
		} else if (ret < 0) {
			if (errno != EAGAIN)
				return buflen - len ? buflen - len : ret;
		} else /* ret == 0 */
			return buflen - len;

		rv = tc_wait_fd_prio(EPOLLIN, t);
		if (rv != RV_OK) {
			errno = EINTR;
			return -1;
		}
	}
}
A blocking variant of the read(2) system call in the libtcr framework.
Example 1. tc_read

4.1.1. Interrupting sleeping tc_ calls

Similar in concept to Unix signals, tc_ signals are used to interrupt sleeping tc_ calls. A tc_thread that wants to be interruptible has to first subscribe a tc_signal with the tc_signal_subscribe(3) function. Any sleeping tc_ function indicates (with its return code) whether the event it was waiting for actually happened, or if it was interrupted by a tc_signal.
Any tc_thread that has a reference to a tc_signal object can fire the signal with tc_signal_fire(3).


[1] Since glibc's implementation of swapcontext uses system calls to preserve and restore signal masks, the TCR library brings its own implementations that execute in user space only.