]> git.vomp.tv Git - vompclient.git/commitdiff
VDR connection bug fixes
authorChris Tallon <chris@vomp.tv>
Thu, 23 Apr 2020 15:14:12 +0000 (16:14 +0100)
committerChris Tallon <chris@vomp.tv>
Thu, 23 Apr 2020 15:14:12 +0000 (16:14 +0100)
- Added util.h util.cc
- Bug fix: Reconnecting after connection lost
- Bug fix: kill -TERM handling fixed during VDP
- Bug fix: kill -TERM during VDR connecting now works

boxstack.cc
tcp.cc
tcp.h
util.cc [new file with mode: 0644]
util.h [new file with mode: 0644]
vconnect.cc
vdr.cc
vdr.h

index d72500b6c1088e2399c8a611dd66cec2aa94fd3b..eaa011018819df0475c225bc473546cf8ce51a8e 100644 (file)
@@ -489,6 +489,7 @@ void BoxStack::removeAllExceptWallpaper()
       toDel = NULL;
     }
 
+    // FIXME what is this?
     if (!videoStack.empty() && videoStack.top().first==toDel) {
        videoStack.pop();
        if (!videoStack.empty()) display=&videoStack.top().second;
@@ -499,11 +500,8 @@ void BoxStack::removeAllExceptWallpaper()
     Log::getInstance()->log("BoxStack", Log::DEBUG, "going to delete boxx %p, num=%d", toDel, numBoxes);
     if (display) Video::getInstance()->setVideoDisplay(*display);
 
-
     if (toDel) delete toDel;
   }
-
-
 }
 
 int BoxStack::handleCommand(int command)
@@ -522,7 +520,7 @@ int BoxStack::handleCommand(int command)
 
     for (i=numBoxes-1; i>=0; i--)
     {
-//      Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i);
+      // Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i);
       retVal = boxes[i]->handleCommand(command);
       if (retVal == 1)
       {
diff --git a/tcp.cc b/tcp.cc
index 0f2f248caaa5fd40f98decf3059f8d480cf7f7b0..96d315c6fc962a9c5a9f48f44a02fd2e297afcba 100644 (file)
--- a/tcp.cc
+++ b/tcp.cc
@@ -137,27 +137,42 @@ bool TCP::connect(const std::string& ip, USHORT port)
 
   // Wait for connect to complete
   fd_set writefds;
-  struct timeval tv;
   FD_ZERO(&writefds);
   FD_SET(sockfd, &writefds);
   int maxfd = sockfd;
 
+  fd_set readfds;
+  FD_ZERO(&readfds);
+
+
 #ifdef WIN32
-  FD_SET(abortSocket, &writefds);
+  FD_SET(abortSocket, &readfds);
 #else
-  FD_SET(abortPipe[0], &writefds);
+  FD_SET(abortPipe[0], &readfds);
   if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
 #endif
 
-  tv.tv_sec = 5; // Allow 5s for a connect
+  struct timeval tv;
+  tv.tv_sec = 20; // Allow 5s for a connect
   tv.tv_usec = 0;
 
-  int selectResult = select(maxfd + 1, NULL, &writefds, NULL, &tv);
+  int selectResult = select(maxfd + 1, &readfds, &writefds, NULL, &tv);
+#ifdef WIN32
+  if (FD_ISSET(abortSocket, &readfds))
+#else
+  if (FD_ISSET(abortPipe[0], &readfds))
+#endif
+  {
+    CLOSESOCKET(sockfd);
+    sockfd = -1;
+    logger->log("TCP", Log::INFO, "connect/select aborting");
+    return false;
+  }
 
   if ((selectResult == 1) || FD_ISSET(sockfd, &writefds))
   {
     freeaddrinfo(aip);
-    logger->log("TCP", Log::INFO, "connected");
+    logger->log("TCP", Log::INFO, "Connected");
     connected = true;
     return true;
   }
@@ -176,6 +191,13 @@ void TCP::shutdown()
   connected = false;
   CLOSESOCKET(sockfd);
   sockfd = -1;
+
+  char waste[10];
+#ifdef WIN32
+  while (recv(abortSocket, waste, 10, 0) > 0) ;
+#else
+  while (::read(abortPipe[0], waste, 10) > 0) ;
+#endif
 }
 
 bool TCP::status()
@@ -245,7 +267,10 @@ bool TCP::read(void* dst, ULONG numBytes)
 #ifdef WIN32
   if (FD_ISSET(abortSocket, &readfds)) { logger->log("TCP", Log::DEBUG, "aborting..."); return false; }
 #else
-  if (FD_ISSET(abortPipe[0], &readfds)) { logger->log("TCP", Log::DEBUG, "aborting..."); return false; }
+
+
+
+  if (FD_ISSET(abortPipe[0], &readfds)) { logger->log("TCP", Log::DEBUG, "B aborting..."); return false; }
 #endif
   
     int recvResult = recv(sockfd, pointer, numBytes - totalReceived, 0);
diff --git a/tcp.h b/tcp.h
index 01034bf9c2f9fe15d71535278d1b6779b757d7dc..39398f9e6caac29f0632f27d456dc5773c71b283 100644 (file)
--- a/tcp.h
+++ b/tcp.h
@@ -40,7 +40,7 @@ class TCP
   public:
     ~TCP();
     bool init(); // Must call this first, once on any new TCP object
-    void shutdown(); // Optional. Closes connection. Can call connect() next
+    void shutdown(); // Optional if this is to be deleted. Closes connection. Can call connect() next
     void abortCall(); // causes a read/connect call to immediately abort
 
     bool connect(const std::string& ip, USHORT port);
diff --git a/util.cc b/util.cc
new file mode 100644 (file)
index 0000000..f46afdf
--- /dev/null
+++ b/util.cc
@@ -0,0 +1,91 @@
+/*
+    Copyright 2020 Chris Tallon
+
+    This file is part of VOMP.
+
+    VOMP is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    VOMP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <time.h>
+#include <iomanip>
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include "util.h"
+
+void MILLISLEEP(ULONG a)
+{
+#ifndef WIN32
+  struct timespec delayTime;
+  delayTime.tv_sec = a / 1000;
+  delayTime.tv_nsec = (a % 1000) * 1000000;
+  nanosleep(&delayTime, NULL);
+#else
+  Sleep(a);
+#endif
+}
+
+std::string tp2str(const std::chrono::time_point<std::chrono::system_clock>& tp)
+{
+  auto tms = std::chrono::time_point_cast<std::chrono::milliseconds>(tp);
+  std::chrono::milliseconds e = tms.time_since_epoch();
+  long long c = e.count();
+  time_t tt = c / 1000;
+  int ttm = c % 1000;
+  auto stm = std::localtime(&tt);
+  std::stringstream ss;
+  ss << std::put_time(stm, "%T") << "." << std::setfill('0') << std::setw(3) << ttm;
+  return ss.str();
+}
+
+/*
+ULLONG htonll(ULLONG a)
+{
+  return (((ULLONG)htonl((ULONG)((a<<32)>> 32))<<32)
+    |(ULONG)htonl(((ULONG) (a >> 32))));
+}
+
+ULLONG ntohll(ULLONG a)
+{
+  return htonll(a);
+}
+
+ULLONG htonll(ULLONG a)
+{
+  #if BYTE_ORDER == BIG_ENDIAN
+    return a;
+  #else
+    ULLONG b = 0;
+
+    b = ((a << 56) & 0xFF00000000000000ULL)
+      | ((a << 40) & 0x00FF000000000000ULL)
+      | ((a << 24) & 0x0000FF0000000000ULL)
+      | ((a <<  8) & 0x000000FF00000000ULL)
+      | ((a >>  8) & 0x00000000FF000000ULL)
+      | ((a >> 24) & 0x0000000000FF0000ULL)
+      | ((a >> 40) & 0x000000000000FF00ULL)
+      | ((a >> 56) & 0x00000000000000FFULL) ;
+
+    return b;
+  #endif
+}
+
+ULLONG ntohll(ULLONG a)
+{
+  return htonll(a);
+}
+
+*/
diff --git a/util.h b/util.h
new file mode 100644 (file)
index 0000000..6701048
--- /dev/null
+++ b/util.h
@@ -0,0 +1,35 @@
+/*
+    Copyright 2020 Chris Tallon
+
+    This file is part of VOMP.
+
+    VOMP is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    VOMP is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <string>
+#include <chrono>
+
+#include "defines.h"
+
+void MILLISLEEP(ULONG a);
+std::string tp2str(const std::chrono::time_point<std::chrono::system_clock>& tp);
+
+
+//ULLONG htonll(ULLONG a);
+//ULLONG ntohll(ULLONG a);
+
+#endif
index b9fe4d852732ec44ca12865aeb203ea89b16b491..c140dfe39f0cacb80e0f6973d43286bc492d2c91 100644 (file)
@@ -88,6 +88,7 @@ void VConnect::stop()
 {
   threadMutex.lock();
   threadReqQuit = true;
+  vdr->abortConnect(); // If this and vdr are connecting, cancel it
   threadCond.notify_one();
   threadMutex.unlock();
   connectThread.join();
@@ -158,7 +159,10 @@ void VConnect::threadMethod()
     draw();
     boxstack->update(this);
 
+    if (threadReqQuit) return;
     success = vdr->connect();
+    if (threadReqQuit) return;
+
     if (success)
     {
       logger->log("VConnect", Log::DEBUG, "Connected ok, doing login");
@@ -188,7 +192,9 @@ void VConnect::threadMethod()
 
     draw();
     boxstack->update(this);
-    MILLISLEEP(delay);
+
+    MILLISLEEP(delay); // FIXME wait on cond?
+    if (threadReqQuit) return;
   } while(!success);
 
   logger->log("VConnect", Log::INFO, "Send VDR connected message");
diff --git a/vdr.cc b/vdr.cc
index 632a6b8eb26360c5c020bf8b0cf039e57795c574..e75681433993f18caa99b0c88a87b560cf43d707 100644 (file)
--- a/vdr.cc
+++ b/vdr.cc
@@ -161,31 +161,71 @@ void VDR::setServerPort(USHORT newPort)
   serverPort = newPort;
 }
 
+
 int VDR::connect()
 {
   maxChannelNumber = 0;
   channelNumberWidth = 1;
 
-  tcp.shutdown();
-  if (!tcp.connect(serverIP, serverPort)) return 0;
+  connectStateMutex.lock();
+  if (connecting || connected) return 0;
+  connecting = true;
+  connectStateMutex.unlock(); // now I have connecting
+
+  bool connectResult = tcp.connect(serverIP, serverPort);
 
-  connected = true;
+  connectStateMutex.lock(); // connected = false, connecting = true, babortConnect = ?
+  connecting = false;
 
-  threadStartProtect.lock();
-  vdrThread = std::thread( [this]
+  if (babortConnect)
   {
+    if (connectResult) tcp.shutdown();
+    babortConnect = false;
+    connectStateMutex.unlock();
+    return 0;
+  }
+
+  if (connectResult)
+  {
+    connected = true;
+
     threadStartProtect.lock();
+    vdrThread = std::thread( [this]
+    {
+      threadStartProtect.lock();
+      threadStartProtect.unlock();
+      threadMethod();
+    });
     threadStartProtect.unlock();
-    threadMethod();
-  });
-  threadStartProtect.unlock();
 
-  return 1;
+    connectStateMutex.unlock();
+    return 1;
+  }
+  else
+  {
+    connectStateMutex.unlock();
+    return 0;
+  }
+}
+
+void VDR::abortConnect() // If there is one, force a running connect call to abort
+{
+  connectStateMutex.lock();
+
+  if (connecting)
+  {
+    babortConnect = true;
+    tcp.abortCall();
+  }
+
+  connectStateMutex.unlock();
 }
 
 void VDR::disconnect()
 {
-  logger->log("VDR", Log::DEBUG, "Disconnect start");
+  std::lock_guard<std::mutex> lg(connectStateMutex);
+
+  if (!connected) return;
 
   if (vdrThread.joinable())
   {
@@ -193,9 +233,11 @@ void VDR::disconnect()
     tcp.abortCall();
     vdrThread.join();
     threadReqStop = false;
-    logger->log("VDR", Log::DEBUG, "done thread stop");
   }
 
+  tcp.shutdown();
+
+  disconnecting = false;
   connected = false;
   logger->log("VDR", Log::DEBUG, "Disconnect");
 }
@@ -229,21 +271,15 @@ void VDR::threadMethod()
 
   while(1)
   {
-    if (threadReqStop) return;
-
-    timeNow = time(NULL);
-    
     readSuccess = tcp.read(&channelID, sizeof(ULONG));
-
     if (threadReqStop) return;
+    timeNow = time(NULL);
 
     if (!readSuccess)
     {
       //logger->log("VDR", Log::DEBUG, "Net read timeout");
       if (!tcp.status()) { connectionDied(); return; } // return to stop this thread
     }
-      
-    // Error or timeout.
 
     if (!lastKAsent) // have not sent a KA
     {
@@ -392,7 +428,7 @@ void VDR::connectionDied()
 {
   // Called from within threadMethod to do cleanup if it decides the connection has died
 
-  connected = false; // though actually it could still be connected until someone calls vdr->disconnect
+  disconnecting = true;
 
   // Need to wake up any waiting channel 1 request-response threads
   // Normally this is done by a packet coming in with channelid and requestid      
@@ -482,9 +518,9 @@ VDR_ResponsePacket* VDR::RequestResponse(VDR_RequestPacket* vrp)
 {
   //logger->log("VDR", Log::DEBUG, "RR %lu", vrp->getOpcode());
 
-  if (!connected)
+  if (!connected || disconnecting)
   {
-    logger->log("VDR", Log::DEBUG, "RR when !connected");
+    logger->log("VDR", Log::DEBUG, "RR when !connected || disconnecting");
     VDR_ResponsePacket* vresp = new VDR_ResponsePacket();
     return vresp; // "no-response" return
   }
@@ -684,7 +720,7 @@ int VDR::doLogin(unsigned int* v_server_min, unsigned int* v_server_max, unsigne
 
 bool VDR::LogExtern(const char* logString)
 {
-  if (!connected) return false;
+  if (!connected || disconnecting) return false;
   int stringLength = strlen(logString);
   int packetLength = stringLength + 8;
   char *buffer=new char[packetLength + 1];
@@ -704,8 +740,9 @@ bool VDR::LogExtern(const char* logString)
   
   if (!tcp.write(buffer, packetLength))
   {
-    connected = false; // stop the rest of the connection  
     delete [] buffer;
+    disconnecting = true; // stop the rest of the connection
+    Control::getInstance()->connectionLost();
     return false;
   }
   delete [] buffer;
@@ -1580,7 +1617,7 @@ void VDR::shutdownVDR()
   else
     logger->log("VDR", Log::DEBUG, "Shutting down vomp only");
 
-  if(!doVDRShutdown || !connected)
+  if(!doVDRShutdown || !connected || disconnecting)
     return;
 
   VDR_RequestPacket vrp;
diff --git a/vdr.h b/vdr.h
index ad00955af3d091412af439443bf0a46b57221b2c..b7bd161738b28a7b57d8d58571d3f73193c3d70b 100644 (file)
--- a/vdr.h
+++ b/vdr.h
@@ -129,6 +129,7 @@ public ExternLogger
     void setReceiveWindow(size_t size);
     int connect();
     void disconnect();
+    void abortConnect(); // If there is one, force a running connect call to abort
     bool isConnected() { return connected; }
     int getChannelNumberWidth() { return channelNumberWidth; }
 
@@ -234,12 +235,17 @@ public ExternLogger
     TCP tcp;
     char serverIP[40];
     USHORT serverPort;
-    bool connected{};
     ULONG maxChannelNumber{};
     bool doVDRShutdown{};
     int channelNumberWidth{1};
     VDR_PacketReceiver* TEMP_SINGLE_VDR_PR;
 
+    std::mutex connectStateMutex; // FIXME improve this
+    bool connecting{};
+    bool disconnecting{};
+    bool babortConnect{};
+    bool connected{};
+
     std::mutex threadStartProtect;
     std::thread vdrThread;
     bool threadReqStop{};