From bdc12f8b125a85e80c2129a33578fec8bd2246bb Mon Sep 17 00:00:00 2001 From: rudolfkoenig <> Date: Sat, 25 Jun 2016 15:26:33 +0000 Subject: [PATCH] HttpUtils: extend timeout in blocking situations (Forum #54697) git-svn-id: https://svn.fhem.de/fhem/trunk@11715 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/HttpUtils.pm | 42 +++++++++++++++++++++++------------------- fhem/fhem.pl | 35 +++++++++++++++++++++++------------ 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/fhem/FHEM/HttpUtils.pm b/fhem/FHEM/HttpUtils.pm index ed8436b2e..ed4d7b3a1 100644 --- a/fhem/FHEM/HttpUtils.pm +++ b/fhem/FHEM/HttpUtils.pm @@ -73,19 +73,21 @@ HttpUtils_Close($) } sub -HttpUtils_Err($$) +HttpUtils_Err($) { - my ($hash, $errtxt) = @_; - $hash = $hash->{hash}; + my ($lhash, $errtxt) = @_; + my $hash = $lhash->{hash}; + + if($lhash->{sts} && $lhash->{sts} == $selectTimestamp) { # busy loop check + Log 4, "extending '$lhash->{msg} $hash->{addr}' timeout due to busy loop"; + InternalTimer(gettimeofday()+1, "HttpUtils_Err", $lhash); + return; + } return if(!defined($hash->{FD})); # Already closed HttpUtils_Close($hash); - $hash->{callback}($hash, "$errtxt $hash->{addr} timed out", ""); + $hash->{callback}($hash, "$lhash->{msg} $hash->{addr} timed out", ""); } -sub HttpUtils_ConnErr($) { my ($hash) = @_; HttpUtils_Err($hash, "connect to");} -sub HttpUtils_ReadErr($) { my ($hash) = @_; HttpUtils_Err($hash, "read from"); } -sub HttpUtils_WriteErr($){ my ($hash) = @_; HttpUtils_Err($hash, "write to"); } - sub HttpUtils_File($) { @@ -154,11 +156,10 @@ HttpUtils_gethostbyname($$$) my %dh = ( conn=>$c, FD=>$c->fileno(), NAME=>"DNS", origHash=>$hash, addr=>$dnsServer, callback=>$fn ); - my %timerHash = ( hash => \%dh ); + my %timerHash = ( hash=>\%dh, msg=>"DNS" ); my $bhost = join("", map { pack("CA*",length($_),$_) } split(/\./, $host)); my $qry = pack("nnnnnn", 0x7072,0x0100,1,0,0,0) . $bhost . pack("Cnn", 0,1,1); my $ql = length($qry); - my $dnsTo = 0.25; $dh{directReadFn} = sub() { # Parse the answer RemoveInternalTimer(\%timerHash); @@ -177,17 +178,20 @@ HttpUtils_gethostbyname($$$) $selectlist{\%dh} = \%dh; my $dnsQuery; + my $dnsTo = 0.25; + my $lSelectTs = $selectTimestamp; $dnsQuery = sub() { - $dnsTo *= 2; - return HttpUtils_Err(\%timerHash, "DNS") if($dnsTo > $hash->{timeout}/2); + $dnsTo *= 2 if($lSelectTs != $selectTimestamp); + $lSelectTs = $selectTimestamp; + return HttpUtils_Err(\%timerHash) if($dnsTo > $hash->{timeout}/2); my $ret = syswrite $dh{conn}, $qry; if(!$ret || $ret != $ql) { my $err = $!; HttpUtils_Close(\%dh); return $fn->($hash, "DNS write error: $err", undef); } - InternalTimer(gettimeofday()+$dnsTo, $dnsQuery, \%timerHash, 0); + InternalTimer(gettimeofday()+$dnsTo, $dnsQuery, \%timerHash); }; $dnsQuery->(); @@ -241,7 +245,7 @@ HttpUtils_Connect($) (int($!)==140 && $^O eq "MSWin32")) { # Nonblocking connect $hash->{FD} = $hash->{conn}->fileno(); - my %timerHash = ( hash => $hash ); + my %timerHash=(hash=>$hash,sts=>$selectTimestamp,msg=>"connect to"); $hash->{directWriteFn} = sub() { delete($hash->{FD}); delete($hash->{directWriteFn}); @@ -262,7 +266,7 @@ HttpUtils_Connect($) $hash->{NAME}="" if(!defined($hash->{NAME}));#Delete might check it $selectlist{$hash} = $hash; InternalTimer(gettimeofday()+$hash->{timeout}, - "HttpUtils_ConnErr", \%timerHash, 0); + "HttpUtils_Err", \%timerHash); return undef; } else { $hash->{callback}($hash, "connect to $hash->{addr}: $!", ""); @@ -379,7 +383,7 @@ HttpUtils_Connect2($) $hash->{FD} = $hash->{conn}->fileno(); $hash->{buf} = ""; $hash->{NAME} = "" if(!defined($hash->{NAME})); - my %timerHash = ( hash => $hash ); + my %timerHash = (hash=>$hash, checkSTS=>$selectTimestamp, msg=>"write to"); $hash->{directReadFn} = sub() { my $buf; my $len = sysread($hash->{conn},$buf,65536); @@ -409,13 +413,13 @@ HttpUtils_Connect2($) shutdown($hash->{conn}, 1) if($s); delete($hash->{directWriteFn}); RemoveInternalTimer(\%timerHash); + $timerHash{msg} = "read from"; InternalTimer(gettimeofday()+$hash->{timeout}, - "HttpUtils_ReadErr", \%timerHash, 0); + "HttpUtils_Err", \%timerHash); } }; $selectlist{$hash} = $hash; - InternalTimer(gettimeofday()+$hash->{timeout}, - "HttpUtils_WriteErr", \%timerHash, 0); + InternalTimer(gettimeofday()+$hash->{timeout}, "HttpUtils_Err",\%timerHash); return undef; } else { diff --git a/fhem/fhem.pl b/fhem/fhem.pl index 38ac8f16f..e70ddbf0d 100755 --- a/fhem/fhem.pl +++ b/fhem/fhem.pl @@ -198,13 +198,19 @@ sub cfgDB_WriteFile($@); # VOLATILE- Set if the definition should be saved to the "statefile" # NOTIFYDEV - if set, the notifyFn will only be called for this device +use vars qw($auth_refresh); +use vars qw($cmdFromAnalyze); # used by the warnings-sub +use vars qw($cvsid); # used in 98_version.pm use vars qw($devcount); # Maximum device number, used for storing +use vars qw($featurelevel); use vars qw($fhem_started); # used for uptime calculation use vars qw($init_done); # use vars qw($internal_data); # FileLog/DbLog -> SVG data transport +use vars qw($lastDefChange); # number of last def/attr change use vars qw($nextat); # Time when next timer will be triggered. use vars qw($readytimeout); # Polling interval. UNIX: device search only use vars qw($reread_active); +use vars qw($selectTimestamp); # used to check last select exit timestamp use vars qw($winService); # the Windows Service object use vars qw(%attr); # Attributes use vars qw(%cmds); # Global command name hash. @@ -212,22 +218,19 @@ use vars qw(%data); # Hash for user data use vars qw(%defaultattr); # Default attributes, used by FHEM2FHEM use vars qw(%defs); # FHEM device/button definitions use vars qw(%inform); # Used by telnet_ActivateInform -use vars qw(%logInform); # Used by FHEMWEB/Event-Monitor use vars qw(%intAt); # Internal at timer hash, global for benchmark +use vars qw(%logInform); # Used by FHEMWEB/Event-Monitor use vars qw(%modules); # List of loaded modules (device/log/etc) use vars qw(%ntfyHash); # hash of devices needed to be notified. use vars qw(%oldvalue); # Old values, see commandref.html use vars qw(%readyfnlist); # devices which want a "readyfn" use vars qw(%selectlist); # devices which want a "select" use vars qw(%value); # Current values, see commandref.html -use vars qw($lastDefChange); # number of last def/attr change -use vars qw(@structChangeHist); # Contains the last 10 structural changes -use vars qw($cmdFromAnalyze); # used by the warnings-sub -use vars qw($featurelevel); -use vars qw(@authorize); # List of authorization devices use vars qw(@authenticate); # List of authentication devices -use vars qw($auth_refresh); -use vars qw($cvsid); # used in 98_version.pm +use vars qw(@authorize); # List of authorization devices +use vars qw(@structChangeHist); # Contains the last 10 structural changes + +$selectTimestamp = gettimeofday(); $cvsid = '$Id$'; my $AttrList = "verbose:0,1,2,3,4,5 room group comment:textField-long alias ". @@ -2767,7 +2770,10 @@ HandleTimeout() return undef if(!$nextat); my $now = gettimeofday(); - return ($nextat-$now) if($now < $nextat); + if($now < $nextat) { + $selectTimestamp = $now; + return ($nextat-$now); + } $now += 0.01;# need to cover min delay at least $nextat = 0; @@ -2792,9 +2798,14 @@ HandleTimeout() } } - return undef if(!$nextat); - $now = gettimeofday(); # possibly some tasks did timeout in the meantime - # we will cover them + if(!$nextat) { + $selectTimestamp = $now; + return undef; + } + + $now = gettimeofday(); # if some callbacks took longer + $selectTimestamp = $now; + return ($now+ 0.01 < $nextat) ? ($nextat-$now) : 0.01; }