From 843b4873d0d6ea75aaddfd6c0d9fd352548a8115 Mon Sep 17 00:00:00 2001 From: markusbloch Date: Thu, 14 Feb 2013 19:51:11 +0000 Subject: [PATCH] fixing 2 race conditions where a device is summarized as present instead of absent git-svn-id: https://svn.fhem.de/fhem/trunk@2726 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/PRESENCE/collectord | 53 +++++++++++++++---- fhem/contrib/PRESENCE/deb/collectord-1.0.deb | Bin 8462 -> 8124 bytes 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/fhem/contrib/PRESENCE/collectord b/fhem/contrib/PRESENCE/collectord index d1a58c5a7..dab2b82d7 100755 --- a/fhem/contrib/PRESENCE/collectord +++ b/fhem/contrib/PRESENCE/collectord @@ -36,7 +36,7 @@ use File::Basename; use Getopt::Long; use threads; use Thread::Queue; - +use Time::HiRes qw(gettimeofday); use warnings; use strict; @@ -241,6 +241,11 @@ while(1) if(not $value =~ /^(absence|present)$/) { $handle{$uuid}{client}->send("$value;$room\n"); + + if($value eq "socket_closed") + { + delete($state{$uuid}{rooms}{$room}); + } } else { @@ -503,11 +508,12 @@ sub doQuery($$$) my $selector; my @client_handle; my $reconnect_count = 0; - + my $last_contact = gettimeofday(); my $previous_state = "absence"; - + my $current_state = "absence"; + my $client_socket = new IO::Socket::INET ( PeerHost => $values{address}, PeerPort => $values{port}, @@ -515,7 +521,7 @@ sub doQuery($$$) Type => SOCK_STREAM, KeepAlive => 1, Blocking => 1 - ) or ( $log_queue->enqueue("$room: could not create socket to ".$values{address}." - $! - \n") and threads->exit()); + ) or ( $log_queue->enqueue("$room: could not create socket to ".$values{address}." - $! - \n")); # if the thread gets a termination signal, the thread must be shutdown by itself local $SIG{TERM} = sub { @@ -525,20 +531,36 @@ sub doQuery($$$) threads->exit(); }; + $selector = IO::Select->new($client_socket); if($client_socket) { + # send the given address to the presence daemon $client_socket->send($do_address."|".$values{absence_timeout}."\n"); } - - # create a socket selector for non-blocking read and disconnect - $selector = IO::Select->new($client_socket); - + else + { + $selector->remove($client_socket); + $client_socket = undef; + + } + # thread main loop while(1) { + + if(defined($client_socket) and not $last_contact > (gettimeofday() - ($current_state eq "absence" ? $values{absence_timeout} : $values{presence_timeout}) - 60)) + { + $log_queue->enqueue("$room: socket to ".$values{address}.":".$values{port}." did not report anything in expected time, resetting socket\n"); + + $selector->remove($client_socket); + close($client_socket); + $client_socket = undef; + } + + if(not defined($client_socket)) { # if it's the first occurance @@ -570,6 +592,9 @@ sub doQuery($$$) # reset the reconnect counter $reconnect_count = 0; + # set the last contact date to now + $last_contact = gettimeofday(); + # add the new established socket to the IO selector for incoming data monitoring. $selector->add($client_socket); # send the given address to the presence daemon @@ -580,18 +605,24 @@ sub doQuery($$$) sleep(9); } } + # if the socket has a message available if(@client_handle = $selector->can_read(1)) { + # get all socket handles which has a message available foreach my $local_client (@client_handle) { # get the message from the socket handle $return = <$local_client>; - + # if the message is defined (not EOF) handle the message... if($return) { + + # set the last contact date + $last_contact = gettimeofday(); + # remove trailing whitespaces and newlines chomp($return); @@ -616,6 +647,8 @@ sub doQuery($$$) # log the timout change to the log queue $log_queue->enqueue("$room: changing to absence timeout (".$values{absence_timeout}.") for device $do_address\n"); + $current_state = "absence"; + # send the new command with the configured absence timeout $local_client->send($do_address."|".$values{absence_timeout}."\n"); } @@ -623,6 +656,8 @@ sub doQuery($$$) { $log_queue->enqueue("$room: changing to presence timeout (".$values{presence_timeout}.") for device $do_address\n"); + $current_state = "present"; + # if the state changes from absence to present, set the presence timeout $local_client->send($do_address."|".$values{presence_timeout}."\n"); } diff --git a/fhem/contrib/PRESENCE/deb/collectord-1.0.deb b/fhem/contrib/PRESENCE/deb/collectord-1.0.deb index 024c14794eb4edcd780eb417b1fc681b5ee3abe9..b891414a106d6072547faa93a74409b12597dde2 100644 GIT binary patch delta 7401 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 b#r^O8cmKQp-T!{qzyAX!L2hCIcmMzj)+=-X delta 7742 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 E3Tt&7d;kCd