See main.cpp. If you don't see the significance of this class for the C++ components, then you might be a Java developer who has been enjoying this level of exception handling for as long as you could remember :-). If you are enjoying what you see, then by all means proceed to UnixSignals.h and send in a peer review if you see something that worries you.

Console Output for main is:

Caught exception Segmentation violation (ANSI)
Default HUP handler called
Default HUP handler called
Caught exception Segmentation violation (ANSI)
Thread 1 - Segmentation violation (ANSI)
Thread 2 - Segmentation violation (ANSI)
Default Segmentation fault handler called

Joegen
/* 
 * File:   main.cpp
 * Author: joegen
 *
 * Created on May 3, 2011, 9:07 AM
 */

#include <cstdlib>
#include <iostream>
#include "StackTrace.h"
#include "UnixSignals.h"
#include <boost/thread.hpp>

using namespace std;

/*
 * 
 */

void sigsegv_handler()
{
  std::cout << "Default Segmentation fault handler called" << std::endl;
  std::cout.flush();
}

void sighup_handler()
{
  std::cout << "Default HUP handler called" << std::endl;
  std::cout.flush();
}

void t1()
{
  try
  {
    TrapUnixSignals;
    std::string* pstring;
    std::cout << pstring->c_str() << std::endl;
  }
  catch(Os::UnixSignalTrap& e)
  {
    std::cout << "Thread 1 - " << e.what() << std::endl;
  }
}

void t2()
{
  try
  {
    TrapUnixSignals;
    std::string* pstring;
    std::cout << pstring->c_str() << std::endl;
  }
  catch(Os::UnixSignalTrap& e)
  {
    std::cout << "Thread 2 - " << e.what() << std::endl;
  }
}


int main(int argc, char** argv)
{
  Os::UnixSignals::instance().registerSignalHandler(SIGSEGV, 
boost::bind(sigsegv_handler));
  Os::UnixSignals::instance().registerSignalHandler(SIGHUP, 
boost::bind(sighup_handler));
  
  try
  {
    TrapUnixSignals;
    std::string* pstring;
    std::cout << pstring->c_str() << std::endl;
  }
  catch(Os::UnixSignalTrap& e)
  {
    std::cout << "Caught exception " << e.what() << std::endl;
  }

  try
  {
    //
    // SIGHUP is not an exception so this won't be caught here and instead 
propagated to the default handler
    //
    TrapUnixSignals;
    ::raise(SIGHUP);
  }
  catch(Os::UnixSignalTrap& e)
  {
    std::cout << "Caught exception " << e.what() << std::endl;
  }

  //
  // This will be caught by the default handler
  //
  ::raise(SIGHUP);

  try
  {
    TrapUnixSignals;
    std::string* pstring;
    std::cout << pstring->c_str() << std::endl;
  }
  catch(Os::UnixSignalTrap& e)
  {
    std::cout << "Caught exception " << e.what() << std::endl;
  }
  
  boost::thread _t1(boost::bind(t1));
  boost::thread _t2(boost::bind(t2));
  _t1.join();
  _t2.join();

  //
  // This will be propagated to the default handler and call an abort
  //
  std::string* pstring;
  std::cout << pstring->c_str() << std::endl;

  return 0;
}

