BIND 10 #446: configuration knob for in memory data source
BIND 10 Development
do-not-reply at isc.org
Thu Dec 23 18:13:49 UTC 2010
#446: configuration knob for in memory data source
---------------------------+------------------------------------------------
Reporter: jinmei | Owner: vorner
Type: task | Status: reviewing
Priority: major | Milestone: y2 12 month milestone
Component: b10-auth | Resolution:
Keywords: | Sensitive: 0
Estimatedhours: 0.0 | Hours: 0
Billable: 1 | Totalhours: 0
Internal: 0 |
---------------------------+------------------------------------------------
Comment(by jinmei):
Replying to [comment:3 vorner]:
> Few comments:
>
Thanks, these are good ones:-)
> - Why does destroyAuthConfigParser exist? When I saw it, I asked „Huh,
does destroying a config parser need special attention? Why shouldn't I
just call delete? What would go wrong?“. As I looked inside it, it does
just that, so I think this one is only confusing.
>
I knew this would look oakward, but technically we cannot simply let a
module delete an object that were newed in a different module, because
these two may use incompatible implementations of new/delete. That's the
reason why I introduced the dedicated "destroy" function.
In practice, however, I admit this may be overkilling because normal
applications are not supposed to create AuthConfigParser directly as
documented (createAuthConfigParser() is provided as a public function
mainly for testing purposes).
Another "tecnically correct" solution is to use a smart pointer and have
createAuthConfigParser() return a smart pointer version of the new'ed
object; the caller can simply forget the deallocation. I didn't choose
this path to reduce the dependency on boost (the only realistic choice of
smart pointer in this context would be boost::shared_ptr), but, on
rethinking it, it may be okay as this interface is supposed to be used
only within our own implementations (and the caller would be tests).
I can think of several possible ways to address your concern:
1. no code change, but add document about the reason for having a
separate function
2. have the caller delete the returned pointer and document it with the
pitfall of new/delete mismatch; remove destroyAuthConfigParser.
3. use a shared pointer and remove destroyAuthConfigParser.
Does any of the options address the concern? If so, what do you think is
the best? If not, do you have a suggestion?
BTW:
> Anyway, this function not only should never throw, it just can't, C++
specifies that if anyone tries to throw out of a destructor, the
application aborts.
>
I guess you are talking about this part of doc:
{{{
/// This function is not expected to throw an exception unless the
underlying
/// implementation of \c parser (an object of a specific derived class of
/// \c AuthConfigParser) throws.
}}}
and you mean that, since the only thing destroyAuthConfigParser() does is
to do delete (which effectively means calling a destructor in this
context), if \c parser throws it should be from a destructor. If so,
that's correct. But I'm not sure what you mean by the "C++ specifies ..."
part. As far as I know, it's not prohibited by the language spec,
although it's widely considered a very bad practice. For example, if you
run this code:
{{{
struct BadThrower {
~BadThrower() { throw 0; }
};
int main() {
try {
BadThrower bt;
} catch (const int& ex) {
std::cout << "bad practice, but possible" << std::endl;
}
return (0);
}
}}}
We'll see this: "bad practice, but possible".
> - Isn't it a lot to ask from commit not to throw any exception? Is it
realistic to say that none of them will ever allocate memory, for example?
>
Yeah, I know it can be a lot. But if we want to implement some way of
"safe configuration update", we need some strong requirements like this.
By "safe" I mean if an attempt of configuration update fails the old
configuration remains valid intact. In other words the commit() variants
are a kind of no-throw swap(), which can sometimes be difficult to
implement but is crucial in some scenarios.
And, since commit() assumes a strong requirement, I separated it from
build(), which can throw and would often need to allocate memory. The
idea is to have build() do any work that can fail without touching the
server object as much as possible, and then commit() completes the task
with much restricted, but safer primitives.
If we think the requirement is too restrictive and unrealistic, possible
alternatives would be:
1. introduce a "cancel()" method (or have the destructor do it), which is
responsible for rolling back incomplete configuration committed in the
server.
2. just let commit() throw when it can't avoid that, with a note that the
system may become inconsistent state in that case
IMO option 1 would actually be as strong as requireing commit() not throw,
so I suspect it's not a realistic option. Option 2 sound irresponsible to
me, and I don't like it, but depending on how we handle the configuration
stuff in practice, we may be able to live with it as a compromise.
What's your opinion?
--
Ticket URL: <http://bind10.isc.org/ticket/446#comment:4>
BIND 10 Development <http://bind10.isc.org>
BIND 10 Development
More information about the bind10-tickets
mailing list