←back to thread

271 points mithcs | 1 comments | | HN request time: 0.213s | source
1. kazinator ◴[] No.45960606[source]
This won't play along with setjmp/longjmp exception handling schemes, unfortunately, without a lot of extra effort.

You can do cleanup handling that integrates with your exception handling library by using pairs of macros inspired by the POSIX pthread_cleanup_push stuff.

  #define cleanup_push(fn, type, ptr, init) { \
    cleanup_node_t node; \
    type ptr = init; do cleanup_push_api(&node, fn, ptr) while (0)

  #define cleanup_pop(ptr) cleanup_pop_api(ptr) \
  }
cleanup_push_api places a cleanup node into the exception stack. This is allocated on the stack: the node object. If an exception goes off, that node will be seen by the exception handling which will call fn(ptr).

The cleanup_pop_api call removes the top node, doing a sanity check that the ptr in the node is the same as ptr. It calls fn(ptr).

The fact that cleanup_push leaves an open curly brace closed by cleanup_pop catches some balancing errors at compile time.

The POSIX pthread_cleanup_pop has an extra boolean parameter indicating whether to do the cleanup call or not. That's sometimes useful when the cleanup is only something done in the case of an abort. E.g. suppose tha the "cleanup" routine is rollback_database_transaction. We don't want that in the happy case; in the happy case we call commit_database_transaction.