Advertisement
homer512

Defer signal

Nov 7th, 2015
117
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.64 KB | None | 0 0
  1. /* Copyright 2015 Florian Philipp
  2.  *
  3.  * Licensed under the Apache License, Version 2.0 (the "License");
  4.  * you may not use this file except in compliance with the License.
  5.  * You may obtain a copy of the License at
  6.  *
  7.  *     http://www.apache.org/licenses/LICENSE-2.0
  8.  *
  9.  * Unless required by applicable law or agreed to in writing, software
  10.  * distributed under the License is distributed on an "AS IS" BASIS,
  11.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12.  * See the License for the specific language governing permissions and
  13.  * limitations under the License.
  14.  */
  15.  
  16. #include <signal.h>
  17. // using sigaction, pthread_sigmask
  18.  
  19. #include <arpa/inet.h>
  20. // using htons
  21. #include <netinet/ip.h>
  22. // using accept, bind, listen, socket
  23.  
  24. #include <unistd.h>
  25. // using close
  26.  
  27. #include <atomic>
  28. // using std::atomic_signal_fence
  29. #include <cstring>
  30. // using std::memset
  31. #include <system_error>
  32. // using std::system_error, std::system_category
  33. #include <cerrno>
  34. // using errno
  35. #include <iostream>
  36. // using std::cout
  37. #include <array>
  38. // using std::array
  39. #include <cassert>
  40. // using assert
  41.  
  42.  
  43. namespace {
  44.  
  45.   /**
  46.    * Throws an std::system_error using errno
  47.    */
  48.   void make_system_error(const char* operation)
  49.   {
  50.     throw std::system_error(errno, std::system_category(), operation);
  51.   }
  52.  
  53.   /**
  54.    * RAII for an installed signal handler. Dtor restores old behavior
  55.    */
  56.   class AlteredSignal
  57.   {
  58.     int signal;
  59.     struct sigaction orig;
  60.   public:
  61.     typedef void (*signal_handler_t)(int);
  62.  
  63.     AlteredSignal() noexcept : signal(-1) {}
  64.     AlteredSignal(int signal, signal_handler_t handler)
  65.       : signal(signal)
  66.     {
  67.       struct sigaction action;
  68.       std::memset(&action, 0, sizeof(action));
  69.       action.sa_handler = handler;
  70.       if(sigaction(signal, &action, &orig))
  71.     make_system_error("sigaction");
  72.     }
  73.     AlteredSignal(const AlteredSignal&) = delete;
  74.     AlteredSignal(AlteredSignal&& o) noexcept
  75.     {
  76.       signal = o.signal;
  77.       o.signal = -1;
  78.       orig = o.orig;
  79.     }
  80.     ~AlteredSignal()
  81.     {
  82.       if(signal >= 0)
  83.     sigaction(signal, &orig, nullptr);
  84.     }
  85.     AlteredSignal& operator=(const AlteredSignal&) = delete;
  86.     AlteredSignal& operator=(AlteredSignal&& o)
  87.     {
  88.       if(signal >= 0 && sigaction(signal, &orig, nullptr))
  89.     make_system_error("sigaction");
  90.       signal = o.signal;
  91.       if(o.signal >= 0) {
  92.     orig = o.orig;
  93.     o.signal = -1;
  94.       }
  95.       return *this;
  96.     }
  97.   };
  98.  
  99.   /**
  100.    * RAII for blocked signals. Dtor unblocks signals again
  101.    */
  102.   class SignalBlock
  103.   {
  104.     sigset_t orig;
  105.     bool active;
  106.   public:
  107.     SignalBlock() noexcept : orig(), active(false) {}
  108.     explicit SignalBlock(const sigset_t& mask)
  109.       : orig(), active(true)
  110.     {
  111.       if(pthread_sigmask(SIG_BLOCK, &mask, &orig))
  112.     make_system_error("pthread_sigmask");
  113.     }
  114.     SignalBlock(const SignalBlock&) = delete;
  115.     SignalBlock(SignalBlock&& o) noexcept
  116.       : orig(o.orig), active(o.active)
  117.     {
  118.       o.active = false;
  119.     }
  120.     ~SignalBlock()
  121.     {
  122.       if(active)
  123.     pthread_sigmask(SIG_SETMASK, &orig, nullptr);
  124.     }
  125.     SignalBlock& operator=(const SignalBlock&) = delete;
  126.     SignalBlock& operator=(SignalBlock&& o)
  127.     {
  128.       if(active && pthread_sigmask(SIG_SETMASK, &orig, nullptr))
  129.     make_system_error("pthread_sigmask");
  130.       active = o.active;
  131.       if(o.active) {
  132.     orig = o.orig;
  133.     o.active = false;
  134.       }
  135.       return *this;
  136.     }
  137.     /**
  138.      * Signal mask that can be used to unblock signals in pselect or ppoll
  139.      */
  140.     const sigset_t& unmask_set() const noexcept
  141.     { return orig; }
  142.   };
  143.  
  144.   /**
  145.    * Installs a signal handler that sets a flag on SIGINT and SIGTERM
  146.    */
  147.   class TerminationFlag
  148.   {
  149.     static bool termflag;
  150.     static const std::array<int, 2> caught_signals;
  151.  
  152.     std::array<AlteredSignal, 2> altered;
  153.  
  154.     /**
  155.      * Signal handler
  156.      */
  157.     static void term_action(int) noexcept
  158.     {
  159.       termflag = true;
  160.       std::atomic_signal_fence(std::memory_order_release);
  161.     }
  162.   public:
  163.     TerminationFlag()
  164.     {
  165.       for(std::size_t i = 0; i < caught_signals.size(); ++i)
  166.     altered[i] = AlteredSignal(caught_signals[i], term_action);
  167.     }
  168.  
  169.     /**
  170.      * Blocks SIGTERM and SIGINT until the returned object is destroyed
  171.      */
  172.     SignalBlock block()
  173.     {
  174.       sigset_t mask;
  175.       if(sigemptyset(&mask))
  176.     make_system_error("sigemptyset");
  177.       for(int sig: caught_signals)
  178.     if(sigaddset(&mask, sig))
  179.       make_system_error("sigaddset");
  180.       return SignalBlock(mask);
  181.     }
  182.  
  183.     /**
  184.      * True if SIGTERM or SIGINT have been received
  185.      */
  186.     bool triggered() const noexcept
  187.     {
  188.       bool val = termflag;
  189.       std::atomic_signal_fence(std::memory_order_acquire);
  190.       return val;
  191.     }
  192.   };
  193.  
  194.   bool TerminationFlag::termflag;
  195.   const std::array<int, 2> TerminationFlag::caught_signals = {
  196.     SIGTERM, SIGINT
  197.   };
  198.  
  199.   /**
  200.    * RAII for a socket or any kind of file descriptor
  201.    */
  202.   struct Socket
  203.   {
  204.     int fd;
  205.     explicit Socket(int fd = -1) noexcept
  206.       : fd(fd) {}
  207.     ~Socket()
  208.     {
  209.       if(fd >= 0)
  210.     close(fd);
  211.     }
  212.     Socket(const Socket&) = delete;
  213.     Socket(Socket&& o) noexcept
  214.       : fd(o.fd)
  215.     {
  216.       o.fd = -1;
  217.     }
  218.     explicit operator bool() const noexcept
  219.     { return fd >= 0; }
  220.     Socket& operator=(const Socket&) = delete;
  221.     Socket& operator=(Socket&& o)
  222.     {
  223.       using std::swap;
  224.       swap(fd, o.fd);
  225.       return *this;
  226.     }
  227.   };
  228.  
  229.   /**
  230.    * Listens on 127.0.0.1:1337 for connections
  231.    */
  232.   class Listener
  233.   {
  234.     Socket sock;
  235.   public:
  236.     Listener()
  237.     {
  238.       if((sock.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  239.     make_system_error("socket");
  240.       struct sockaddr_in addr;
  241.       std::memset(&addr, 0, sizeof(addr));
  242.       addr.sin_family = AF_INET;
  243.       addr.sin_port = htons(1337);
  244.       addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  245.       if(bind(sock.fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)))
  246.     make_system_error("bind");
  247.       if(listen(sock.fd, 128))
  248.     make_system_error("listen");
  249.     }
  250.  
  251.     /**
  252.      * Blocking call to accept a new connection
  253.      */
  254.     Socket accept()
  255.     {
  256.       int fd;
  257.       if((fd = ::accept(sock.fd, nullptr, 0)) < 0)
  258.     make_system_error("accept");
  259.       return Socket(fd);
  260.     }
  261.  
  262.     /**
  263.      * Blocking, interruptible call to accept a new connection
  264.      *
  265.      * \param sigmask signal mask for pselect
  266.      */
  267.     Socket poll(const sigset_t& sigmask)
  268.     {
  269.       fd_set read_fds;
  270.       FD_ZERO(&read_fds);
  271.       FD_SET(sock.fd, &read_fds);
  272.       int n_fds;
  273.       if((n_fds = pselect(sock.fd + 1, &read_fds, nullptr /*write*/,
  274.               nullptr /*except*/, nullptr /*timeout*/, &sigmask))
  275.      < 0)
  276.     make_system_error("pselect");
  277.       assert(n_fds == 1);
  278.       assert(FD_ISSET(sock.fd, &read_fds));
  279.       return accept();
  280.     }
  281.   };
  282.  
  283.   /**
  284.    * TODO: Actually process the client.
  285.    */
  286.   void handle_client(Socket)
  287.   {}
  288. }
  289.  
  290. int main()
  291. {
  292.   TerminationFlag termination;
  293.   Listener listener;
  294.   while(1) {
  295.     Socket sock;
  296.     {
  297.       SignalBlock block = termination.block();
  298.       /* important: Block signals before checking them
  299.        * to avoid race condition
  300.        */
  301.       if(termination.triggered())
  302.     break;
  303.       try {
  304.     /* Now use pselect or ppoll to wait for a client with unmasked signals.
  305.      * A signal will cause an EINTR. For simplicity, we check this in the
  306.      * next iteration
  307.      */
  308.     sock = listener.poll(block.unmask_set());
  309.       } catch(std::system_error& err) {
  310.     if(err.code().value() == EINTR)
  311.       continue;
  312.     throw;
  313.       }
  314.     }
  315.     handle_client(std::move(sock));
  316.   }
  317.   std::cout << "Clean termination\n";
  318. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement