New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
decide NULL object handling #2839
Comments
Is a null object an object which skipped its init call? If so, then I will chime in :) Init calls cannot be placed in subs, if statements, or be a part of an expression. Vcc can detect and throw an error on unsafe init usage. This would make it impossible for init to be skipped. This is exactly how other languages handle this, at compile time: https://en.m.wikipedia.org/wiki/Uninitialized_variable#Use_in_languages |
@rezan I agree and think that you are basically describing a variant of option b). But a static check at VCC time still leaves a window for an initialization to fail at vcl load time and will probably be harder to implement than simply checking after vcl_init completion if all declared objects were initialized. |
Not sure I understand this? If you eject out of VCL, only call fini on objects where init was called. Part of initialization is registering the fini (see my patch), so in theory this can baked into the implementation. How exactly do you plan on checking if init was called on all variables after the fact? You will do this at compile time or runtime? |
First of all the only call fini on objects where init was called is broken at the moment, see #2297 |
What exactly is the problem we're trying to solve here? Is there internal code that works with VMODs that might crash if an object is NULL? That doesn't happen for method invocations, since the object pointer is passed into the C function that implements the method. If the rest of Varnish is not necessarily safe when an object pointer is NULL, then I can see the rationale in one of these solutions. But if that's not the problem, then I don't understand why we need to do anything about it at all. Why shouldn't a VMOD author choose to work with NULL with some sort of significance? Say, here's what the VMOD does with an uninitialized object. How can we know that that won't ever be an appropriate design choice for a VMOD? Why shouldn't VCL authors have the freedom to make the initialization of an object dependent on a condition? To be sure, a NULL object pointer could lead to a segfault or an assertion failure, but this is C, after all, which famously gives you enough rope to hang yourself. There are numerous ways that a VMOD can get Varnish to crash, whether or not objects are NULL. So it has always seemed obvious to me that VMODs only exist on the understanding that the programmers have to know how to do it right. I don't think that programmer hand-holding should be the responsibility of this project. |
@slimhazard totally agree! |
@slimhazard @rezan I apologize for apparently having been too terse in an initial issue description again, I should try to never again assume that anything is so obvious that anyone from the dev team will be able to make sense of a brief description. The current doctrine in vmods is to So right now, a
crashes varnish for all practical purposes. This contradicts the design requirement that pure VCL should be safe. So my question for clarification is if we should a) handle NULL and not panic, iow require vmod authors to not assert on null and instead call
in the java world, what would be the semantics of Calling a method on a NULL object does not seem to be a valid concept to me, but, sure, we're free to define NULL as a valid object in vcl, if we were inclined to do so. That's option a).
I agree. But our coding paradigm should be a safe example, and right now it is not. |
c) have varnish keep track of objects initialization to ensure that constructors are called exactly once, otherwise fail |
@Dridi I believe that is a runtime solution. We should always opt for compile time solutions for perf reasons. We compile once, but "run" many orders of magnitude more times. Over time, these runtime checks will add overhead, accumulate, and then leave us with no options but to accept slowness as a fact of the Varnish runtime. Unless you want to introduce a JIT into the runtime which would optimize out these checks under certain conditions :) |
@rezan I don't think your comment applies. |
@nigoroll I guess we disagree on the definition of runtime and compile time then... |
@rezan I fail to follow in any way. Can you explain please why you consider vcl_init runtime? It's called after the vcl is compiled to C by vcc, compiled into a shared object and loaded during the warmup. You said:
This would only apply if we transitioned the vcl from cold to warm many orders of magnitude more often than loading it. In the context of performance, this argument does not click with me. In particular not with regard to a check which should take <1us per object. |
Right, so your saying that the runtime penalty for checking for NULL objects is low. Might be true. But remember, this is software. Our decisions needs to be forward looking, otherwise, we lose the much needed ability to keep evolving, competing, and solving problems. What happens when we introduce request scoped objects? Are we still in a situation where the runtime overhead for NULL checking is low? This is exactly runtime vs compile time! |
I feel I'm the wrong student for the lecture on performance fundamentals. ;) Please let's focus on the initial question and maybe reach a conclusion during the next bugwash |
I suggest that we look into making the bundled VMODs robust in the case of an uninitialized object, using existing means, such as calling Other than that, IMO the rationale given here is indeed hand-holding, and I don't think it should be the business of the project -- in particular, we shouldn't be imposing technical restrictions on VMODs, or forbidding otherwise legal language syntax (such as initializations within an Every time I try to articulate my reasons for that, my bad habit of getting long-winded and philosophical gets in the way. So @nigoroll, instead of me writing an enormous comment that's too much work for anyone to read, I suggest that we discuss it the next time we see each other in person. |
I don't think Keeping track of constructor calls and enforcing exactly one object instantiation per import foo;
sub vcl_init {
if (logic) {
new bar = foo.baz("once");
}
if (broken-logic) {
new bar = foo.baz("again");
}
} That gives the choice to VMODs to handle We already have a "VRT_fail'd" check after _every single VCL statement_ so arguing about null checks or keeping track of exactly-once object instantiations for performance reasons is a fruitless discussion. edit: the systematic VRT_fail'd check also happens at run time, during proper transactions. |
I wrote a test case:
And it yields an error:
So good news, we can't have the broken logic described previously. The bad news is that we may still have a symbol not going through a proper initialization. The "bad" news is we can't have alternate instantiations. Assuming option c) where we enforce that all objects must run their constructors, either we use the actual object's pointer to do the exactly-once check at the end of I understand the appeal of being able to skip a constructor in VCL, but it doesn't make sense to use a symbol at runtime that didn't go through its |
@slimhazard your interpretation of a) is what I originally intended, so I've changed the description (it's always good to have a native speaker on the team :) ) The only relevant point was not to assert for option a), I did not mean to dictate the exact means of error handlig. @Dridi thank you: yes I was wrong about vcl_init being called for a warm event - it does not get called for a vcl warm. But the context of that misinformation was the performance argument, so that's even less relevant. |
bugwash: b), but a) can be activated in the vcc file. @bsdphk to write it up |
Bugwash conclusion: We will annotate $Object in .vcc files to allow them to be NULL. If not so marked, vcl_init{} will fail if they are not initialized. |
Allow VMOD writers to permit with NULL_OK flag. Only call object destructor on initialized objects. Fixes #2839 Conflicts: lib/libvmod_debug/vmod.vcc
Allow VMOD writers to permit with NULL_OK flag. Only call object destructor on initialized objects. Fixes varnishcache#2839 Conflicts: lib/libvmod_debug/vmod.vcc
while working on #2830, I thought it might be about time to make a decision on how to handle NULL (uninitialized) objects (for example, because a
new
statement was not reached or the constructor failed). I see two basic optionsa) advise vmod authors to not assert on null and instead call
VRT_fail()
or otherwise handle the case of the object pointer being NULL safelyb) require that all objects be initialized when
vcl_init {}
returns or fail the vcl loadThe text was updated successfully, but these errors were encountered: