Preventing changes to a module's variables in mod_perl

M

max

I have a module that exists as a central data store, returning simple
scalars or references to larger structures. All of its data is stored
in a big hash, and I use AUTOLOAD to return the requested value.

The problem is that, with mod_perl, if the script that uses the module
changes the data (which with auto-vivication is pretty easy), the data
is changed for all subsequent scripts using that module run by that
particular apache child, which leads to the usual hair-pulling
intermittent bugs that mod_perl users historically puzzle over.

I've looked at a couple locking options, but the problem is that the
data can be arbitrarily deep references (which seems to rule out
Hash::Util). I might have MyModule->foo->{bar}{$baz}, and if $baz isn't
a valid key, it gets auto-vivified and there goes my weekend.

Is there a way I can prevent these changes from happening? And without
a huge performance penalty?

Thanks.
 
T

Ted Zlatanov

I have a module that exists as a central data store, returning simple
scalars or references to larger structures. All of its data is stored
in a big hash, and I use AUTOLOAD to return the requested value.

The problem is that, with mod_perl, if the script that uses the module
changes the data (which with auto-vivication is pretty easy), the data
is changed for all subsequent scripts using that module run by that
particular apache child, which leads to the usual hair-pulling
intermittent bugs that mod_perl users historically puzzle over.

I've looked at a couple locking options, but the problem is that the
data can be arbitrarily deep references (which seems to rule out
Hash::Util). I might have MyModule->foo->{bar}{$baz}, and if $baz isn't
a valid key, it gets auto-vivified and there goes my weekend.

Can you return a tied hash where you control each operation on the
hash (retaining the syntax you have) to the effect you want? There
may even be a CPAN module that does what you want, though I'm not
aware of it.

Ted
 
B

Brian McCauley

I have a module that exists as a central data store, returning simple
scalars or references to larger structures. All of its data is stored
in a big hash, and I use AUTOLOAD to return the requested value.

The problem is that, with mod_perl, if the script that uses the module
changes the data (which with auto-vivication is pretty easy), the data
is changed for all subsequent scripts using that module run by that
particular apache child, which leads to the usual hair-pulling
intermittent bugs that mod_perl users historically puzzle over.

I've looked at a couple locking options, but the problem is that the
data can be arbitrarily deep references (which seems to rule out
Hash::Util).

The Readonly module claims to recursively follow deep structures (by
default).

Is there a reason you can't simply make the central data store readonly?
 
M

max

Brian said:
The Readonly module claims to recursively follow deep structures (by
default).

Is there a reason you can't simply make the central data store readonly?

Thanks. I had tried using Storable's dclone to return a copy, which
worked but was about a hundred times slower than just returning a
reference. Readonly seems to only be about four times slower.

Is there a way to get Readonly to warn instead of die when a
modification attempt is made?
 
B

Brian McCauley

Is there a way to get Readonly to warn instead of die when a
modification attempt is made?

There's no such feature at the moment but it would be no biggie to add
if you wanted to. Just change the croaks() to carps().
 
M

max

Brian said:
There's no such feature at the moment but it would be no biggie to add
if you wanted to. Just change the croaks() to carps().

Seemingly better still, Readonly uses its own croak wrapper, so adding

sub Readonly::croak { warn $_[0]; }

to my module seems to do the trick.

Thanks again for your help.
 
B

Brian McCauley

Brian said:
There's no such feature at the moment but it would be no biggie to add
if you wanted to. Just change the croaks() to carps().

Seemingly better still, Readonly uses its own croak wrapper, so adding

sub Readonly::croak { warn $_[0]; }

to my module seems to do the trick.

You'd probably get more informative errors with

sub Readonly::croak {
require Carp;
goto &Carp::carp;
}

The problem with this approach is it breaks any other uses of Readonly
that are going on in other modules that anyone who happens to use your
module is also using.
 
A

anno4000

Brian McCauley said:
Brian said:
(e-mail address removed) wrote:

Is there a way to get Readonly to warn instead of die when a
modification attempt is made?

There's no such feature at the moment but it would be no biggie to add
if you wanted to. Just change the croaks() to carps().

Seemingly better still, Readonly uses its own croak wrapper, so adding

sub Readonly::croak { warn $_[0]; }

to my module seems to do the trick.

You'd probably get more informative errors with

sub Readonly::croak {
require Carp;
goto &Carp::carp;
}

The problem with this approach is it breaks any other uses of Readonly
that are going on in other modules that anyone who happens to use your
module is also using.

The substitution can be localized to a block or file:

local *Readonly::croak = sub {
require Carp;
goto &Carp::carp;
};

Anno
 
B

Brian McCauley

The substitution can be localized to a block or file:

local *Readonly::croak = sub {
require Carp;
goto &Carp::carp;
};

No! local() is dynamically scoped not lexical. Changes made with
local() are _not_ localized to a block or file but to a stack frame
(and subframes thereof).

Anyhow, even if it where lexical it would help the OP since he wants to
control the behaviour of complex data structures returned by his
modules when code outwith his control tries to modify them.
 
A

anno4000

Brian McCauley said:
No! local() is dynamically scoped not lexical. Changes made with
local() are _not_ localized to a block or file but to a stack frame
(and subframes thereof).

Yes, of course. That means, once control has left the block the change
is reverted. In simple cases that may be good enough.
Anyhow, even if it where lexical it would help the OP since he wants to
control the behaviour of complex data structures returned by his
modules when code outwith his control tries to modify them.

Hmm... tie() comes to mind then.

I admit I've lost context of the thread, so I'm not sure if that big
gun is warranted. Maybe a certain module named Tie::OneOff could be
useful... :)

Anno
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top