diff options
Diffstat (limited to 'lib/Log/Log4perl/Util')
-rw-r--r-- | lib/Log/Log4perl/Util/Semaphore.pm | 264 | ||||
-rw-r--r-- | lib/Log/Log4perl/Util/TimeTracker.pm | 259 |
2 files changed, 523 insertions, 0 deletions
diff --git a/lib/Log/Log4perl/Util/Semaphore.pm b/lib/Log/Log4perl/Util/Semaphore.pm new file mode 100644 index 0000000..e88e39b --- /dev/null +++ b/lib/Log/Log4perl/Util/Semaphore.pm @@ -0,0 +1,264 @@ +#////////////////////////////////////////// +package Log::Log4perl::Util::Semaphore; +#////////////////////////////////////////// +use IPC::SysV qw(IPC_RMID IPC_CREAT IPC_EXCL SEM_UNDO IPC_NOWAIT + IPC_SET IPC_STAT SETVAL); +use IPC::Semaphore; +use POSIX qw(EEXIST); +use strict; +use warnings; +use constant INTERNAL_DEBUG => 0; + +########################################### +sub new { +########################################### + my($class, %options) = @_; + + my $self = { + key => undef, + mode => undef, + uid => undef, + gid => undef, + destroy => undef, + semop_wait => .1, + semop_retries => 1, + creator => $$, + %options, + }; + + $self->{ikey} = unpack("i", pack("A4", $self->{key})); + + # Accept usernames in the uid field as well + if(defined $self->{uid} and + $self->{uid} =~ /\D/) { + $self->{uid} = (getpwnam $self->{uid})[2]; + } + + bless $self, $class; + $self->init(); + + my @values = (); + for my $param (qw(mode uid gid)) { + push @values, $param, $self->{$param} if defined $self->{$param}; + } + $self->semset(@values) if @values; + + return $self; +} + +########################################### +sub init { +########################################### + my($self) = @_; + + print "Semaphore init '$self->{key}'/'$self->{ikey}'\n" if INTERNAL_DEBUG; + + $self->{id} = semget( $self->{ikey}, + 1, + &IPC_EXCL|&IPC_CREAT|($self->{mode}||0777), + ); + + if(! defined $self->{id} and + $! == EEXIST) { + print "Semaphore '$self->{key}' already exists\n" if INTERNAL_DEBUG; + $self->{id} = semget( $self->{ikey}, 1, 0 ) + or die "semget($self->{ikey}) failed: $!"; + } elsif($!) { + die "Cannot create semaphore $self->{key}/$self->{ikey} ($!)"; + } +} + +########################################### +sub status_as_string { +########################################### + my($self, @values) = @_; + + my $sem = IPC::Semaphore->new($self->{ikey}, 1, 0); + + my $values = join('/', $sem->getall()); + my $ncnt = $sem->getncnt(0); + my $pidlast = $sem->getpid(0); + my $zcnt = $sem->getzcnt(0); + my $id = $sem->id(); + + return <<EOT; +Semaphore Status +Key ...................................... $self->{key} +iKey ..................................... $self->{ikey} +Id ....................................... $id +Values ................................... $values +Processes waiting for counter increase ... $ncnt +Processes waiting for counter to hit 0 ... $zcnt +Last process to perform an operation ..... $pidlast +EOT +} + +########################################### +sub semsetval { +########################################### + my($self, %keyvalues) = @_; + + my $sem = IPC::Semaphore->new($self->{ikey}, 1, 0); + $sem->setval(%keyvalues); +} + +########################################### +sub semset { +########################################### + my($self, @values) = @_; + + print "Setting values for semaphore $self->{key}/$self->{ikey}\n" if + INTERNAL_DEBUG; + + my $sem = IPC::Semaphore->new($self->{ikey}, 1, 0); + $sem->set(@values); +} + +########################################### +sub semlock { +########################################### + my($self) = @_; + + my $operation = pack("s!*", + # wait until it's 0 + 0, 0, 0, + # increment by 1 + 0, 1, SEM_UNDO + ); + + print "Locking semaphore '$self->{key}'\n" if INTERNAL_DEBUG; + $self->semop($self->{id}, $operation); +} + +########################################### +sub semunlock { +########################################### + my($self) = @_; + +# my $operation = pack("s!*", +# # decrement by 1 +# 0, -1, SEM_UNDO +# ); +# + print "Unlocking semaphore '$self->{key}'\n" if INTERNAL_DEBUG; + +# # ignore errors, as they might result from trying to unlock an +# # already unlocked semaphore. +# semop($self->{id}, $operation); + + semctl $self->{id}, 0, SETVAL, 0; +} + +########################################### +sub remove { +########################################### + my($self) = @_; + + print "Removing semaphore '$self->{key}'\n" if INTERNAL_DEBUG; + + semctl ($self->{id}, 0, &IPC_RMID, 0) or + die "Removing semaphore $self->{key} failed: $!"; +} + +########################################### +sub DESTROY { +########################################### + my($self) = @_; + + if($self->{destroy} && $$==$self->{creator}) { + $self->remove(); + } +} + +########################################### +sub semop { +########################################### + my($self, @args) = @_; + + my $retries = $self->{semop_retries}; + + my $rc; + + { + $rc = semop($args[0], $args[1]); + + if(!$rc and + $! =~ /temporarily unavailable/ and + $retries-- > 0) { + $rc = 'undef' unless defined $rc; + print "semop failed (rc=$rc), retrying\n", + $self->status_as_string if INTERNAL_DEBUG; + select undef, undef, undef, $self->{semop_wait}; + redo; + } + } + + $rc or die "semop(@args) failed: $! "; + $rc; +} + +1; + +__END__ + +=encoding utf8 + +=head1 NAME + +Log::Log4perl::Util::Semaphore - Easy to use semaphores + +=head1 SYNOPSIS + + use Log::Log4perl::Util::Semaphore; + my $sem = Log::Log4perl::Util::Semaphore->new( key => "abc" ); + + $sem->semlock(); + # ... critical section + $sem->semunlock(); + + $sem->semset( uid => (getpwnam("hugo"))[2], + gid => 102, + mode => 0644 + ); + +=head1 DESCRIPTION + +Log::Log4perl::Util::Semaphore provides the synchronisation mechanism +for the Synchronized.pm appender in Log4perl, but can be used independently +of Log4perl. + +As a convenience, the C<uid> field accepts user names as well, which it +translates into the corresponding uid by running C<getpwnam>. + +=head1 LICENSE + +Copyright 2002-2013 by Mike Schilli E<lt>m@perlmeister.comE<gt> +and Kevin Goess E<lt>cpan@goess.orgE<gt>. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=head1 AUTHOR + +Please contribute patches to the project on Github: + + http://github.com/mschilli/log4perl + +Send bug reports or requests for enhancements to the authors via our + +MAILING LIST (questions, bug reports, suggestions/patches): +log4perl-devel@lists.sourceforge.net + +Authors (please contact them via the list above, not directly): +Mike Schilli <m@perlmeister.com>, +Kevin Goess <cpan@goess.org> + +Contributors (in alphabetical order): +Ateeq Altaf, Cory Bennett, Jens Berthold, Jeremy Bopp, Hutton +Davidson, Chris R. Donnelly, Matisse Enzer, Hugh Esco, Anthony +Foiani, James FitzGibbon, Carl Franks, Dennis Gregorovic, Andy +Grundman, Paul Harrington, Alexander Hartmaier David Hull, +Robert Jacobson, Jason Kohles, Jeff Macdonald, Markus Peter, +Brett Rann, Peter Rabbitson, Erik Selberg, Aaron Straup Cope, +Lars Thegler, David Viner, Mac Yang. + diff --git a/lib/Log/Log4perl/Util/TimeTracker.pm b/lib/Log/Log4perl/Util/TimeTracker.pm new file mode 100644 index 0000000..35847c6 --- /dev/null +++ b/lib/Log/Log4perl/Util/TimeTracker.pm @@ -0,0 +1,259 @@ +################################################## +package Log::Log4perl::Util::TimeTracker; +################################################## + +use 5.006; +use strict; +use warnings; +use Log::Log4perl::Util; +use Carp; + +our $TIME_HIRES_AVAILABLE; + +BEGIN { + # Check if we've got Time::HiRes. If not, don't make a big fuss, + # just set a flag so we know later on that we can't have fine-grained + # time stamps + $TIME_HIRES_AVAILABLE = 0; + if(Log::Log4perl::Util::module_available("Time::HiRes")) { + require Time::HiRes; + $TIME_HIRES_AVAILABLE = 1; + } +} + +################################################## +sub new { +################################################## + my $class = shift; + $class = ref ($class) || $class; + + my $self = { + reset_time => undef, + @_, + }; + + $self->{time_function} = \&_gettimeofday unless + defined $self->{time_function}; + + bless $self, $class; + + $self->reset(); + + return $self; +} + +################################################## +sub hires_available { +################################################## + return $TIME_HIRES_AVAILABLE; +} + +################################################## +sub _gettimeofday { +################################################## + # Return secs and optionally msecs if we have Time::HiRes + if($TIME_HIRES_AVAILABLE) { + return (Time::HiRes::gettimeofday()); + } else { + return (time(), 0); + } +} + +################################################## +sub gettimeofday { +################################################## + my($self) = @_; + + my($seconds, $microseconds) = $self->{time_function}->(); + + $microseconds = 0 if ! defined $microseconds; + return($seconds, $microseconds); +} + +################################################## +sub reset { +################################################## + my($self) = @_; + + my $current_time = [$self->gettimeofday()]; + $self->{reset_time} = $current_time; + $self->{last_call_time} = $current_time; + + return $current_time; +} + +################################################## +sub time_diff { +################################################## + my($time_from, $time_to) = @_; + + my $seconds = $time_to->[0] - + $time_from->[0]; + + my $milliseconds = int(( $time_to->[1] - + $time_from->[1] ) / 1000); + + if($milliseconds < 0) { + $milliseconds = 1000 + $milliseconds; + $seconds--; + } + + return($seconds, $milliseconds); +} + +################################################## +sub milliseconds { +################################################## + my($self, $current_time) = @_; + + $current_time = [ $self->gettimeofday() ] unless + defined $current_time; + + my($seconds, $milliseconds) = time_diff( + $self->{reset_time}, + $current_time); + + return $seconds*1000 + $milliseconds; +} + +################################################## +sub delta_milliseconds { +################################################## + my($self, $current_time) = @_; + + $current_time = [ $self->gettimeofday() ] unless + defined $current_time; + + my($seconds, $milliseconds) = time_diff( + $self->{last_call_time}, + $current_time); + + $self->{last_call_time} = $current_time; + + return $seconds*1000 + $milliseconds; +} + +1; + +__END__ + +=encoding utf8 + +=head1 NAME + +Log::Log4perl::Util::TimeTracker - Track time elapsed + +=head1 SYNOPSIS + + use Log::Log4perl::Util::TimeTracker; + + my $timer = Log::Log4perl::Util::TimeTracker->new(); + + # equivalent to Time::HiRes::gettimeofday(), regardless + # if Time::HiRes is present or not. + my($seconds, $microseconds) = $timer->gettimeofday(); + + # reset internal timer + $timer->reset(); + + # return milliseconds since last reset + $msecs = $timer->milliseconds(); + + # return milliseconds since last call + $msecs = $timer->delta_milliseconds(); + +=head1 DESCRIPTION + +This utility module helps tracking time elapsed for PatternLayout's +date and time placeholders. Its accuracy depends on the availability +of the Time::HiRes module. If it's available, its granularity is +milliseconds, if not, seconds. + +The most common use of this module is calling the gettimeofday() +method: + + my($seconds, $microseconds) = $timer->gettimeofday(); + +It returns seconds and microseconds of the current epoch time. If +Time::HiRes is installed, it will simply defer to its gettimeofday() +function, if it's missing, time() will be called instead and $microseconds +will always be 0. + +To measure time elapsed in milliseconds, use the reset() method to +reset the timer to the current time, followed by one or more calls to +the milliseconds() method: + + # reset internal timer + $timer->reset(); + + # return milliseconds since last reset + $msecs = $timer->milliseconds(); + +On top of the time span between the last reset and the current time, +the module keeps track of the time between calls to delta_milliseconds(): + + $msecs = $timer->delta_milliseconds(); + +On the first call, this will return the number of milliseconds since the +last reset(), on subsequent calls, it will return the time elapsed in +milliseconds since the last call to delta_milliseconds() instead. Note +that reset() also resets the time of the last call. + +The internal timer of this module gets its time input from the POSIX time() +function, or, if the Time::HiRes module is available, from its +gettimeofday() function. To figure out which one it is, use + + if( $timer->hires_available() ) { + print "Hooray, we get real milliseconds!\n"; + } else { + print "Milliseconds are just bogus\n"; + } + +For testing purposes, a different time source can be provided, so test +suites can simulate time passing by without actually having to wait: + + my $start_time = time(); + + my $timer = Log::Log4perl::Util::TimeTracker->new( + time_function => sub { + return $start_time++; + }, + ); + +Every call to $timer->epoch() will then return a time value that is one +second ahead of the value returned on the previous call. This also means +that every call to delta_milliseconds() will return a value that exceeds +the value returned on the previous call by 1000. + +=head1 LICENSE + +Copyright 2002-2013 by Mike Schilli E<lt>m@perlmeister.comE<gt> +and Kevin Goess E<lt>cpan@goess.orgE<gt>. + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself. + +=head1 AUTHOR + +Please contribute patches to the project on Github: + + http://github.com/mschilli/log4perl + +Send bug reports or requests for enhancements to the authors via our + +MAILING LIST (questions, bug reports, suggestions/patches): +log4perl-devel@lists.sourceforge.net + +Authors (please contact them via the list above, not directly): +Mike Schilli <m@perlmeister.com>, +Kevin Goess <cpan@goess.org> + +Contributors (in alphabetical order): +Ateeq Altaf, Cory Bennett, Jens Berthold, Jeremy Bopp, Hutton +Davidson, Chris R. Donnelly, Matisse Enzer, Hugh Esco, Anthony +Foiani, James FitzGibbon, Carl Franks, Dennis Gregorovic, Andy +Grundman, Paul Harrington, Alexander Hartmaier David Hull, +Robert Jacobson, Jason Kohles, Jeff Macdonald, Markus Peter, +Brett Rann, Peter Rabbitson, Erik Selberg, Aaron Straup Cope, +Lars Thegler, David Viner, Mac Yang. + |