princepawn has asked for the wisdom of the Perl Monks concerning the following question:

The Apache module has a send_fd() method which is nice if you are going to send the entire file, but when you want to send a range (and this is possible under HTTP/1.1 RFC 2068),then you can't do it. So I wrote the following method, but mod_perl is balking at my use of my variables in the scope which encloses the subroutine.

what is the best way to fix this?

sub Apache::send_range { my ($r,$fh, $offset, $end) = @_; seek($fh,$offset,0); my $buf; my $bufsiz=4096; my $current_position = $offset; sub read_size { my $amount_to_go = ($end - $current_position); if ($amount_to_go <= $bufsiz) { $bufsiz; } else { $amount_to_go; } } while (read($fh,$buf,read_size)) { $current_position += read_size; $r->print($buf); } }
  • Comment on mod_perl: variable $end will not stay shared (nested subroutines problem)
  • Download Code

Replies are listed 'Best First'.
(tye)Re: mod_perl: variable $end will not stay shared (nested subroutines problem)
by tye (Sage) on Apr 18, 2001 at 03:01 UTC

    Just to be concrete:

    sub Apache::send_range { my ($r,$fh, $offset, $end) = @_; seek($fh,$offset,0); my $buf; my $bufsiz=4096; my $current_position = $offset; my $read_size= sub { my $amount_to_go = ($end - $current_position); if ($amount_to_go <= $bufsiz) { $bufsiz; } else { $amount_to_go; } } while (read($fh,$buf,&$read_size)) { $current_position += &$read_size; $r->print($buf); } }

            - tye (but my friends call me "Tye")
Re: mod_perl: variable $end will not stay shared (nested subroutines problem)
by arturo (Vicar) on Apr 17, 2001 at 23:55 UTC

    Here's an application of a problem-solving strategy I've found to be occasionally helpful

    1. Consult perldoc perldiag if you don't understand the error message
    2. Work out why you're getting the error message (if it isn't obvious) from the information thus gleaned.
    3. Follow up on any suggestions you can find in the docs.

    Interesting how this question came up right after one4k4's concerning a similar issue; long story short, your whole script is a subroutine when you run it under Apache::Registry. 'Course, you're getting it because you have a nested sub the way you've coded it anyhow. But back to the strategy outlined above =>

    From perldoc perldiag the solution to the "won't stay shared" issue is:

    This problem can usually be solved by making the inner
                  subroutine anonymous, using the sub {} syntax.
    

    Another approach would be to move the read_size subroutine's declaration outside of the block.

    These are purely structural solutions; I'm not even sure you need to be doing what you're trying to do here (read is built to handle the issue of when you try to read a larger chunk than what's left in the file).

    HTH

    Philosophy can be made out of anything. Or less -- Jerry A. Fodor

      I'm not even sure you need to be doing what you're trying to do here (read is built to handle the issue of when you try to read a larger chunk than what's left in the file).

      I said what I was doing: I don't want to always read to end of file. Only to the byte position specified...

        Hm, well, then, here's an alternate approach that does away with a subroutine. I assume the goal is to avoid loading a large file willy nilly into a Perl scalar (because read is already buffered):

        • Work out the total # of bytes to read.
        • Work out how many chunks of $bufsiz that would need to be read.
        • Work out the remaining # of bytes
        • print out a sufficient # of big chunks
        • print out the last chunk

        Some code (totally off the top of my head, untested, just to give the idea):

        # get $start, $end my $bufsiz = 4096; my $total_length = $end-$start; my $chunks = int $total_length / $bufsiz; my $data; if ($chunks) { foreach (1 ... $chunks) { read (FILE, $data, $bufsiz); print $data; } } read (FILE, $data, $total_length % $bufsiz); print $data;

        Philosophy can be made out of anything. Or less -- Jerry A. Fodor

Re: mod_perl: variable $end will not stay shared (nested subroutines problem)
by sierrathedog04 (Hermit) on Apr 18, 2001 at 01:46 UTC
    Yesterday there was much discussion among the senior monks of "will not stay shared" errors, and I was privileged to listen to their wisdom.

    In your case, you are seeing this error because you declare the lexical variable 'my $bufsis' outside your named subroutine read_size() but $bufsis then appears inside your named subroutine. (Variables declared using my() are called lexical in Perlish.)

    When a lexical variable and a named subroutine are in the same lexical scope, and the lexical variable is declared outside the subroutine but appears inside the subroutine you get a message such as yours.

    Solutions? Well, you can make the lexical variable a global one. Or you can pass the lexical variable as a parameter to your subroutine. Or you can make your subroutine anonymous instead of named. There are a lot of workarounds, but I don't know which is the best. I think that Tye or Tilly yesterday proposed something along the lines of the first solution, make the lexical variable a non-lexical one.