1 package FCGI::ProcManager::Constrained;
4 use Carp qw/ confess /;
5 use base 'FCGI::ProcManager';
11 my $self = $proto->SUPER::new(@_);
12 $self->{max_requests} = $ENV{PM_MAX_REQUESTS} || 0 unless defined $self->{max_requests};
13 $self->{sizecheck_num_requests} = $ENV{PM_SIZECHECK_NUM_REQUESTS} || 0 unless defined $self->{sizecheck_num_requests};
14 $self->{max_size} = $ENV{PM_MAX_SIZE} || 0 unless defined $self->{max_size};
15 if ($self->{sizecheck_num_requests} && ! _can_check_size()) {
16 confess "Cannot load size check modules for your platform: sizecheck_num_requests > 0 unsupported";
21 sub max_requests { shift->pm_parameter('max_requests', @_); }
23 sub sizecheck_num_requests { shift->pm_parameter('sizecheck_num_requests', @_); }
25 sub max_size { shift->pm_parameter('max_size', @_); }
29 $self->SUPER::handling_init();
30 $self->{_request_counter} = 0;
33 sub pm_post_dispatch {
35 if ($self->max_requests > 0 && ++$self->{_request_counter} == $self->max_requests) {
36 $self->pm_exit("safe exit after max_requests");
38 if ($self->sizecheck_num_requests
39 and $self->{_request_counter} # Not the first request
40 and $self->{_request_counter} % $self->sizecheck_num_requests == 0
42 $self->exit("safe exit due to memory limits exceeded after " . $self->request_count . " requests")
43 if $self->_limits_are_exceeded;
45 $self->SUPER::pm_post_dispatch();
48 sub _limits_are_exceeded {
51 my ($size, $share, $unshared) = $self->_check_size();
53 return 1 if $self->max_size && $size > $self->max_size;
54 return 0 unless $share;
56 # return 1 if $self->min_share_size && $share < $self->min_share_size;
57 # return 1 if $self->max_unshared_size && $unshared > $self->max_unshared_size;
62 # The following code is wholesale is nicked from Apache::SizeLimit::Core
67 my ($size, $share) = $class->_platform_check_size();
69 return ($size, $share, $size - $share);
74 eval { require($mod); 1; }
78 my ($major,$minor) = split(/\./, $Config{'osvers'});
79 if ($Config{'osname'} eq 'solaris' &&
80 (($major > 2) || ($major == 2 && $minor >= 6))) {
81 *_can_check_size = sub () { 1 };
82 *_platform_check_size = \&_solaris_2_6_size_check;
83 *_platform_getppid = \&_perl_getppid;
85 elsif ($Config{'osname'} eq 'linux' && _load('Linux::Pid')) {
86 *_platform_getppid = \&_linux_getppid;
87 *_can_check_size = sub () { 1 };
88 if (_load('Linux::Smaps') && Linux::Smaps->new($$)) {
90 *_platform_check_size = \&_linux_smaps_size_check;
94 *_platform_check_size = \&_linux_size_check;
97 elsif ($Config{'osname'} =~ /(?:bsd|aix)/i && _load('BSD::Resource')) {
98 # on OSX, getrusage() is returning 0 for proc & shared size.
99 *_can_check_size = sub () { 1 };
100 *_platform_check_size = \&_bsd_size_check;
101 *_platform_getppid = \&_perl_getppid;
104 *_can_check_size = sub () { 0 };
108 sub _linux_smaps_size_check {
111 return $class->_linux_size_check() unless $USE_SMAPS;
113 my $s = Linux::Smaps->new($$)->all;
114 return ($s->size, $s->shared_clean + $s->shared_dirty);
117 sub _linux_size_check {
120 my ($size, $share) = (0, 0);
121 if (open my $fh, '<', '/proc/self/statm') {
122 ($size, $share) = (split /\s/, scalar <$fh>)[0,2];
126 $class->_error_log("Fatal Error: couldn't access /proc/self/status");
129 # linux on intel x86 has 4KB page size...
130 return ($size * 4, $share * 4);
133 sub _solaris_2_6_size_check {
136 my $size = -s "/proc/self/as"
137 or $class->_error_log("Fatal Error: /proc/self/as doesn't exist or is empty");
138 $size = int($size / 1024);
140 # return 0 for share, to avoid undef warnings
144 # rss is in KB but ixrss is in BYTES.
145 # This is true on at least FreeBSD, OpenBSD, & NetBSD
146 sub _bsd_size_check {
148 my @results = BSD::Resource::getrusage();
149 my $max_rss = $results[2];
150 my $max_ixrss = int ( $results[3] / 1024 );
152 return ($max_rss, $max_ixrss);
155 sub _win32_size_check {
158 # get handle on current process
159 my $get_current_process = Win32::API->new(
161 'get_current_process',
165 my $proc = $get_current_process->Call();
167 # memory usage is bundled up in ProcessMemoryCounters structure
168 # populated by GetProcessMemoryInfo() win32 call
169 my $DWORD = 'B32'; # 32 bits
170 my $SIZE_T = 'I'; # unsigned integer
172 # build a buffer structure to populate
173 my $pmem_struct = "$DWORD" x 2 . "$SIZE_T" x 8;
175 = pack( $pmem_struct, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
177 # GetProcessMemoryInfo is in "psapi.dll"
178 my $get_process_memory_info = new Win32::API(
180 'GetProcessMemoryInfo',
185 my $bool = $get_process_memory_info->Call(
188 length $mem_counters,
191 # unpack ProcessMemoryCounters structure
192 my $peak_working_set_size =
193 (unpack($pmem_struct, $mem_counters))[2];
195 # only care about peak working set size
196 my $size = int($peak_working_set_size / 1024);
201 sub _perl_getppid { return getppid }
202 sub _linux_getppid { return Linux::Pid::getppid() }