# # # patch "Monotone/AutomateStdio.pm" # from [7152213b7464b06ad72524cea2c006f5589959f2] # to [8cca176ed92c2cd9462ffbb7d8604481532983df] # # patch "Monotone/AutomateStdio.pod" # from [163e0f0e4e9f144e28244e76c0bc8317aba0a977] # to [1ce23141cedc55c1450df1341cebd1bb21a59ecf] # ============================================================ --- Monotone/AutomateStdio.pm 7152213b7464b06ad72524cea2c006f5589959f2 +++ Monotone/AutomateStdio.pm 8cca176ed92c2cd9462ffbb7d8604481532983df @@ -57,6 +57,7 @@ use Carp; # Standard Perl and CPAN modules. use Carp; +use IO::Poll qw(POLLIN); use IPC::Open3; use POSIX qw(:errno_h); use Symbol qw(gensym); @@ -74,15 +75,18 @@ my $database_locked_re = qr/.*sqlite err my $database_locked_re = qr/.*sqlite error: database is locked.*/; -# Global error and database locked callback routine references and associated -# client data. +# Global error, database locked and io wait callback routine references and +# associated client data. +my $carper = sub { return; }; my $croaker = \&croak; my $db_locked_handler = sub { return; }; -my($carper, - $db_locked_handler_data, +my $io_wait_handler = sub { return; }; +my($db_locked_handler_data, $error_handler, $error_handler_data, + $io_wait_handler_data, + $io_wait_handler_timeout, $warning_handler, $warning_handler_data); @@ -127,6 +131,7 @@ sub register_error_handler($;$$$); sub parents(address@hidden); sub register_db_locked_handler(;$$$); sub register_error_handler($;$$$); +sub register_io_wait_handler(;$$$$); sub roots($\@); sub select(address@hidden); sub tags($$;$); @@ -151,7 +156,7 @@ our @EXPORT_OK = qw(); our @EXPORT = qw(); our @EXPORT_OK = qw(); -our $VERSION = 0.5; +our $VERSION = 0.6; # ############################################################################## # @@ -180,21 +185,26 @@ sub new($;$) my $this; - $this = {db_name => $db_name, - mtn_pid => 0, - mtn_in => undef, - mtn_out => undef, - mtn_err => undef, - mtn_err_msg => "", - mtn_aif_major => 0, - mtn_aif_minor => 0, - cmd_cnt => 0, - db_locked_handler => undef, - db_locked_handler_data => undef}; + $this = {db_name => $db_name, + mtn_pid => 0, + mtn_in => undef, + mtn_out => undef, + mtn_err => undef, + poll => undef, + error_msg => "", + mtn_aif_major => 0, + mtn_aif_minor => 0, + cmd_cnt => 0, + db_locked_handler => undef, + db_locked_handler_data => undef, + io_wait_handler => undef, + io_wait_handler_data => undef, + io_wait_handler_timeout => 1}; + bless($this, $class); startup($this); - return bless($this, $class); + return $this; } # @@ -1949,9 +1959,9 @@ sub toposort($\@@) # # Routine - register_error_handler # -# Description - Register the specified routine as an error handler for this -# library. This is a class method rather than an object one -# as errors can be raised when calling the constructor. +# Description - Register the specified routine as an error handler for +# class. This is a class method rather than an object one as +# errors can be raised when calling the constructor. # # Data - $this : The object. This may not be present # depending upon how this method is called and @@ -1959,7 +1969,7 @@ sub toposort($\@@) # $severity : The level of error that the handler is being # registered for. One of "error", "warning" or # "both". -# $callback : A reference to the error handler routine. If +# $handler : A reference to the error handler routine. If # this is not provided then the existing error # handler routine is unregistered and errors # are handled in the default way. @@ -1987,8 +1997,7 @@ sub register_error_handler($;$$$) else { $croaker = \&croak; - $error_handler = undef; - $error_handler_data = undef; + $error_handler = $error_handler_data = undef; } } elsif ($severity eq "warning") @@ -2001,7 +2010,8 @@ sub register_error_handler($;$$$) } else { - $carper = $warning_handler = $warning_handler_data = undef; + $carper = sub { return; }; + $warning_handler = $warning_handler_data = undef; } } elsif ($severity eq "both") @@ -2015,8 +2025,9 @@ sub register_error_handler($;$$$) } else { - $carper = $error_handler = $warning_handler = undef; + $warning_handler = $warning_handler_data = undef; $error_handler_data = $warning_handler_data = undef; + $carper = sub { return; }; $croaker = \&croak; } } @@ -2032,14 +2043,15 @@ sub register_error_handler($;$$$) # Routine - register_db_locked_handler # # Description - Register the specified routine as a database locked handler -# for this library. This is a class method rather than an -# object one as locked databases can be encountered when -# calling the constructor. +# for this class. This is both a class as well as an object +# method. When used as a class method, the specified database +# locked handler is used as the default handler for all those +# objects that do not specify their own handlers. # -# Data - $this : The object. This may not be present -# depending upon how this method is called and -# is ignored if it is present anyway. -# $callback : A reference to the database locked handler +# Data - $this : Either the object, the package name or not +# present depending upon how this method is +# called. +# $handler : A reference to the database locked handler # routine. If this is not provided then the # existing database locked handler routine is # unregistered and database locking clashes @@ -2056,13 +2068,13 @@ sub register_db_locked_handler(;$$$) { my $this; - if ($_[0] eq __PACKAGE__) + if (ref($_[0]) eq __PACKAGE__) { + $this = $_[0]; shift(); } - elsif (ref($_[0]) eq __PACKAGE__) + elsif ($_[0] eq __PACKAGE__) { - $this = $_[0]; shift(); } my($handler, $client_data) = @_; @@ -2097,6 +2109,97 @@ sub register_db_locked_handler(;$$$) # ############################################################################## # +# Routine - register_io_wait_handler +# +# Description - Register the specified routine as an I/O wait handler for +# this class. This is both a class as well as an object +# method. When used as a class method, the specified I/O wait +# handler is used as the default handler for all those +# objects that do not specify their own handlers. +# +# Data - $this : Either the object, the package name or not +# present depending upon how this method is +# called. +# $handler : A reference to the I/O wait handler routine. +# If this is not provided then the existing +# I/O wait handler routine is unregistered. +# $timeout : The timeout, in seconds, that this class +# should wait for input before calling the I/O +# wait handler. +# $client_data : The client data that is to be passed to the +# registered I/O wait handler when it is +# called. +# +############################################################################## + + + +sub register_io_wait_handler(;$$$$) +{ + + my $this; + if (ref($_[0]) eq __PACKAGE__) + { + $this = $_[0]; + shift(); + } + elsif ($_[0] eq __PACKAGE__) + { + shift(); + } + my($handler, $timeout, $client_data) = @_; + + if (defined($timeout)) + { + if ($timeout !~ m/^\d*\.{0,1}\d+$/ || $timeout < 0 || $timeout > 20) + { + my $msg = + "I/O wait handler timeout invalid or out of range, resetting"; + if (defined($this)) + { + $this->{error_msg} = $msg; + &$carper($this->{error_msg}); + } + carp($msg); + $timeout = 1; + } + } + else + { + $timeout = 1; + } + + if (defined($this)) + { + if (defined($handler)) + { + $this->{io_wait_handler} = $handler; + $this->{io_wait_handler_data} = $client_data; + $this->{io_wait_handler_timeout} = $timeout; + } + else + { + $this->{io_wait_handler} = $this->{io_wait_handler_data} = undef; + } + } + else + { + if (defined($handler)) + { + $io_wait_handler = $handler; + $io_wait_handler_data = $client_data; + $io_wait_handler_timeout = $timeout; + } + else + { + $io_wait_handler = $io_wait_handler_data = undef; + } + } + +} +# +############################################################################## +# # Routine - get_db_name # # Description - Return the the file name of the Monotone database as given @@ -2124,12 +2227,13 @@ sub get_db_name($) # # Routine - get_error_message # -# Description - Return the last error message received from the mtn -# subprocess. +# Description - Return the message for the last error reported by this +# class. # # Data - $this : The object. -# Return Value : The last error message received, or an empty -# string if nothing has gone wrong yet. +# Return Value : The message for the last error detected, or +# an empty string if nothing has gone wrong +# yet. # ############################################################################## @@ -2140,7 +2244,7 @@ sub get_error_message($) my $this = $_[0]; - return $this->{mtn_err_msg}; + return $this->{error_msg}; } # @@ -2229,6 +2333,7 @@ sub closedown($) } } } + $this->{poll} = undef; $this->{mtn_pid} = 0; } @@ -2416,7 +2521,7 @@ sub mtn_command_with_options($$$\@@) # See if we are to retry on database locked conditions. $retry = &$handler($this, $handler_data) - if ($this->{mtn_err_msg} =~ m/$database_locked_re/ + if ($this->{error_msg} =~ m/$database_locked_re/ || $db_locked_exception); # If we are to retry then close down the subordinate mtn process, @@ -2429,7 +2534,7 @@ sub mtn_command_with_options($$$\@@) } else { - &$carper($this->{mtn_err_msg}) if (defined($carper)); + &$carper($this->{error_msg}); return; } } @@ -2473,6 +2578,9 @@ sub mtn_read_output($\$) $err, $err_code, $err_occurred, + $handler, + $handler_data, + $handler_timeout, $header, $i, $last, @@ -2481,6 +2589,23 @@ sub mtn_read_output($\$) $err = $this->{mtn_err}; + # Work out what I/O wait handler is to be used. + + if (defined($this->{io_wait_handler})) + { + $handler = $this->{io_wait_handler}; + $handler_data = $this->{io_wait_handler_data}; + $handler_timeout = $this->{io_wait_handler_timeout}; + } + else + { + $handler = $io_wait_handler; + $handler_data = $io_wait_handler_data; + $handler_timeout = $io_wait_handler_timeout; + } + + # Read in the data. + $$buffer = ""; $chunk_start = 1; $err_occurred = 0; @@ -2489,6 +2614,14 @@ sub mtn_read_output($\$) do { + # Wait here for some data, calling the I/O wait handler every second + # whilst we wait. + + while ($this->{poll}->poll($handler_timeout) == 0) + { + &$handler($this, $handler_data); + } + # If necessary, read in and process the chunk header, then we know how # much to read in etc. @@ -2575,7 +2708,7 @@ sub mtn_read_output($\$) if ($err_occurred) { - $this->{mtn_err_msg} = $$buffer; + $this->{error_msg} = $$buffer; $$buffer = ""; return; } @@ -2632,6 +2765,8 @@ sub startup($) "stdio"); } $this->{cmd_cnt} = 0; + $this->{poll} = IO::Poll->new(); + $this->{poll}->mask($this->{mtn_out}, POLLIN); interface_version($this, $version); ($this->{mtn_aif_major}, $this->{mtn_aif_minor}) = ($version =~ m/^(\d+)\.(\d+)$/); ============================================================ --- Monotone/AutomateStdio.pod 163e0f0e4e9f144e28244e76c0bc8317aba0a977 +++ Monotone/AutomateStdio.pod 1ce23141cedc55c1450df1341cebd1bb21a59ecf @@ -6,7 +6,7 @@ Monotone::AutomateStdio - Perl interface =head1 VERSION -0.5 +0.6 =head1 SYNOPSIS @@ -89,14 +89,15 @@ Please note: =item 1) -Warnings are generated whenever a message is received from an mtn subprocess -that is flagged as being in error. For example, this occurs when an invalid -selector is given to the $mtn-Eselect() method. The subprocess does not -exit under these conditions. Errors, however, are generated whenever this class -detects abnormal behaviour such as output that is formatted in an unexpected -manor or output appearing on the mtn subprocess's STDERR file descriptor. Under -these circumstances it is expected that the application should exit or at least -destroy any Monotone::AutomateStdio objects that are in error. +Warnings can be generated internally or whenever a message is received from an +mtn subprocess that is flagged as being in error. For example, this occurs when +an invalid selector is given to the $mtn-Eselect() method. The subprocess +does not exit under these conditions. Errors, however, are generated whenever +this class detects abnormal behaviour such as output that is formatted in an +unexpected manor or output appearing on the mtn subprocess's STDERR file +descriptor. Under these circumstances it is expected that the application +should exit or at least destroy any Monotone::AutomateStdio objects that are in +error. =item 2) @@ -123,20 +124,47 @@ database locked conditions. The value of Registers the handler specified as a subroutine reference in $handler for database locked conditions. The value of $client_data is simply passed to the -handler and can be used by the caller to provide a context. This is a class -method as well as an object one as database lock conditions can occur when -calling a constructor. When used as a class method, this method can be used to -set up a database locked handler for all Monotone::AutomateStdio objects that -do not specify their own handlers. If no handler is given then the database -locked condition handling is reset to the default behaviour. +handler and can be used by the caller to provide a context. This is both a +class method as well as an object one. When used as a class method, the +specified database locked handler is used for all Monotone::AutomateStdio +objects that do not specify their own handlers. If no handler is given then the +database locked condition handling is reset to the default behaviour. The handler subroutine is given two arguments, the first one is the -Monotone::AutomateStdio object that encountered the locked database or undef if -this happened during construction and the second is the value passed in as -$client_data when the hander was registered. If the handler returns true then -the command that failed is retried, if false is returned (undef) then the -default behaviour is performed, which is to treat it like any other error. +Monotone::AutomateStdio object that encountered the locked database and the +second is the value passed in as $client_data when the hander was +registered. If the handler returns true then the request that failed is +retried, if false is returned (undef) then the default behaviour is performed, +which is to treat it like any other error. +Typically one would register a database locked handler when you are using this +class in a long running application where it is quite possible that a user +might do an mtn sync in the background. For example, mtn-browse uses this +handler to display a `Database is locked, please retry...' dialog window. + +=item Bregister_io_wait_handler([$handler, +$timeout[, $client_data]])> + +Registers the handler specified as a subroutine reference in $handler for I/O +wait conditions. This class will call the handler after waiting $timeout +seconds for data to come from the mtn subprocess. The value of $client_data is +simply passed to the handler and can be used by the caller to provide a +context. This is both a class method as well as an object one. When used as a +class method, the specified I/O wait handler is used for all +Monotone::AutomateStdio objects that do not specify their own handlers. If no +handler is given then I/O wait handling is reset to the default behaviour. + +The handler subroutine is given two arguments, the first one is the +Monotone::AutomateStdio object that is waiting for output from the mtn +subprocess and the second is the value passed in as $client_data when the +handler was registered. + +Typically one would register an I/O wait handler when you are using this class +in an interactive application where it may be necessary to do some periodic +background processing whilst waiting for the mtn subprocess to do +something. For example, mtn-browse uses this handler to process any outstanding +Gtk2 events so that the application can keep its windows up to date. + =back =head1 OBJECT METHODS @@ -453,9 +481,15 @@ Get a list of parents for the specified =item B<$mtn-Eregister_db_locked_handler([$handler[, $client_data]])> -Registers a database locked handler for the object rather than globally. For +Registers a database locked handler for the object rather than the class. For further details please see the description of the class method. +=item B<$mtn-Eregister_io_wait_handler([$handler, $timeout +[, $client_data]])> + +Registers an I/O wait handler for the object rather than the class. For further +details please see the description of the class method. + =item B<$mtn-Eroots(address@hidden)> Get a list of root revisions, i.e. revisions with no parents. @@ -487,7 +521,8 @@ Monotone::AutomateStdio-Eregister_db Except for the constructor and the Monotone::AutomateStdio-Eregister_db_locked_handler(), -Monotone::AutomateStdio-Eregister_error_handler(), $mtn-Eclosedown(), +Monotone::AutomateStdio-Eregister_error_handler(), +Monotone::AutomateStdio-Eregister_io_wait_handler(), $mtn-Eclosedown(), $mtn-Eget_db_name(), $mtn-Eget_error_message() and $mtn-Eget_pid() methods, all remaining methods return a boolean success indicator, true for success or false for failure (where true is 1 and false is undef). The @@ -495,8 +530,9 @@ Monotone::AutomateStdio-Eregister_db string or undef, $mtn-Eget_error_message() returns a string, $mtn-Eget_pid() returns an integer and Monotone::AutomateStdio-Eregister_db_locked_handler(), -Monotone::AutomateStdio-Eregister_error_handler() and $mtn-Eclosedown() -do not return anything. +Monotone::AutomateStdio-Eregister_error_handler(), +Monotone::AutomateStdio-Eregister_io_wait_handler() and +$mtn-Eclosedown() do not return anything. =head1 NOTES