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