In this article I will present to you some macro definitions for handling interruptible system calls.
In POSIX operating systems (such as GNU/Linux) various system calls may be interrupted and return the following errors:
- EINTR (Interrupted system call)
- EAGAIN (Resource temporarily unavailable / Try again)
- EWOULDBLOCK (Operation would block)
Whenever a system call fails and returns any of the above errors we might need to retry the call. These errors occur more often on system calls that need some time to complete and are I/O related. Also, all the error names specified by POSIX.1 must have distinct values, with the exception of EAGAIN and EWOULDBLOCK, which may be the same.
In GNU/Linux operating system these two errors have the same value:
#define EWOULDBLOCK EAGAIN
Some system calls may return none, all or some of the above errors depending on their implementation. Here, follows the header containing the macro definitions for handling the errors:
/* * Copyright (C) 2014 Efstathios Chatzikyriakidis (stathis.chatzikyriakidis@gmail.com). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _INTERRUPTION_WRAPPER_HELPER_MACROS_ #define _INTERRUPTION_WRAPPER_HELPER_MACROS_ #include <errno.h> /* * variables. */ static const unsigned char maximum_attempts = 100; /* * macros. */ #define HANDLE_EINTR(x) ({ \ unsigned char _attempts = 0; \ \ typeof(x) _result; \ \ do \ { \ _result = (x); \ } \ while (_result == -1 \ && errno == EINTR \ && _attempts++ < maximum_attempts); \ \ _result; \ }) #define HANDLE_EAGAIN(x) ({ \ unsigned char _attempts = 0; \ \ typeof(x) _result; \ \ do \ { \ _result = (x); \ } \ while (_result == -1 \ && (errno == EAGAIN || \ errno == EWOULDBLOCK) \ && _attempts++ < maximum_attempts); \ \ _result; \ }) #define HANDLE_EINTR_EAGAIN(x) ({ \ unsigned char _attempts = 0; \ \ typeof(x) _result; \ \ do \ { \ _result = (x); \ } \ while (_result == -1 \ && (errno == EINTR || \ errno == EAGAIN || \ errno == EWOULDBLOCK) \ && _attempts++ < maximum_attempts); \ \ _result; \ }) #endif // _INTERRUPTION_WRAPPER_HELPER_MACROS_
The header defines the following macros:
- HANDLE_EINTR – handles the EINTR error
- HANDLE_EAGAIN – handles the EAGAIN and EWOULDBLOCK errors
- HANDLE_EINTR_EAGAIN – handles the EINTR, EAGAIN and EWOULDBLOCK errors
The macros have a maximum number of retries and return the last returning value of the system call.
Here are some examples:
int file_descriptor = HANDLE_EINTR(open(file_path, O_RDWR)); pid_t pid = HANDLE_EAGAIN(fork()); ssize_t fetched = HANDLE_EINTR_EAGAIN(read(file_descriptor, count)); ssize_t written = HANDLE_EINTR_EAGAIN(write(file_descriptor, buffer, count)); int client_socket = HANDLE_EINTR(accept(server_socket, NULL, NULL)); HANDLE_EINTR(usleep(idle_time_in_microseconds)); if (HANDLE_EINTR(dup2(file_descriptor, STDIN_FILENO)) < 0) { // an error occured } while (HANDLE_EINTR(waitpid(WAIT_ANY, NULL, WNOHANG)) > 0) { // do something } while (HANDLE_EINTR(wait (NULL)) > 0) ;
Lastly, please take account that although the system call “int close(int fd);” (in Linux kernel) may fail with EINTR you should not retry the call because this may lead to a known bug.
Happy Hacking!