ICE 3.4.2
[php5-ice-freebsdport.git] / cpp / src / IceUtil / CtrlCHandler.cpp
blobf4d150cc1fcc7373ac7727cef8daba66ad62a56b
1 // **********************************************************************
2 //
3 // Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
4 //
5 // This copy of Ice is licensed to you under the terms described in the
6 // ICE_LICENSE file included in this distribution.
7 //
8 // **********************************************************************
10 #ifdef __sun
11 # define _POSIX_PTHREAD_SEMANTICS
12 #endif
14 #include <IceUtil/CtrlCHandler.h>
15 #include <IceUtil/MutexPtrLock.h>
16 #include <IceUtil/Mutex.h>
18 #ifndef _WIN32
19 # include <signal.h>
20 #endif
22 using namespace std;
23 using namespace IceUtil;
25 namespace
28 CtrlCHandlerCallback _callback = 0;
29 const CtrlCHandler* _handler = 0;
31 IceUtil::Mutex* globalMutex = 0;
33 class Init
35 public:
37 Init()
39 globalMutex = new IceUtil::Mutex;
42 ~Init()
44 delete globalMutex;
45 globalMutex = 0;
49 Init init;
53 CtrlCHandlerException::CtrlCHandlerException(const char* file, int line) :
54 Exception(file, line)
58 static const char* ctrlCHandlerName = "IceUtil::CtrlCHandlerException";
60 string
61 CtrlCHandlerException::ice_name() const
63 return ctrlCHandlerName;
66 Exception*
67 CtrlCHandlerException::ice_clone() const
69 return new CtrlCHandlerException(*this);
72 void
73 CtrlCHandlerException::ice_throw() const
75 throw *this;
78 void
79 CtrlCHandler::setCallback(CtrlCHandlerCallback callback)
81 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(globalMutex);
82 _callback = callback;
85 CtrlCHandlerCallback
86 CtrlCHandler::getCallback() const
88 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(globalMutex);
89 return _callback;
92 #ifdef _WIN32
94 static BOOL WINAPI handlerRoutine(DWORD dwCtrlType)
96 CtrlCHandlerCallback callback = _handler->getCallback();
97 if(callback != 0)
99 callback(dwCtrlType);
101 return TRUE;
105 CtrlCHandler::CtrlCHandler(CtrlCHandlerCallback callback)
107 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(globalMutex);
108 bool handler = _handler != 0;
110 if(handler)
112 throw CtrlCHandlerException(__FILE__, __LINE__);
114 else
116 _callback = callback;
117 _handler = this;
118 lock.release();
120 SetConsoleCtrlHandler(handlerRoutine, TRUE);
124 CtrlCHandler::~CtrlCHandler()
126 SetConsoleCtrlHandler(handlerRoutine, FALSE);
128 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(globalMutex);
129 _handler = 0;
133 #else
135 extern "C"
138 static void*
139 sigwaitThread(void*)
141 sigset_t ctrlCLikeSignals;
142 sigemptyset(&ctrlCLikeSignals);
143 sigaddset(&ctrlCLikeSignals, SIGHUP);
144 sigaddset(&ctrlCLikeSignals, SIGINT);
145 sigaddset(&ctrlCLikeSignals, SIGTERM);
148 // Run until I'm cancelled (in sigwait())
150 for(;;)
152 int signal = 0;
153 int rc = sigwait(&ctrlCLikeSignals, &signal);
154 #if defined(__APPLE__)
156 // WORKAROUND: sigwait is not a cancelation point on MacOS X. To cancel this thread, the
157 // destructor cancels the thread and send a signal to the thread to unblock sigwait, then
158 // we explicitly test for cancellation.
160 pthread_testcancel();
161 #endif
163 // Some sigwait() implementations incorrectly return EINTR
164 // when interrupted by an unblocked caught signal
166 if(rc == EINTR)
168 continue;
170 assert(rc == 0);
172 rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
173 assert(rc == 0);
175 CtrlCHandlerCallback callback = _handler->getCallback();
177 if(callback != 0)
179 callback(signal);
182 rc = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
183 assert(rc == 0);
185 return 0;
190 namespace
193 pthread_t _tid;
197 CtrlCHandler::CtrlCHandler(CtrlCHandlerCallback callback)
199 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(globalMutex);
200 bool handler = _handler != 0;
202 if(handler)
204 throw CtrlCHandlerException(__FILE__, __LINE__);
206 else
208 _callback = callback;
209 _handler = this;
211 lock.release();
213 // We block these CTRL+C like signals in the main thread,
214 // and by default all other threads will inherit this signal
215 // mask.
217 sigset_t ctrlCLikeSignals;
218 sigemptyset(&ctrlCLikeSignals);
219 sigaddset(&ctrlCLikeSignals, SIGHUP);
220 sigaddset(&ctrlCLikeSignals, SIGINT);
221 sigaddset(&ctrlCLikeSignals, SIGTERM);
222 int rc = pthread_sigmask(SIG_BLOCK, &ctrlCLikeSignals, 0);
223 assert(rc == 0);
225 // Joinable thread
226 rc = pthread_create(&_tid, 0, sigwaitThread, 0);
227 assert(rc == 0);
231 CtrlCHandler::~CtrlCHandler()
233 int rc = pthread_cancel(_tid);
234 assert(rc == 0);
235 #if defined(__APPLE__)
237 // WORKAROUND: sigwait isn't a cancellation point on MacOS X, see
238 // comment in sigwaitThread
240 rc = pthread_kill(_tid, SIGTERM);
241 //assert(rc == 0); For some reaosns, this assert is sometime triggered
242 #endif
243 void* status = 0;
244 rc = pthread_join(_tid, &status);
245 assert(rc == 0);
246 #if !defined(__APPLE__)
247 assert(status == PTHREAD_CANCELED);
248 #endif
250 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(globalMutex);
251 _handler = 0;
255 #endif