elegant error handling in message processors, how?

P

petschy

hello all,

i'm writing network message processors for a server app. after
dispatching, the processor function is called for the given message
type. the tokens in the message are validated, checked against the
server state, and if everything is ok, an operation is perfomed, and
optionally a reply is sent back. in some cases of invalid input, an
error message is sent back.

with the naive approach this leads to deeply nested if statements
(complex messages), where the innermost true block does the actual
operation and sends the reply. else branches might send the error
reply. this structure is hard to oversee and maintain, so i was
searching for better ways.

A)
bool error = true;
const char* reason = "?";

do {
....
if (invalid input that needs reply) {
reason = "FOO";
break;
}
if (invalid input that doesn't need reply) {
return;
}
...
} while ((error = false));
if (error) {
send_err_reply(reason);
} else {
send_reply();
}

this is ok only when there are no validators in for/while/case
constructs. also, if called functions throw exceptions, there should be
a try/catch block, too. this brings us to plan b:

B)
try {
...
if (invalid input that needs reply) {
throw "FOO";
}
if (invalid input that doesn't need reply) {
return;
}
...
send_reply();
} catch (const char* e) {
send_err_reply(e);
} catch (...) {
// catch other exceptions from called functions and either ignore
them or send back an error
}

i'm not completely satisfied with this approach, since it uses
throw/catch in the same function, but i haven't yet found an equally
simple but more efficient solution, considering that throwing/catching
has some overhead in time/space compared to other flow control
mechanisms.

any ideas?

cheers, p
 
A

AnonMail2005

petschy said:
hello all,

i'm writing network message processors for a server app. after
dispatching, the processor function is called for the given message
type. the tokens in the message are validated, checked against the
server state, and if everything is ok, an operation is perfomed, and
optionally a reply is sent back. in some cases of invalid input, an
error message is sent back.

with the naive approach this leads to deeply nested if statements
(complex messages), where the innermost true block does the actual
operation and sends the reply. else branches might send the error
reply. this structure is hard to oversee and maintain, so i was
searching for better ways.

A)
bool error = true;
const char* reason = "?";

do {
....
if (invalid input that needs reply) {
reason = "FOO";
break;
}
if (invalid input that doesn't need reply) {
return;
}
...
} while ((error = false));
if (error) {
send_err_reply(reason);
} else {
send_reply();
}

this is ok only when there are no validators in for/while/case
constructs. also, if called functions throw exceptions, there should be
a try/catch block, too. this brings us to plan b:

B)
try {
...
if (invalid input that needs reply) {
throw "FOO";
}
if (invalid input that doesn't need reply) {
return;
}
...
send_reply();
} catch (const char* e) {
send_err_reply(e);
} catch (...) {
// catch other exceptions from called functions and either ignore
them or send back an error
}

i'm not completely satisfied with this approach, since it uses
throw/catch in the same function, but i haven't yet found an equally
simple but more efficient solution, considering that throwing/catching
has some overhead in time/space compared to other flow control
mechanisms.

any ideas?

cheers, p

Have a single top level handler do all of your try/catch processing.
Then all of your handlers only have to throw exceptions and not
have to worry about try/catch blocks. The top level and only the
top level will send back the error message if it catches an exception.

Of course, the individual handlers will have to worry about being
appropriately exception safe. That is, you don't want to leave your
server in a bad state if a handler throws an exception.
 
P

petschy

Have a single top level handler do all of your try/catch processing.
Then all of your handlers only have to throw exceptions and not
have to worry about try/catch blocks. The top level and only the
top level will send back the error message if it catches an exception.

Of course, the individual handlers will have to worry about being
appropriately exception safe. That is, you don't want to leave your
server in a bad state if a handler throws an exception.

the problem with the single top-level try/catch approach is that the
message dispatch system is quite generic, and knows nothing about what
reject message would be sent with what detail info. also, the
dispatcher can't determine, whether a reject should be sent at all.
these two pieces of information is only available in the given message
processor, so must be dealt with there. of course this info could be
packed into dedicated exception objects, but this would complicate
things quite a lot, extra classes, try/catch/rethrows... also, the
message processing is transactional, and the transaction is rolled back
if an exception leaves a message processor, which aborts the sending of
the reject message among other tasks.

p
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,585
Members
45,080
Latest member
mikkipirss

Latest Threads

Top