15

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?

kalaxy
  • 1,608
  • 1
  • 14
  • 14

3 Answers3

8

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.

Rawler
  • 1,480
  • 1
  • 11
  • 23
Sam Miller
  • 23,808
  • 4
  • 67
  • 87
  • Too bad, I didn't realize the implementation was using write. I had hoped that they were using send and I could enable passing the MSG_NOSIGNAL flag. Thanks for the example code. – kalaxy Nov 03 '11 at 15:23
  • @kalaxy you can use MSG_NOSIGNAL with send – Sam Miller Nov 03 '11 at 15:48
3

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.

Dean Povey
  • 9,256
  • 1
  • 41
  • 52
2

signal_init has do it in the file boost/asio/detail/signal_init.hpp

Mysticial
  • 464,885
  • 45
  • 335
  • 332
billow
  • 125
  • 1
  • 1
  • 9
  • 1
    But it seems to do it only for a few platforms. Unfortunately it notably is not included as of Boost 1.55.0 on Linux and OS X. – eregon May 15 '14 at 17:54