error handling

Niels Möller nisse at lysator.liu.se
Fri Dec 19 09:17:47 UTC 2014


Victor Shoup <shoup at cs.nyu.edu> writes:

> But just to make clear what my own (rather narrow) goals are:
> to be able to compile GMP in such a way that the mpn-level
> routines do not call abort (except perhaps for asserts that
> indicate an internal bug), but rather, throw a C++ exception.

For memory allocation, you can kind-of do this already, just register
C++ memory allocator functions which throw a C++ exception on error. The
exception function pointer which Torbjörn suggested would let one do
something similar for divide by zero and _mp_size overflow).

The real problem is to do clean up temporary storage.

If at all possible, I would prefer to define cleanup interfaces so that
(1) they can be used from C without too much hassle, and (2) they
integrate nicely with C++. And not have a separete C++ build of gmp.

I wrote about one approach the other day. Another approach might be to
have gmp allocation use memory pools (a construction which is common in
objc programs, with its "autorelease" machinery).

Memory pools have a parent (so they form a stack or maybe even a
directed non-cyclic graph). Introducce a (thread-local) global variable
pointing to the current pool, and all gmp-internal allocation is taken
from the current pool. This applied to all of TMP_ALLOC, temporary mpz_t
objects in gmp itself, and mpz_t objects in the application code (and I
think that's pretty important).

Main memory pool functions are to push a new pool as the current one,
and to pop the current pool, deallocating all memory in that pool,
including any memory in its child pools.

To preserve result values, one also needs some method for elevating
certain objects from the current memory pool to its parent pool.

Assuming the memory alllocation functions throw exception on errors,
typical code catching that exception, cleaning up, and returning an
error indication, would be something like

  bool
  foo (mpz_t out, const mpz_t in)
  {
    mpz_t tmp;
    memory_pool_push ();
    try {
      mpz_init (tmp);
      compute_something (tmp, in);
      mpz_set_parent (out, tmp);
      memory_pool_pop ();
      return true;
    }
    catch (...) {
      memory_pool_pop ();
      return false;
    }
  }

The nice thing here is that the book-keeping of which object belongs to
which pool is the responsibility of the pool implementation, so it's not
part of mpz_t. And the main overhead should be placed with the code
which actually wants to catch the exception. The machinery could be
optional, one could leave the default allocation functions as is, and
have some function to replace them with pool-aware allocation functions,
and call that by default if linking with -lgmp++.

The one thing I find a bit ugly with the above approach is the extra tmp
object and the mpz_set_parent call. The problematic scenario is if the
out variable is passed to compute_something, which it first reallocates
it and then throws an exception; in this case deallocating the meory
pool will leave out in an undefined state. Maybe one could do something
more clever when reallocating, e.g., one could check which pool the
current allocation belongs to, and make the reallocation from the same
pool.

And with some care, I think this could be package reasonably nicely also
with plain C, with some macro which both pushes a new memory pool and
pushes a new jmpbuf for the exception handler. Some of this has the
flavour of a general exception handling system for C, but that's
unlikely to ever fly. So I think we should aim for internal gmp use, and
application functions doing gmp calls only, no other allocations or
anything else needing cleanup.

Regards,
/Niels

-- 
Niels Möller. PGP-encrypted email is preferred. Keyid C0B98E26.
Internet email is subject to wholesale government surveillance.


More information about the gmp-devel mailing list