File Coverage

blib/lib/MogileFS/Util.pm
Criterion Covered Total %
statement 33 136 24.2
branch 4 52 7.6
condition 0 8 0.0
subroutine 7 25 28.0
pod 0 18 0.0
total 44 239 18.4


line stmt bran cond sub pod time code
1             package MogileFS::Util;
2 8     8   25445 use strict;
  8         20  
  8         395  
3 8     8   47 use Carp qw(croak);
  8         21  
  8         476  
4 8     8   1064 use Time::HiRes;
  8         2097  
  8         76  
5 8     8   5234 use MogileFS::Exception;
  8         20  
  8         614  
6 8     8   9579 use MogileFS::DeviceState;
  8         23  
  8         31429  
7              
8             require Exporter;
9             our @ISA = qw(Exporter);
10             our @EXPORT_OK = qw(
11             error undeferr debug fatal daemonize weighted_list every
12             wait_for_readability wait_for_writeability throw error_code
13             max min first okay_args device_state
14             );
15              
16             sub every {
17 0     0 0 0 my ($delay, $code) = @_;
18 0         0 my ($worker, $psock_fd);
19 0 0       0 if ($worker = MogileFS::ProcManager->is_child) {
20 0         0 $psock_fd = $worker->psock_fd;
21             }
22             CODERUN:
23 0         0 while (1) {
24 0         0 my $start = Time::HiRes::time();
25 0         0 my $explicit_sleep = undef;
26              
27             # run the code in a loop, so "next" will get out of it.
28 0         0 foreach (1) {
29             $code->(sub {
30 0     0   0 $explicit_sleep = shift;
31 0         0 });
32             }
33              
34 0         0 my $now = Time::HiRes::time();
35 0         0 my $took = $now - $start;
36 0 0       0 my $sleep_for = defined $explicit_sleep ? $explicit_sleep : ($delay - $took);
37 0 0       0 next unless $sleep_for > 0;
38              
39             # simple case, not in a child process (this never happens currently)
40 0 0       0 unless ($psock_fd) {
41 0         0 Time::HiRes::sleep($sleep_for);
42 0         0 next;
43             }
44              
45 0         0 while ($sleep_for > 0) {
46 0         0 my $last_time_pre_sleep = $now;
47 0         0 $worker->forget_woken_up;
48 0 0       0 if (wait_for_readability($psock_fd, $sleep_for)) {
49             # TODO: uncomment this and watch an idle server and how many wakeups. could optimize.
50             #local $Mgd::POST_SLEEP_DEBUG = 1;
51             #warn "WOKEN UP FROM SLEEP in $worker [$$]\n";
52 0         0 $worker->read_from_parent;
53 0 0       0 next CODERUN if $worker->was_woken_up;
54             }
55 0         0 $now = Time::HiRes::time();
56 0         0 $sleep_for -= ($now - $last_time_pre_sleep);
57             }
58             }
59             }
60              
61             sub debug {
62 0     0 0 0 my ($msg, $level) = @_;
63 0 0       0 return unless $Mgd::DEBUG >= 1;
64 0 0       0 if (my $worker = MogileFS::ProcManager->is_child) {
65 0         0 $worker->send_to_parent("debug $msg");
66             } else {
67 0         0 my $dbg = "[debug] $msg";
68 0         0 MogileFS::ProcManager->NoteError(\$dbg);
69 0         0 Mgd::log('debug', $msg);
70             }
71             }
72              
73             our $last_error;
74             sub error {
75 0     0 0 0 my ($errmsg) = @_;
76 0         0 $last_error = $errmsg;
77 0 0       0 if (my $worker = MogileFS::ProcManager->is_child) {
78 0         0 my $msg = "error $errmsg";
79 0         0 $msg =~ s/\s+$//;
80 0         0 $worker->send_to_parent($msg);
81             } else {
82 0         0 MogileFS::ProcManager->NoteError(\$errmsg);
83 0         0 Mgd::log('debug', $errmsg);
84             }
85 0         0 return 0;
86             }
87              
88             # like error(), but returns undef.
89             sub undeferr {
90 0     0 0 0 error(@_);
91 0         0 return undef;
92             }
93              
94             sub last_error {
95 0     0 0 0 return $last_error;
96             }
97              
98             sub fatal {
99 0     0 0 0 my ($errmsg) = @_;
100 0         0 error($errmsg);
101 0         0 die $errmsg;
102             }
103              
104             sub throw {
105 0     0 0 0 my ($errcode) = @_;
106 0         0 MogileFS::Exception->new($errcode)->throw;
107             }
108              
109             sub error_code {
110 0     0 0 0 my ($ex) = @_;
111 0 0       0 return "" unless UNIVERSAL::isa($ex, "MogileFS::Exception");
112 0         0 return $ex->code;
113             }
114              
115             sub daemonize {
116 0     0 0 0 my($pid, $sess_id, $i);
117              
118             ## Fork and exit parent
119 0 0       0 if ($pid = fork) { exit 0; }
  0         0  
120              
121             ## Detach ourselves from the terminal
122 0 0       0 croak "Cannot detach from controlling terminal"
123             unless $sess_id = POSIX::setsid();
124              
125             ## Prevent possibility of acquiring a controling terminal
126 0         0 $SIG{'HUP'} = 'IGNORE';
127 0 0       0 if ($pid = fork) { exit 0; }
  0         0  
128              
129             ## Change working directory
130 0         0 chdir "/";
131              
132             ## Clear file creation mask
133 0         0 umask 0;
134              
135 0 0       0 print STDERR "Daemon running as pid $$.\n" if $MogileFS::DEBUG;
136              
137             ## Close open file descriptors
138 0         0 close(STDIN);
139 0         0 close(STDOUT);
140 0         0 close(STDERR);
141              
142             ## Reopen stderr, stdout, stdin to /dev/null
143 0 0       0 if ( $MogileFS::DEBUG ) {
144 0         0 open(STDIN, "+>/tmp/mogilefsd.log");
145             } else {
146 0         0 open(STDIN, "+>/dev/null");
147             }
148 0         0 open(STDOUT, "+>&STDIN");
149 0         0 open(STDERR, "+>&STDIN");
150             }
151              
152             # input:
153             # given an array of arrayrefs of [ item, weight ], returns weighted randomized
154             # list of items (without the weights, not arrayref; just list)
155             #
156             # a weight of 0 means to exclude that item from the results list; i.e. it's not
157             # ever used
158             #
159             # example:
160             # my @items = weighted_list( [ 1, 10 ], [ 2, 20 ], [ 3, 0 ] );
161             #
162             # returns (1, 2) or (2, 1) with the latter far more likely
163             sub weighted_list (@) {
164 100     100 0 1238 my @list = grep { $_->[1] > 0 } @_;
  200         447  
165 100         94 my @ret;
166              
167 100         98 my $sum = 0;
168 100         261 $sum += $_->[1] foreach @list;
169              
170             my $getone = sub {
171 200 100   200   538 return shift(@list)->[0]
172             if scalar(@list) == 1;
173              
174 100         200 my $val = rand() * $sum;
175 100         101 my $curval = 0;
176 100         222 for (my $idx = 0; $idx < scalar(@list); $idx++) {
177 176         184 my $item = $list[$idx];
178 176         204 $curval += $item->[1];
179 176 100       422 if ($curval >= $val) {
180 100         134 my ($ret) = splice(@list, $idx, 1);
181 100         121 $sum -= $item->[1];
182 100         361 return $ret->[0];
183             }
184             }
185 100         331 };
186              
187 100         254 push @ret, $getone->() while @list;
188 100         520 return @ret;
189             }
190              
191             # given a file descriptor number and a timeout, wait for that descriptor to
192             # become readable; returns 0 or 1 on if it did or not
193             sub wait_for_readability {
194 0     0 0   my ($fileno, $timeout) = @_;
195 0 0 0       return 0 unless $fileno && $timeout >= 0;
196              
197 0           my $rin = '';
198 0           vec($rin, $fileno, 1) = 1;
199 0           my $nfound = select($rin, undef, undef, $timeout);
200              
201             # nfound can be undef or 0, both failures, or 1, a success
202 0 0         return $nfound ? 1 : 0;
203             }
204              
205             sub wait_for_writeability {
206 0     0 0   my ($fileno, $timeout) = @_;
207 0 0 0       return 0 unless $fileno && $timeout;
208              
209 0           my $rout = '';
210 0           vec($rout, $fileno, 1) = 1;
211 0           my $nfound = select(undef, $rout, undef, $timeout);
212              
213             # nfound can be undef or 0, both failures, or 1, a success
214 0 0         return $nfound ? 1 : 0;
215             }
216              
217             # if given an HTTP URL, break it down into [ host, port, URI ], else
218             # returns die, because we don't support non-http-mode anymore
219             sub url_parts {
220 0     0 0   my $path = shift;
221 0 0         if ($path =~ m!^http://(.+?)(?::(\d+))?(/.+)$!) {
222 0   0       return [ $1, $2 || 80, $3 ];
223             }
224 0           Carp::croak("Bogus URL: $path");
225             }
226              
227             sub max {
228 0     0 0   my ($n1, $n2) = @_;
229 0 0         return $n1 if $n1 > $n2;
230 0           return $n2;
231             }
232              
233             sub min {
234 0     0 0   my ($n1, $n2) = @_;
235 0 0         return $n1 if $n1 < $n2;
236 0           return $n2;
237             }
238              
239             sub first (&@) {
240 0     0 0   my $code = shift;
241 0           foreach (@_) {
242 0 0         return $_ if $code->();
243             }
244 0           undef;
245             }
246              
247             sub okay_args {
248 0     0 0   my ($href, @okay) = @_;
249 0           my %left = %$href;
250 0           delete $left{$_} foreach @okay;
251 0 0         return 1 unless %left;
252 0           Carp::croak("Unknown argument(s): " . join(", ", sort keys %left));
253             }
254              
255             sub device_state {
256 0     0 0   my ($state) = @_;
257 0           return MogileFS::DeviceState->of_string($state);
258             }
259              
260             1;