Poll and Kqueue Hack
Author: Samuel Williams When: Thursday, 02 April 2009April 2009
May 2009
August 2009
September 2009
October 2009
- Building a Concrete Bath
- LED Lighting Comparison
- Thinking about Programming Languages
- How To Be A Consultant
- Lucid Programming Dojo
- Exim4 + ClamAV + SpamAssassin
- Secure login using AJAX
- Ramaze And Rack
- ActiveMerchant
- Concurrency And Immutability
- Floating Point Numbers
- Programming And Debugging
- Useful jQuery Plugins
- Loading Anonymous Ruby Classes
- 尺八 (Shakuhachi)
- Card Trick
- Object Oriented C
- Gemcutter
- Writing Clearly
- Richard Stallman In Christchurch
- Magnatune
- Client Side Graphing
- Zena CMS
November 2009
February 2010
March 2010
April 2010
May 2010
June 2010
July 2010
August 2010
September 2010
December 2010
January 2011
March 2011
May 2011
August 2011
September 2011
I've had quite a few issues with poll and kqueue on Mac OS X/Darwin. When trying to poll for events using fileno(stdin) or STDIN_FILENO, it gives errors such as Operation not supported (kqueue) or POLLNVAL (poll).
On Darwin (at least up until Mac OS X 10.5.5), stdin, stdout and stderr are actually devices! Not pipes like one might expect. Also, poll is implemented on top of kqueue, therefore problems with kqueue also affect poll.
Because of this, polling stdin won't work. Here is an example which fails on Mac OS X 10.5.5:
#include <stdio.h>
#include <poll.h>
int main (int argc, char ** argv)
{
struct pollfd pfd;
pfd.fd = fileno(stdin);
pfd.events = POLLIN|POLLOUT;
pfd.revents = 0;
int result = poll(&pfd, 1, 0);
if (result < 0) {
printf("poll = %d\n", result);
perror("poll failed");
}
printf("pfd.revents = %d\n", pfd.revents);
if (pfd.revents & POLLNVAL) {
printf("invalid file descriptor\n");
}
return 0;
}
A hackity solution
This code uses boost threads. What it does is create three pipes, one for stdin, one for stdout and one for stderr. The originals are moved to different fds and then we spawn three threads to process data between the original devices and the newly created pipes. The result of this is that STDIN_FILENO, as an example, now refers to the write end of a pipe. The other end of this pipe is being read by the thread, and any data is written out to the original stdin device.
class ConsolePipeRedirector {
boost::thread_group m_threads;
bool m_done;
public:
ConsolePipeRedirector () : m_done(false)
{
}
static void copyDataBetweenFileDescriptors (int input, int output)
{
char buf[512];
while(1) {
ssize_t count = read(input, buf, 512);
if (count < 0) {
perror("copyDataBetweenFileDescriptors");
} else if (count == 0) {
break;
}
write(output, buf, count);
}
}
void reopenStandardFileDescriptorsAsPipes ()
{
if (m_done) return;
m_done = true;
int result;
int stdinPipe[2], stdoutPipe[2], stderrPipe[2];
// Create a new pipe for stdin data
result = pipe(stdinPipe);
if (result) perror("pipe(stdinPipe)");
// Copy the stdin device to a new fd
int stdinDevice = dup(STDIN_FILENO);
// Same again for stdout
result = pipe(stdoutPipe);
if (result) perror("pipe(stdoutPipe)");
int stdoutDevice = dup(STDOUT_FILENO);
// Same again for stderr
result = pipe(stderrPipe);
if (result) perror("pipe(stderrPipe)");
int stderrDevice = dup(STDERR_FILENO);
// Copy the pipe endpoint into the standard place for in, out and err.
dup2(stdinPipe[0], STDIN_FILENO);
dup2(stdoutPipe[1], STDOUT_FILENO);
dup2(stderrPipe[1], STDERR_FILENO);
// Spawn threads to handle reading and writing in a blocking fashion.
m_threads.create_thread(bind(copyDataBetweenFileDescriptors, stdinDevice, stdinPipe[1]));
m_threads.create_thread(bind(copyDataBetweenFileDescriptors, stdoutPipe[0], stdoutDevice));
m_threads.create_thread(bind(copyDataBetweenFileDescriptors, stderrPipe[0], stderrDevice));
}
};
In this case, I've fixed all three standard fds. However, it is probably only useful in most cases to fix stdin - this is likely to be the only device you would want to poll. I've included support for all three as an example of how it would be done.
Poll specifically is a bit different across different platforms. Therefore, you need to be aware of the differences.
Comments
Please note, you can leave a comment that uses (limited) XHTML and Textile syntax.