/*
 * Copyright (c) 2011 eZuce, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

#ifndef UNIXSIGNALS_H
#define	UNIXSIGNALS_H

#include <signal.h>
#include <pthread.h>
#include <setjmp.h>
#include <map>
#include <vector>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>

namespace Os
{
  namespace detail
  {
    typedef boost::function<void()> Handler;
    typedef std::vector<Handler> HandlerList;
    typedef std::map<int, HandlerList> HandlerMap;
    typedef boost::lock_guard<boost::recursive_mutex> mutex_lock;
    static HandlerMap _handlers;
    static boost::recursive_mutex _handlersMutex;


    //
    // The jump vector for each thread
    //
    struct JumpBuffer{sigjmp_buf buf;};
    typedef std::vector<JumpBuffer> JumpBufferVec;
    typedef std::map<pthread_t, JumpBufferVec> JumpBuffers;
    static JumpBuffers _jumpBuffers;
    static boost::recursive_mutex _jumpBufferMutex;

    static sigjmp_buf& _pushJumpBuffer()
    {
      mutex_lock lock(_jumpBufferMutex);
      pthread_t tid = ::pthread_self();
      JumpBuffers::iterator jumpBuffer = _jumpBuffers.find(tid);
      if (jumpBuffer == _jumpBuffers.end())
      {
        JumpBufferVec jumpVec;
        _jumpBuffers[tid] = jumpVec;
        JumpBuffer buf;
        _jumpBuffers[tid].push_back(buf);
        return _jumpBuffers[tid].back().buf;
      }
      else
      {
        JumpBuffer buf;
        jumpBuffer->second.push_back(buf);
        return jumpBuffer->second.back().buf;
      }
    }

    static void _popJumpBuffer()
    {
      _jumpBufferMutex.lock();
      pthread_t tid = ::pthread_self();
      JumpBuffers::iterator jumpBuffer = _jumpBuffers.find(tid);
      if (jumpBuffer != _jumpBuffers.end())
      {
        jumpBuffer->second.pop_back();
      }
      _jumpBufferMutex.unlock();
    }

    sigjmp_buf* _getJumpBuffer()
    {
      mutex_lock lock(_jumpBufferMutex);
      pthread_t tid = ::pthread_self();
      JumpBuffers::iterator jumpBuffer = _jumpBuffers.find(tid);
      if (jumpBuffer != _jumpBuffers.end() && !jumpBuffer->second.empty())
        return &(jumpBuffer->second.back().buf);
      return 0;
    }

    static void _handle_signal(int sig)
    {
      if (sig == SIGILL ||
        sig == SIGBUS ||
        sig == SIGSEGV ||
        sig == SIGSYS)
      {
        _jumpBufferMutex.lock();
        //
        // Check if a jump buffer is registered
        //
        sigjmp_buf* pJumpBuff = detail::_getJumpBuffer();
        if (pJumpBuff)
        {
          //
          // Jump to the calling thread if a jump buffer is set
          //
          //
          // Make sure we unlock the mutex before we jump
          //
          _jumpBufferMutex.unlock();
          ::siglongjmp(*pJumpBuff, sig);
        }
        _jumpBufferMutex.unlock();
      }

      _handlersMutex.lock();
      HandlerMap::iterator callBacks = _handlers.find(sig);
      if (callBacks != _handlers.end())
      {
        for (HandlerList::iterator iter = callBacks->second.begin(); iter != callBacks->second.end(); iter++)
        {
          Handler& handler = *iter;
          handler();
        }
      }
      _handlersMutex.unlock();

      if (sig == SIGILL ||
        sig == SIGBUS ||
        sig == SIGSEGV ||
        sig == SIGSYS)
        std::abort();
    }
  }

  class UnixSignals
  {
  public:
    typedef detail::Handler Handler;

    static UnixSignals& instance()
    {
      static UnixSignals me;
      static bool initialized = false;
      if (!initialized)
      {
        //
        // Do some initialization stuffs here
        //
        initialized = true;
      }
      return me;
    };

    void registerSignalHandler(int sig, Handler handler)
    {
      detail::_handlersMutex.lock();
      detail::HandlerMap::iterator handlers = detail::_handlers.find(sig);
      if (handlers == detail::_handlers.end())
      {
        struct sigaction sa;
        sa.sa_handler = detail::_handle_signal;
        sa.sa_flags   = 0;
        ::sigemptyset(&sa.sa_mask);
        ::sigaction(sig,  &sa, 0);

        detail::HandlerList handlerList;
        handlerList.push_back(handler);
        detail::_handlers[sig] = handlerList;
      }
      else
      {
        handlers->second.push_back(handler);
      }
      detail::_handlersMutex.unlock();
    }

  private:
    UnixSignals(){}
    UnixSignals(const UnixSignals&){}
  };

  class UnixSignalTrap : public std::exception
  {
  public:
    UnixSignalTrap() :
      _jumpBuffer(0),
      _sig(0)
    {
    }

    UnixSignalTrap(int sig) :
      _jumpBuffer(0),
      _sig(sig)
    {
    }

    ~UnixSignalTrap() throw()
    {
      if (_jumpBuffer)
        Os::detail::_popJumpBuffer();
    }

    void throwSignalException(int sig)
    {
      if (sig)
        throw UnixSignalTrap(sig);
    }

    const char* what() const throw()
    {
      switch (_sig)
      {
        case	SIGHUP:
          return "Hangup (POSIX)";
        case	SIGINT:
          return "Interrupt (ANSI)";
        case	SIGQUIT:
          return "Quit (POSIX)";
        case	SIGILL:
          return "Illegal instruction (ANSI)";
        case	SIGTRAP:
          return "Trace trap (POSIX)";
        case	SIGABRT:
          return "Abort (ANSI)";
        case	SIGBUS:
          return "BUS error (4.2 BSD)";
        case	SIGFPE:
          return "Floating-point exception (ANSI)";
        case	SIGKILL:
          return "Kill, unblockable (POSIX)";
        case	SIGUSR1:
          return "User-defined signal 1 (POSIX)";
        case	SIGSEGV:
          return "Segmentation violation (ANSI)";
        case	SIGUSR2:
          return "User-defined signal 2 (POSIX)";
        case	SIGPIPE:
          return "Broken pipe (POSIX)";
        case	SIGALRM:
          return "Alarm clock (POSIX)";
        case	SIGTERM:
          return "Termination (ANSI)";
        case	SIGSTKFLT:
          return "Stack fault";
        case	SIGCHLD:
          return "Child status has changed (POSIX)";
        case	SIGCONT:
          return "Continue (POSIX)";
        case	SIGSTOP:
          return "Stop, unblockable (POSIX)";
        case	SIGTSTP:
          return "Keyboard stop (POSIX)";
        case	SIGTTIN:
          return "Background read from tty (POSIX)";
        case	SIGTTOU:
          return "Background write to tty (POSIX)";
        case	SIGURG:
          return "Urgent condition on socket (4.2 BSD)";
        case	SIGXCPU:
          return "CPU limit exceeded (4.2 BSD)";
        case	SIGXFSZ:
          return "File size limit exceeded (4.2 BSD)";
        case	SIGVTALRM:
          return "Virtual alarm clock (4.2 BSD)";
        case	SIGPROF:
          return "Profiling alarm clock (4.2 BSD)";
        case	SIGWINCH:
          return "Window size change (4.3 BSD, Sun)";
        case	SIGPOLL:
          return "Pollable event occurred (System V)";
        case	SIGPWR:
          return "Power failure restart (System V)";
        case SIGSYS:
          return "Bad system call";
        default:
          return "Unknown Signal Caught";
      }
    }

    int sig() const{ return _sig; }

    sigjmp_buf* jumpBuffer()
    {
      if (!_jumpBuffer)
        _jumpBuffer = &(Os::detail::_pushJumpBuffer());
      return _jumpBuffer;
    }
  private:
    sigjmp_buf* _jumpBuffer;
    int _sig;
  };

  #define TrapUnixSignals Os::UnixSignalTrap __os_signal_trap__; \
  __os_signal_trap__.throwSignalException(::sigsetjmp(*(__os_signal_trap__.jumpBuffer()), 1));

}


#endif	/* UNIXSIGNALS_H */

_______________________________________________
sipx-dev mailing list
[email protected]
List Archive: http://list.sipfoundry.org/archive/sipx-dev/

Reply via email to