Transactions and in-memory data (part 2 of n)

Buffering the opertations wasn't particularly successful.

Let's look at maintaining a rollback log.

Seems pretty simple.  You can do something like:

typedef void (*rollback_function_ptr_t)(void *context);
typedef struct _rollback_record {
rollback_function_ptr_t rollbackFunction;
struct _rollback_record *next;
} rollback_record_t;

void do_rollback(rollback_record_t *head) {
while (head != NULL) {
rollback_record_t *next = head->next;
(*head->rollbackFunction)(head + 1);
free(head);
head = next;
}
}

That's pretty simple, eh?  Since rollbacks probably have to be done in reverse order of changes, you have to maintain the list with a stack discipline (always adding to the head).  Whenever you're going to do anything which mutates global in-memory state, you first allocate a rollback record plus space for your own data, fill it in and then put it on the head of the list.

Sounds good at one level, but simultaneously it totally breaks the model of folks like STL who were trying to do the right thing by providing a few functions like std::map::erase() which do not fail.  Now these destructive actions have to be able to fail since they can allocate a rollback record which can fail.

Next time I'll go into this model a little more and point out some of its problems but since I hadn't written since Thursday I wanted to get the start of this out at least.