Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Beware of global variables, for they may prevent your destruction.

by kyle (Abbot)
on Nov 25, 2009 at 20:03 UTC ( [id://809419]=perlmeditation: print w/replies, xml ) Need Help??

In Can DBI modify queries on the fly?, I was looking for a way to insert comments into the SQL that's sent to our app's database so that the DB's log could help us find problems in the code. Through the helpful hints here, I eventually came to a general solution, which I implemented in a branch, tested with our (poor) automated tests, and put out for our QA department to look at next.

In the SQL comments, I wanted information about our current request, but I couldn't see a way, from a DBI root module, to get that object. This isn't literally the original Apache request object but our own wrapper around it, which is passed around a lot but not globally accessible. Naturally it is not passed to any DBI function, where I wanted to use it.

So I stuck it in a global (package) variable.

Problem solved! When we create the object in the main request handler (which dispatches all over the place), a reference is stored in a global variable, and I can get that variable from the DBI code elsewhere.

Do you see the bug I had just created?

The request object we use does some bookkeeping when it gets destroyed at the end of each request. Since I'd made a permanent reference to it in a package variable, it never got destroyed, and that important end-of-life action never happened. This caused some fairly strange bugs.

The solution is simple enough.

use Scalar::Util qw( weaken ); $Package::Variable::REQUEST = $our_request; weaken $Package::Variable::REQUEST;

Now the package variable gets turned to undef when the original gets destroyed, and it doesn't prevent the destruction anymore.

My lesson from this experience, I'd sum up this way: When putting a lexically scoped reference into a package variable, consider making it a weak reference. That could probably go not only for package variables, but any variable with a scope larger than the original. I don't expect that using Scalar::Util::weaken is always the best thing to do, but it will always be worth thinking about.

Replies are listed 'Best First'.
Re: Beware of global variables, for they may prevent your destruction.
by dsheroh (Monsignor) on Nov 26, 2009 at 09:15 UTC
    I ran into just about the exact same thing a couple weeks ago (in a FastCGI app, even) and dealt with it in a slightly more direct fashion:
    our $foo_global = $foo; $obj->method_that_needs_foo_but_takes_no_params; undef $foo_global;
    While either way of handling this works, I prefer the undef solution for a case like mine where the global data is only needed for one specific call, as it lets me immediately clean it up after that need passes. weaken is definitely the better solution in other cases, though, where you may not know as clearly when you'll be done with the global.
Re: Beware of global variables, for they may prevent your destruction.
by JavaFan (Canon) on Nov 25, 2009 at 22:00 UTC
    Since I'd made a permanent reference to it in a package variable, it never got destroyed
    That's not true. It will get destroyed at program termination, assuming your program terminates normally.

      That's something I should have been more clear about. All this is taking place in a mod_perl setting. Previous to my change, the object was expected to be destroyed at the end of each request. Since my change, it would still live on between requests, until the next request came along and replaced it in the package variable (which is too late for the new object to benefit from the experience of the old object).

      Perhaps I could get away with saying that "never" is an exaggeration. Strictly speaking, each one was destroyed, just not on time.

      However destructors run at program termination often find that data they expected to have still around is not there. Such as data inside of the object you are destroying

      Therefore it is safer to deallocate globals in END blocks. That way you can control what is there. But be careful not to do anything that messes up $!, else exit may not work as expected.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://809419]
Approved by moritz
Front-paged by keszler
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (7)
As of 2024-03-19 03:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found