Thursday, August 16, 2018

A short signal-handling-hack

… unbounded knowledge is an adventure at the boundary of uncertainty …

-- Jacob Bronowski (The Acent of man)

Introduction

Following is a short simple hack implementing an RAII based signal handling guard passing signals as template parameters.

The guard constructor sets a signal handler for a list of signals and the destructor resets the original signal handlers. The code makes use of variadic templates in a way I find pretty cool.

There is not much to this blog entry except for the code showing how to apply a function to each element in a list of template parameters.

Using the code

A small example using standard RAII style shows how the signal guard can be used:

// test program
int main(){
  {
    // install a signal handler for some signals
    sigguard<SIGINT,SIGHUP>([](int sig){});

    // code, code, code ...
    // ...
  }
  // old signal handler re-installed when 'sigguard' destructor runs
  // ...
}

The code

#include <stack>
#include <iostream>
#include <utility>
#include <csignal>
#include <cstring>

template<int...SIGS>
class sigguard{
public:
  // ctor
  sigguard(sighandler_t fsig):fsig_(fsig){
    setsigs(std::integer_sequence<int,SIGS...>());
  }
  // dtor
  ~sigguard(){
    while(!oldhandlers_.empty()){
      auto const[sig,fsig]=oldhandlers_.top();
      oldhandlers_.pop();
      signal(sig,fsig);
    }
  }
  private:
    // install signal handler for list of signals
    template<int...sigs>
    void setsigs(std::integer_sequence<int,sigs...>){
      using xxx=int[];
      (void)xxx{(setsig(sigs),0)...};
    }
    // set a single signal handler and save the old one so it can be set in the dtor
    void setsig(int sig){
      auto oldhandler=signal(sig,fsig_);
      if(oldhandler==SIG_ERR){
        throw std::runtime_error(std::string("could not set signal handler: ")+strsignal(sig));
      }
      oldhandlers_.push(std::make_pair(sig,oldhandler));
    }
    // old signal handlers
    std::stack<std::pair<int,sighandler_t>>oldhandlers_;

    // signal handler function
    sighandler_t fsig_;
};