lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Thu, Jun 19, 2014 at 5:54 PM, Josh Haberman <jhaberman@gmail.com> wrote:
On Thu, Jun 19, 2014 at 2:26 PM, Aaron Faanes <dafrito@gmail.com> wrote:
> The safest solution IMO would be to write your luaopen_*
> to be resilient (or a no-op) in the face of redundant requires.
> If all they do are assignments to the global environment,
> this level of safety is easy to attain. I'd be curious to hear
> if this wasn't the case for you. :)

My C module keeps a weak table mapping (C pointer) -> (Lua wrapper).
This table is created in my luaopen function. So if my luaopen
function got called more than once, I would end up with more than one
table. That would be bad because I generally rely on the guarantee
that I have at most one Lua wrapper per C object.

I currently store this table in the registry, so I do have the option
of checking to see whether the table already exists when I create it.
But I think I should be able to store this as an upvalue of my C
functions instead if I choose (for example, if it gives better
performance). If I store the table as an upvalue (the equivalent of
declaring it as "local" in a pure Lua module), there would be no way
to do the right thing if my luaopen function gets called multiple
times.

An upvalue associated with the exported material would, I think, be the best course if the goal is solely to minimize risk. You can also hide state in a userdata's metatable.


> I guess I treat require's "load only once" ultimately as a very
> strong hint rather than as a fundamental guarantee - it's just
> too easy for the programmer to break.

It seems like an important guarantee if the module has any state.


True, but it sounds like you're coupling two independent tasks:

1. Loading code into the global environment
2. Running initialization code

If you couple these, then you're forced into trying to prevent redundant loads through require. This is technically doable through the techniques you mentioned above, but it's much simpler to write your require to only do #1, which is safely reentrant by default.

(This is because if the user circumvents require, then the worst they'll get is get a different set of upvalues. These would have Lua globals, which would just be reloaded without harm, and underlying C upvalue state, which may or may not be safely reloaded - that would be up to the underlying API to handle, but that API already has to worry about those issues just from the C people using it, so presumably it should have some way of handling it.)

The binding could minimize the situations when that error condition has to run through mechanisms like require and semaphores in the registry, but it seems unsafe to actively rely on a guarantee that doesn't actually guarantee anything. ;) The Lua wrapper <--> C object issue you mentioned sounds to me like a slightly different topic (referential transparency), but that's not a problem that is specific to require.

I hope this helped - It sounds like you have the right approach, but I just wanted to point out that require promises a bit more than it can provide, so you'll need to look elsewhere to ensure that guarantee is honored.

-- Aaron

--
Aaron Faanes <dafrito@gmail.com>