From octave-maintainers-request at bevo dot che dot wisc dot edu Sun Nov 17 19:42:45 2002 Subject: Re: sparse matrices and error handling From: "John W. Eaton" To: octave-maintainers at bevo dot che dot wisc dot edu Date: Sun, 17 Nov 2002 19:42:22 -0600 On 17-Nov-2002, Paul Kienzle wrote: | IIRC, SuperLU needs to be compiled with an error function defined which | prints the error and exits. I don't believe it allows for an error | handler which returns. It's been a long time since I looked at it though. Then I think the right thing to do is use setjmp and octave_jump_to_enclosing_context from the error handler, though more cleanup may be needed if SuperLU allocates resources. If it is not possible to do that without modifying SuperLU, then we may want to consider distributing a modified version with Octave, in which case we can probably avoid the need for setjmp/longjmp. | BTW, isn't it a misnomer to refer to the setjmp/longjmp setup as only | applying to foreign code? It seems to me that you would want it around | any code which can take more than 1/2 second or so to complete, even | if it is coded in C++ and is part of liboctave. If you don't have it,then | how can you break out of such code since Ctrl-C can't directly throw an | exception? In code that we can modify (all of Octave) we can use the new OCTAVE_QUIT macro. It checks to see if an interrupt has occurred, and if so, throws an exception. So everything that is not part of Octave and that doesn't allow us to insert checks for the interrupt state and maybe throw exceptions is "foreign code" that has to handle interrupts some other way. The simplest way seems to be a longjmp out of a signal handler and back to the location where the foreign code was called, then throw an exception. | The usual question of cost and convenience. | | In order to properly collect resources after exceptions you have to write | your code just so. I'd say it is much harder to get this right without exception handling, because you would have store a pointer to everything you need to free, even for local objects. For example, we currently have a lot of code that does something like some_fun (...) { ... Matrix mx (m, n); ... } and as it is now, the memory allocated for mx will not be deleted if we longjmp while this function is active. To handle this, we would need to write something like some_fun (...) { // Need to mark beginning of frame in unwind_protect list. unwind_protect::begin_frame (); ... Matrix mx (m, n); unwind_protect_local_matrix (mx); ... // Would not delete here, the Matrix destructor handles that, so // we need to discard info about locally allocated objects. unwind_protect::run_frame_discarding_local_objects (); } for *every* matrix allocated this way. Seems like a huge cost to me, even if we added the unwind protect code to the Matrix constructor (though that seems like a bad mixture to me). Exception handling saves a lot of work for things like this because it guarantees that if an exception causes us to unwind the call stack, destructors for any live local objects will be called. | I don't yet have a feel for how inconvenient "just so" | is. Is it much less hassle than unwind_protect? Typically it just means making sure that resources are allocated in constructors and released in destructors. To be safe, you need to be careful about how write the constructors and destructors, but I don't think it is really hard. Not all of Octave has it right yet, but for the most part, resources are handled in constructors and destructors. | I would also like to know how much it costs compared to unwind_protect. I don't really know, though I suspect that it is not all that much. There is a huge benefit compared to not using exceptions, since Octave's unwind_protect is really only handling some resources. Anything that is allocated as a local object will not go away if we call longjmp from a signal handler to go back to the top level. With exception handling, we will clean up everything allocated that way when we go back up the call stack. We would still have to handle things allocated outside of constructors, but we have to worry about those things anyway. Moving allocations like that inside constructors is probably one of the better ways to solve the problem. The C++ auto_ptr class can help. jwe