0031681: Foundation Classes - Shared Libraries Cannot Be Loaded
[occt.git] / src / Standard / Standard_ErrorHandler.cxx
1 // Copyright (c) 1998-1999 Matra Datavision
2 // Copyright (c) 1999-2014 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 //============================================================================
16 //==== Titre: Standard_ErrorHandler.cxx
17 //==== Role : class "Standard_ErrorHandler" implementation.
18 //============================================================================
19 #include <Standard_ErrorHandler.hxx>
20 #include <Standard_Failure.hxx>
21 #include <Standard_Mutex.hxx>
22 #include <Standard.hxx>
23
24 #ifndef _WIN32
25 #include <pthread.h>
26 #else
27 #include <windows.h>
28 #endif
29
30 // ===========================================================================
31 // The class "Standard_ErrorHandler" variables
32 // ===========================================================================
33
34 // During [sig]setjmp()/[sig]longjmp() K_SETJMP is non zero (try)
35 // So if there is an abort request and if K_SETJMP is non zero, the abort
36 // request will be ignored. If the abort request do a raise during a setjmp
37 // or a longjmp, there will be a "terminating SEGV" impossible to handle.
38
39 //==== The top of the Errors Stack ===========================================
40 static Standard_ErrorHandler* Top = 0;
41
42 //! A mutex to protect from concurrent access to Top.
43 //! Mutex is defined as function to avoid issues caused by
44 //! an undefined static variables initialization order across compilation units (@sa #0031681 bug).
45 //! Note that we should NOT use Sentry while in this class, as Sentry
46 //! would register mutex as callback in the current exception handler.
47 static Standard_Mutex& GetMutex()
48 {
49   static Standard_Mutex theMutex;
50   return theMutex;
51 }
52
53 static inline Standard_ThreadId GetThreadID()
54 {
55 #ifndef _WIN32
56   return (Standard_ThreadId)pthread_self();
57 #else
58   return GetCurrentThreadId();
59 #endif
60 }
61
62 //============================================================================
63 //====  Constructor : Create a ErrorHandler structure. And add it at the 
64 //====                'Top' of "ErrorHandler's stack".
65 //============================================================================
66
67 Standard_ErrorHandler::Standard_ErrorHandler () : 
68        myStatus(Standard_HandlerVoid), myCallbackPtr(0)
69 {
70   myThread   = GetThreadID();
71   memset (&myLabel, 0, sizeof(myLabel));
72
73   GetMutex().Lock();
74   myPrevious = Top;
75   Top        = this;
76   GetMutex().Unlock();
77 }
78
79
80 //============================================================================
81 //==== Destructor : Delete the ErrorHandler and Abort if there is a 'Error'.
82 //============================================================================
83
84 void Standard_ErrorHandler::Destroy()
85 {
86   Unlink();
87   if (myStatus == Standard_HandlerJumped)
88   {
89     // jumped, but not caught
90     Abort (myCaughtError);
91   }
92 }
93
94
95 //=======================================================================
96 //function : Unlink
97 //purpose  : 
98 //=======================================================================
99
100 void Standard_ErrorHandler::Unlink()
101 {
102   // put a lock on the stack
103   GetMutex().Lock();
104   
105   Standard_ErrorHandler* aPrevious = 0;
106   Standard_ErrorHandler* aCurrent = Top;
107   
108   // locate this handler in the stack
109   while(aCurrent!=0 && this!=aCurrent) {
110     aPrevious = aCurrent;
111     aCurrent = aCurrent->myPrevious;
112   }
113   
114   if(aCurrent==0) {
115     GetMutex().Unlock();
116     return;
117   }
118   
119   if(aPrevious==0) {
120     // a top exception taken
121     Top = aCurrent->myPrevious;
122   }
123   else {
124     aPrevious->myPrevious=aCurrent->myPrevious;
125   }
126   myPrevious = 0;
127   GetMutex().Unlock();
128
129   // unlink and destroy all registered callbacks
130   Standard_Address aPtr = aCurrent->myCallbackPtr;
131   myCallbackPtr = 0;
132   while ( aPtr ) {
133     Standard_ErrorHandler::Callback* aCallback = (Standard_ErrorHandler::Callback*)aPtr;
134     aPtr = aCallback->myNext;
135     // Call destructor explicitly, as we know that it will not be called automatically
136     aCallback->DestroyCallback();
137   }
138 }
139
140 //=======================================================================
141 //function : IsInTryBlock
142 //purpose  :  test if the code is currently running in
143 //=======================================================================
144
145 Standard_Boolean Standard_ErrorHandler::IsInTryBlock()
146 {
147   Standard_ErrorHandler* anActive = FindHandler(Standard_HandlerVoid, Standard_False);
148   return anActive != NULL;
149 }
150
151
152 //============================================================================
153 //==== Abort: make a longjmp to the saved Context.
154 //====    Abort if there is a non null 'Error'
155 //============================================================================
156
157 void Standard_ErrorHandler::Abort (const Handle(Standard_Failure)& theError)
158 {
159   Standard_ErrorHandler* anActive = FindHandler(Standard_HandlerVoid, Standard_True);
160
161   //==== Check if can do the "longjmp" =======================================
162   if(anActive == NULL) {
163     std::cerr << "*** Abort *** an exception was raised, but no catch was found." << std::endl;
164     if (!theError.IsNull())
165       std::cerr << "\t... The exception is:" << theError->GetMessageString() << std::endl;
166     exit(1);
167   }
168
169   anActive->myStatus = Standard_HandlerJumped;
170   longjmp(anActive->myLabel, Standard_True);
171 }
172
173
174 //============================================================================
175 //==== Catches: If there is a 'Error', and it is in good type 
176 //====          returns True and clean 'Error', else returns False.
177 //============================================================================
178
179 Standard_Boolean Standard_ErrorHandler::Catches (const Handle(Standard_Type)& AType) 
180 {
181   Standard_ErrorHandler* anActive = FindHandler(Standard_HandlerJumped, Standard_False);
182   if(anActive==0)
183     return Standard_False;
184   
185   if(anActive->myCaughtError.IsNull())
186     return Standard_False;
187
188   if(anActive->myCaughtError->IsKind(AType)){
189     myStatus=Standard_HandlerProcessed;
190     return Standard_True;
191   } else {
192     return Standard_False;
193   }
194 }
195
196 Handle(Standard_Failure) Standard_ErrorHandler::LastCaughtError()
197 {
198   Handle(Standard_Failure) aHandle;
199   Standard_ErrorHandler* anActive = FindHandler(Standard_HandlerProcessed, Standard_False);
200   if(anActive!=0) 
201     aHandle = anActive->myCaughtError;
202   
203   return aHandle;
204 }
205
206 Handle(Standard_Failure) Standard_ErrorHandler::Error() const
207 {
208   return myCaughtError;
209 }
210
211
212 void Standard_ErrorHandler::Error (const Handle(Standard_Failure)& theError)
213 {
214   Standard_ErrorHandler* anActive = FindHandler (Standard_HandlerVoid, Standard_False);
215   if (anActive == NULL)
216     Abort (theError);
217
218   anActive->myCaughtError = theError;
219 }
220
221
222 Standard_ErrorHandler* Standard_ErrorHandler::FindHandler(const Standard_HandlerStatus theStatus,
223                                                           const Standard_Boolean theUnlink)
224 {
225   // lock the stack
226   GetMutex().Lock();
227     
228   // Find the current ErrorHandler Accordin tread
229   Standard_ErrorHandler* aPrevious = 0;
230   Standard_ErrorHandler* aCurrent = Top;
231   Standard_ErrorHandler* anActive = 0;
232   Standard_Boolean aStop = Standard_False;
233   Standard_ThreadId aTreadId = GetThreadID();
234   
235   // searching an exception with correct ID number
236   // which is not processed for the moment
237   while(!aStop) {
238     while(aCurrent!=NULL && aTreadId!=aCurrent->myThread) {
239       aPrevious = aCurrent;
240       aCurrent = aCurrent->myPrevious;
241     }
242     
243     if(aCurrent!=NULL) {
244       if(theStatus!=aCurrent->myStatus) {
245         
246         if(theUnlink) {
247           //unlink current
248           if(aPrevious==0) {
249             // a top exception taken
250             Top = aCurrent->myPrevious;
251           }
252           else {
253             aPrevious->myPrevious=aCurrent->myPrevious;
254           }
255         }
256         
257         //shift
258         aCurrent = aCurrent->myPrevious;
259       }
260       else {
261         //found one
262         anActive = aCurrent;
263         aStop = Standard_True;
264       }
265     }
266     else {
267       //Current is NULL, means that no handlesr
268       aStop = Standard_True;
269     }
270   }
271   GetMutex().Unlock();
272   
273   return anActive;
274 }
275
276 #if defined(OCC_CONVERT_SIGNALS)
277
278 Standard_ErrorHandler::Callback::Callback ()
279   : myHandler(0), myPrev(0), myNext(0)
280 {
281 }
282
283 Standard_ErrorHandler::Callback::~Callback ()
284 {
285   UnregisterCallback();
286 }
287
288 void Standard_ErrorHandler::Callback::RegisterCallback ()
289 {
290   if ( myHandler ) return; // already registered
291
292   // find current active exception handler
293   Standard_ErrorHandler *aHandler =
294     Standard_ErrorHandler::FindHandler(Standard_HandlerVoid, Standard_False);
295
296   // if found, add this callback object first to the list
297   if ( aHandler ) {
298     myHandler = aHandler;
299     myNext = aHandler->myCallbackPtr;
300     if ( myNext ) ((Standard_ErrorHandler::Callback*)myNext)->myPrev = this;
301     aHandler->myCallbackPtr = this;
302   }
303 }
304
305 void Standard_ErrorHandler::Callback::UnregisterCallback ()
306 {
307   if ( ! myHandler ) return;
308   if ( myNext )
309     ((Standard_ErrorHandler::Callback*)myNext)->myPrev = myPrev;
310   if ( myPrev )
311     ((Standard_ErrorHandler::Callback*)myPrev)->myNext = myNext;
312   else if ( ((Standard_ErrorHandler*)myHandler)->myCallbackPtr == this)
313     ((Standard_ErrorHandler*)myHandler)->myCallbackPtr = (Standard_ErrorHandler::Callback*)myNext;
314   myHandler = myNext = myPrev = 0;
315 }
316 #endif