Report problems to ATLAS LXR Team (with time and IP address indicated)

The LXR Cross Referencer

source navigation ]
diff markup ]
identifier search ]
general search ]
 
 
Architecture: linux ]
Version: head ] [ nightly ] [ GaudiDev ]
  Links to LXR source navigation pages for stable releases [ 12.*.* ]   [ 13.*.* ]   [ 14.*.* ] 

001                 ZOOM Exception Mechanism Design
002                 -------------------------------
003 
004 The Zoom exception mechanism is intended to be the tools used by ZOOM modules
005 to handle exceptions in a useful way and provide the user with interfaces to
006 control how to react to problems.  It is not (at this time) intended to be a
007 mechanism for the user to hook into to define his own exceptions, although that
008 may come later.  Frankly, successful implementation and use by ZOOM modules will
009 be necessary to sell this structure to our users as potentially useful, anyway.
010 
011 We need the mechanism "now" because we are trying to deliver at least two
012 packages which will have to be re-instrumented when the mechanism is finally
013 adopted.
014 
015 This is not a "signal handler" mechanism, which could be used to establish
016 behavior for segment violations, arthmetic exceptions, and so forth.  It
017 would be nice to have this but C++ does not provide generic support for
018 those activities.  A **later** possibility will be to provide this support for
019 POSIX-compliant operating systems, and coordinate it with the exception
020 mechanism.
021 
022 
023 We need to be able to cope with the following realities:
024 --------------------------------------------------------
025 
026 1 - Users will not, in every case, imbed calls into a try block.
027 
028 2 - Frameworks will incorporate code from multiple users and in some
029     circumstances cannot afford to abort the entire job if some rare path
030     in one user's code blows up.  To the extent we can help with this, we must.
031 
032 3 - Framework creators CAN be expected to imbed portions in try blocks, or
033     set up behaviors for various exceptions, assuming we give them the necessary
034     tools.
035 
036     In the remainder of this document, the "user" who sets things up is assmued
037     to be such a framework manager.
038 
039 4 - There is need for coordinated logging and related behavior.
040 
041 
042 To be able to satisfy this, the goal is to allow:
043 -------------------------------------------------
044 
045 1 - The user should be able to specify, for a given sort of exception, whether
046     she wants to:
047 
048     a - Throw the exception via the C++ mechanism, thus aborting unless she
049         in the framework or a lower-level user responsible catchs the problem.
050 
051     b - Invoke a user-written handler when the exception occurs, which may
052         itself determine it is necessary to throw the exception.
053 
054     c - Ignore the exception RETURNING TO THE SPOT IN THE ZOOM MODULE
055         THAT DETECTED THE PROBLEM.  This can happen with or without a handler
056         being invoked.  Typically, the module will then return some approriate
057         pseudo-value to the user.
058 
059 2 - In cases where exceptions are to be handled or ignored, there should be
060     a well-known way to get information about the existance of a problem,
061     analogous to the errno mechanism in C.
062 
063 3 - Explanatory strings should be associated with the problem at the point
064     of origin.
065 
066 4 - The exceptions are organized in a hierarchical manner, which uniformly
067     applies one simple category philosophy that the users can understand,
068     across the various ZOOM packages.
069 
070 
071 With this in mind, our mechanism has the following structure:
072 -------------------------------------------------------------
073 
074 
075 1 - Upon detecting a problem for which it would like to (potential) throw an
076     exception, the module code will instead invoke the ZMthrow macro.
077 
078 2 - ZMthrow takes as its argument a ZMexception object constructor, which
079     has as ITS first argument a const char*.  The intent is for the ZMexception
080     object actually do be derived from the base ZMexception class, and for
081     the char* first argument to contain an explanatory message.  Particular
082     ZMexceptions can also have construction forms taking a second string, or
083     whatever.
084     For example,
085         ZMthrow ( HepTuple::ZMxCapture( "Column not found", name ) );
086 
087 3 - The ZMthrow macro appends __LINE__, __FILE__ and calls ZMexcept.
088 
089 4 - ZMexcept has signature
090 
091         void ZMexcept ( ZMexception x, int line,
092                                 char file[], char data[], char time[]);
093 
094     It does the following:
095 
096     a - Places x.ZMexceptionId into a circular buffer ZMerrno.  Actually,
097         constructing the ZMexcept object does that.  More about ZMerrno later.
098 
099     b - Determines for this exception what sort of logging is enabled, and logs
100         it.  Note that derived exceptions must modify the general logging
101         method if they wish to include information beyond the message and
102         file/line/time stamp.
103 
104     c - Determines whether a handler has been established, and if so invokes it.
105         The handler will be passed the exception object, and can be set up to
106         also take the line/file/time arguments.  The handler returns a bool
107         which if true will say to throw the exception.
108 
109     d - Determines (after any handler has been invoked) whether to ignore the
110         exception.  It will do either
111                 throw x;
112         or
113                 return;
114 
115 5 - ZMerrno is analogous to the C/Unix errno mechanism, but allows viewing a
116     history of the last N problems detected.  We anticipate using it like errno,
117     via exception id, but we copies of place the whole exception object onto
118     ZMerrno to provide more info if desired.  The interface is:
119 
120     a - ((creation somehow, specifying N))
121 
122     b - ZMerrno.write (ZMexcption* x);    // copy an exception onto ZMerrno
123 
124     The user would not use a and b but would use:
125 
126     c - int ZMerrno.read ();       // read the last id value on ZMerrno
127         int ZMerrno.read (int k);  // read the last-but-k id value on ZMerrno
128 
129     d - void ZMerrno.clear();      // put a zero (indicating no current error)
130                                    // on ZMerrno.
131 
132     e - void ZMerrno.pop();        // remove an entry setting the top to the
133                                    // previous entry.  For instance, if you
134                                    // have a loop in which some known ignorable
135                                    // happening places a value on ZMerrno, you
136                                    // can pop each one so as not to wipe out
137                                    // the history for others.
138 
139     f - ZMexception* ZMerrno.get()      // Return pointer to the last or
140         ZMexception* ZMerrno.get(int k) // last-but-k exception on ZMerrno.
141                                         // Allows perusal of things like the
142                                         // message and the logger and handler
143                                         // used when the exception was
144                                         // encountered.
145 
146     Thus after the start, or after ZMerrno.clear(), the user can always find
147     out whether any ignored exceptions had occured by doing
148         if (ZMerrno.read()) { ... then something has happened }
149 
150     If we later incorporate signal handling, this ZMerrno stack is where the
151     user can find out whether he has had his arithmetic error since the last
152     clear.
153 
154     ZMerrno is pronounced "oops."
155 
156 6 - The id 0 indicates no error.  Each upper 16-bit value is assigned to a
157     module to avoid conflicts; the values starting with upper bit set are
158     reserved for user definition at a later date.  The lower 16 bits distinguish
159     the specific error.
160 
161     Each ZMexception object class has its own error id which is established
162     (hardwired) at construction.
163 
164     The ZMexception codes for the upper 16 bits are defined in ZMexception.h.
165     The lower 16 bits, for each module are defined in files with names like
166     HepTupleZMexception.h.
167 
168 7 - When a ZMexcept exception is thrown it does a ZMerrno.write(this.id).  Thus
169     ZMerrno tracks these exceptions even if they are explicitly thrown and
170     caught by the user outside the ZMthrow/ZMexception mechanism.
171 
172 8 - Logging is discussed separately.
173 
174 
175 The user interface to control exception behavior is:
176 -----------------------------------------------------
177 
178 By the way, this is an interface for the framework manager to use; the lower
179 level user would generally never establish handlers or ignores, and would
180 only use the ZMerrno.read() and .clear().
181 
182 1 - To establish a handler for a particular exception type, say for
183     ZMexceptCapture:
184 
185         HepTuple::ZMxCapture.setHandler ( myhandler, "handlerName" );
186 
187         The handlerName string gives a convenient way to tell about the
188         handling in log messages ("... was handled by mySuperHandler").
189 
190     The signature of the handler must be:
191         bool myhandler ( ZMexception x );
192     We will have a ZMhandler class to contain that and the handlerName.
193 
194     Note that the handler has access to the fields of x including for all
195     ZMexception objects:
196                 char* message;
197                 int line;
198                 char* sourceFileName;
199                 int serialNumber;
200                 ZMhandler handler;
201                 ZMlogger  logger;
202                 // and class-wide data:
203                 static int id;
204                 static char* messagePreamble;
205                 static int count;
206     and, for particular derived ZMexception objects, any secondary messages
207     or other information provided to the constructor and kept in the object.
208 
209     The handler should return false to ignore the exception and return to
210     the user code (from ZMexcept), or true to throw the exception after
211     the handler returns.  The user can also throw an exception explicitly
212     in the handler.
213 
214 2 - You may establish a handler for an entire base class, which applies to any
215     ZMexceptions derived from in that class for which no specific handler is
216     established.  For example, HepTuple::ZMxNewColumn is derived
217     from HepTuple::ZMxGeneral so
218 
219         HepTuple::ZMxGeneral.setHandler ( myDefaultHandler );
220 
221     will apply to HepTuple::ZMxNewColumn if that is ever ZMthrown.
222 
223 3 - Note that if a handler is established for a subclass it takes precedence
224     over that for the base class.  If you instead wish to call the base class
225     handler after executing the specific handler, you must invoke it explicitly.
226     Only if no handler has been established will the exception check for and
227     invoke a handler in its base class automatically.
228 
229 3a- At times you may wish to prevent the calling of the base class handler yet
230     have no need for explicit handling.  The mechanism provides ZMtrivialHandler
231     which merely checks whether (in the absense of a handler) the exception
232     would be ignored or thrown, and returns false or true accordingly.
233 
234     Conversely, setting the handler to NULL will cause it to revert to the
235     initial behavior of defering to the handler of the parent class.
236 
237 4 - The user can tell the system to whether or not to ignore an unhandled
238     ZMexception:
239 
240         HepTuple::ZMxCapture.ignore()
241         HepTuple::ZMxCapture.dontIgnore()
242 
243     The default is don't ignore -- meaning throw the exception unless a handler
244     is invoked and comes back false.  The same rules for inheritance apply as
245     in the handler case:  If you haven't specifically said anything about a
246     subclass, then what you have said about the parent class will apply.  Thus
247     calling dontIgnore() is NOT the same as not calling ignore().  In analogy
248     with handling, we need a way to say "pretend I never said ignore or dont;
249     use the parent class decision".  This is:
250 
251         HepTuple::ZMxCapture.useParentIgnore()
252 
253 5 - Sometimes you may want to ignore an exception the first N times and then
254     react differently.  The handler can have a static count variable to
255     implement this behavior.  Alternatively, there is a call to establish this
256     behavior even if no handler is used:
257         HepTuple::ZMxCapture.ignore(N)
258 
259 
260 
261 
262 The part of the interface seen by non-framework-manager users is simpler:
263 --------------------------------------------------------------------------
264 
265 1 - The ZMerrno methods may be used to see if (ignored) exceptions have
266     happened.  Typically, the user used to Unix exceptions would call
267     ZNerrno.read() and maybe ZMerrno.clear().
268 
269 2 - The ordinary user can try, and catch, these ZMexceptions.  We ask that
270     usrs not throw ZMexceptions that the ZOOM modules throw.  Later we may
271     extend support to include user-defined subclasses of ZMexception.
272 
273 3 - When an exception occurs and is not ignored, the user will know the
274     message given for the exception, as well as the and line number where the
275     ZMthrow macro was invoked.  This is inside ZOOM code.  By doing
276     debug core <usersource> the user can get a traceback into the user
277     routines to find which line of his code encountered the problem.
278 
279 4 - The status of what was set up for ignoring and handling may be probed, so
280     temporary control behavior can be set and then put back to what it was:
281 
282         handler() returns the established handler.
283         logger()  returns the established logger.
284 
285         int ignoreStatus();  // -2 - ignore was specified (or ignore N was
286                              //      specified but have none left)
287                              // -1 - dontIgnore was specified
288                              // positive integer - ignore N times was specified
289                              //                    and have this number left
290                              //  0 - neither was specified; use policy from
291                              //      parent
292                         
293         void setIgnorePolicy(int);
294 
295 
296 Logging is handled as follows:
297 ------------------------------
298 
299 0 - The connection between a class of ZMexceptions and logging that is through a
300     ZMlogger.  The (framework) user may create her own logger but typically will
301     use our provided class ZMlog, which has is a ZMlogger and has a constructor
302     taking a file name.  Any actions taken by the logger we describe below
303     will refer to how ZMlog behaves.
304 
305 1 - Every individual ZMexception object can have a method for creating
306     (from the other arguments aside from messge) a string to put into a log.
307     (Of course, it may be inherited from its base class.)  But the message
308     as well as time, line, id, and so forth is not to be handled by each;
309     instead, ZMthrow handles this.
310 
311     The method to create the string is logMessage().
312 
313     ZMexcept will at various points call the logThis() method of the established
314     logger when it wants to log information.
315 
316 1a- The user assgns a ZMlogger to an exception class by
317         ZMexception::setLogger(ZMlogger*).
318     For example,
319 
320         ZMlog* mylog ("logfilename.txt");
321         ZMxCapture::setLogger(mylog);
322 
323     The pointer returned should be checked.  In the case of ZMlog, it can be
324     NULL for two reasons:  The file cannot be opened for append, or the file
325     is already open for some other purpose.  (If it is already opened for
326     logging, that is fine; this logger will also cause logging of messages
327     there).
328 
329 2 - ZMexception has a method setLogger(ZMlogger) which will open a log using
330     that logger for that type of exception.
331 
332     This will establish this logger to be used for exceptions of this class.
333     In the case of ZMlog, that means it will establish the file which was
334     provided when the logger was constructed, as a logging point for exceptions
335     of this class.  (This is class static information.)
336 
337 2a- The ZMlog object will log to a file:  its constructor will open a log to
338     that file for that type of exception.
339 
340     A user can provide a different logger which, for example, ties into the
341     CDF or D0 general logging mechanism instead of writing to a file.
342 
343 3 - If no logging is specified for a class the file defaults to the base class.
344     Thus HepTuple::ZMxCapture might log to the file for HepTuple::ZMxGeneral,
345     or if no logging is established there, for ZMexception.  It is possible (the
346     default) that no logging is established anywhere.
347 
348 4 - You may log to multiple files; each ZMexception (sub)class has a class
349     static linked list of log files.
350 
351 
352 - - - - up to here
353 
354 
355 
356 
357 5 - Aside from single files, you can also establish a "rolling log" pair of
358     files:
359         bool ZMexception::log(const char filename1[],
360                                 int n, const char filename2[], );
361     The way this works is that the first file is opened (still for append),
362     and up to n exception instances are logged into it, at which point
363     it is closed, renamed to the second file, and re-created (empty) for
364     more logging.  A script can detect when this has happended and archive
365     the second file if desired.
366 
367 6 - If you wish to cease logging a particular exception type in general or
368     to a particular log file, you may:
369         HepTuple::ZMxCapture.stopLog();
370         HepTuple::ZMxCapture.stopLog(filename);
371     If no exceptions are logging to a particular log anymore, that file will
372     be closed.
373 
374 7 - To be able to temporarily modify logging behavior for an exception, you
375     may call
376         int saveLogInstructions(),
377     and later call
378         restoreLogInstructions(int).
379 
380 
381 
382 The following usage recommendations pertain to writers of ZOOM code:
383 --------------------------------------------------------------------
384 
385 As shown in the examples, place your definitions of the exception objects
386 inside the class definition for the class.  That way, methods within this
387 class can simply call the shorter name -- ZMxCapture rather than
388 ZMxHepTupleCapture.
389 
390 Don't create too many types of exceptions.  The error codes appearing on the
391 ZMerrno stack are defined per each type, but unless you envision the user
392 needing to AUTOMATICALLY distinguish between one problem and another in
393 the same submodule via ZMerrno, don't define them as two separate exception
394 types.
395 
396 In cases where the user interface promises a routine will not throw an
397 exception (but might return false if something goes awry) the ZOOM code
398 itself should enclose any possible ZMthrows in try blocks, to catch the problem
399 in case the user has not specified ignoring the exception.
400 
401 Note that the argument to the constructor of the ZMexception in ZMthrow can
402 (and often will) be a string formed by concatenating some fixed informatory
403 message with some variable information, as in
404         ZMthrow ( ZMxCapture( "Column not found", name ) );
405 To do this, one should have a constructor for that sort of ZMexceptoin object
406 with the extra argument in its signature, as well as the one with just a
407 const char[].  One could alternatively do someting like
408         ZMthrow ( ZMxCapture( strcat("Column not found", name) ) );
409 but if you use the exception in more than one place, the recommended second
410 constructor is less work.
411 
412 When returning a pointer, in circumstances where things have gone wrong, the
413 usual action is to return a null pointer.  In many cases this is appropriate,
414 especially if the user interface defines it.  However, the sloppy user might
415 cause an (uncatchable) bus error if he does not check for null pointer.
416 Consider returning a pointer to a braindead object (whose methods throw
417 ignorable ZMthrows) rather than NULL -- then the job might not abort.
418 
419 
420 Documentation should be layered:
421 
422 1 - How the user interacts with the mechanism (VERY brief).
423 
424 2 - How the framework manager user interacts (settting up handlers, logs,
425     ignores).
426 
427 3 - How to define a ZMx class.
428 
429 4 - Usage recommendations for writers of ZOOM code.
430 

source navigation ] diff markup ] identifier search ] general search ]

Due to the LXR bug, the updates fail sometimes to remove references to deleted files. The Saturday's full rebuilds fix these problems
This page was automatically generated by the LXR engine. Valid HTML 4.01!