Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I prevent SIGPIPE when using boost::asio?

I'm using a pipe to communicate between two processes on Gnu/Linux. The receiving end closes the pipe while the sending end is still trying to send data. Here is some code that emulates the situation.

#include <unistd.h>                                                              
#include <boost/asio.hpp>                                                        

int main()                                                                       
{                                                                                
    int pipe_fds[2];                                             
    if( ::pipe(pipe_fds) != 0 ) return 1;                                        
    // close the receiving end 
    ::close( pipe_fds[0] );

    boost::asio::io_service io;                                             
    boost::asio::posix::stream_descriptor sd( io, pipe_fds[1] );     
    boost::system::error_code ec;                                                
    sd.write_some( boost::asio::buffer("blah"), ec );

    return 0;                                                                    
}

When I run it I get a SIGPIPE; classic situation, I know. However, I see that boost::asio::error::basic_errors has a broken_pipe value. I would expect that to be returned in the error_code without a signal being raised.

Can this be done without creating a SIGPIPE handler for my process? For instance, is there a configuration option to boost::asio that I'm missing? Maybe something that would enable MSG_NOSIGNAL in the implementation?

like image 487
kalaxy Avatar asked Nov 02 '11 20:11

kalaxy


2 Answers

Install a signal handler to ignore SIGPIPE if you wish to see the appropriate error_code

code and compile

#include <unistd.h>
#include <iostream>
#include <boost/asio.hpp>


int main( int argc, const char** argv )
{
    bool ignore = false;
    if ( argc > 1 && !strcmp(argv[1], "ignore") ) {
        ignore = true;
    }
    std::cout << (ignore ? "" : "not ") << "ignoring SIGPIPE" << std::endl;

    if ( ignore ) {
        struct sigaction sa;
        std::memset( &sa, 0, sizeof(sa) );
        sa.sa_handler = SIG_IGN;
        int res = sigaction( SIGPIPE, &sa, NULL);
        assert( res == 0 );
    }

    int pipe_fds[2];
    if( ::pipe(pipe_fds) != 0 ) return 1;
    // close the receiving end 
    ::close( pipe_fds[0] );

    boost::asio::io_service io;
    boost::asio::posix::stream_descriptor sd( io, pipe_fds[1] );
    boost::system::error_code ec;
    sd.write_some( boost::asio::buffer("blah"), ec );

    if ( ec ) {
        std::cerr << boost::system::system_error(ec).what() << std::endl;
    } else {
        std::cout << "success" << std::endl;
    }

    return 0;
}

samjmill@bgqfen7 ~> g++ pipe.cc -lboost_system -lboost_thread-mt
samjmill@bgqfen7 ~> 

run

samm@macmini ~> ./a.out 
not ignoring SIGPIPE
samm@macmini ~> echo $?
141
samm@macmini ~> ./a.out ignore
ignoring SIGPIPE
Broken pipe
samm@macmini ~> 

The rationale for this behavior is in the write(2) man page

EPIPE

fd is connected to a pipe or socket whose reading end is closed. When this happens the writing process will also receive a SIGPIPE signal. (Thus, the write return value is seen only if the program catches, blocks or ignores this signal.)

emphasis added by me.

like image 96
Sam Miller Avatar answered Sep 18 '22 08:09

Sam Miller


SIGPIPE is generated by the operating system when one end of a pipe is not connected - you can't really prevent it with boost::asio. You can however simply ignore the signal and the rest should take care of itself.

like image 36
Dean Povey Avatar answered Sep 18 '22 08:09

Dean Povey