File: IPCServer.cpp

package info (click to toggle)
audacity 3.7.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 134,800 kB
  • sloc: cpp: 366,277; ansic: 198,323; lisp: 7,761; sh: 3,414; python: 1,501; xml: 1,385; perl: 854; makefile: 125
file content (154 lines) | stat: -rw-r--r-- 4,404 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/**********************************************************************

  Audacity: A Digital Audio Editor

  @file IPCServer.cpp

  @author Vitaly Sverchinsky

  Part of lib-ipc library

**********************************************************************/

#include "IPCServer.h"
#include "IPCChannel.h"

#include <thread>
#include <mutex>
#include <stdexcept>

#include "internal/ipc-types.h"
#include "internal/socket_guard.h"
#include "internal/BufferedIPCChannel.h"

class IPCServer::Impl
{
   bool mTryConnect{true};
   std::mutex mSync;
   std::unique_ptr<BufferedIPCChannel> mChannel;
   std::unique_ptr<std::thread> mConnectionRoutine;
   int mConnectPort{0};

   socket_guard mListenSocket;
public:

   Impl(IPCChannelStatusCallback& callback)
   {
      mListenSocket = socket_guard { socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) };
      if(!mListenSocket)
         throw std::runtime_error("cannot create socket");
      
      sockaddr_in addrhint{};
      addrhint.sin_family = AF_INET;
      addrhint.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
      addrhint.sin_port = htons(INADDR_ANY);

      static const int yes { 1 };
      if(setsockopt(*mListenSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&yes), sizeof(yes)) == SOCKET_ERROR)
         throw std::runtime_error("cannot configure listen socket");

      if(bind(*mListenSocket, reinterpret_cast<const sockaddr*>(&addrhint), sizeof(addrhint)) == SOCKET_ERROR)
         throw std::runtime_error("socket bind error");

      if(listen(*mListenSocket, 1) == SOCKET_ERROR)
         throw std::runtime_error("socket listen error");

      sockaddr_in addr{};
      socklen_t addr_len { sizeof (addr) };
      if(getsockname(*mListenSocket, reinterpret_cast<sockaddr*>(&addr), &addr_len) == SOCKET_ERROR)
         throw std::runtime_error("failed to get socket name");

      mConnectPort = ntohs(addr.sin_port);

      mChannel = std::make_unique<BufferedIPCChannel>();
      mConnectionRoutine = std::make_unique<std::thread>([this, &callback]
      {
         socket_guard connfd;
         
         while(true)
         {
            {
               //combine flag check and internal state initialization within a single lock...

               std::lock_guard lck {mSync};
               if(!mTryConnect)
                  return;

               if(connfd)
               {
                  mListenSocket.reset();//do not need that any more
                  try
                  {
                     mChannel->StartConversation(connfd.release(), callback);
                  }
                  catch(...)
                  {
                     callback.OnConnectionError();
                  }
                  //initialization finished, we can leave now
                  break;
               }
            }

            fd_set readfds, exceptfds;
            FD_ZERO(&readfds);
            FD_ZERO(&exceptfds);
            FD_SET(*mListenSocket, &readfds);
            FD_SET(*mListenSocket, &exceptfds);

            auto ret = select(NFDS(*mListenSocket), &readfds, nullptr, &exceptfds, nullptr);
            if(ret == 1)
            {
               connfd = socket_guard { accept(*mListenSocket, nullptr, nullptr) };
               if(!connfd)
               {
                  callback.OnConnectionError();
                  break;
               }
               //connection created, finish initialization during next loop iteration under guarded section
            }
            else//SOCKET_ERROR
            {
               callback.OnConnectionError();
               break;
            }
         }
      });
   }

   int GetConnectPort() const noexcept { return mConnectPort; }

   ~Impl()
   {
      {
         std::lock_guard lck{mSync};
         mTryConnect = false;
         //this will also interrupt select in connection thread
         mListenSocket.reset();
         mChannel.reset();
      }
      if(mConnectionRoutine)
         mConnectionRoutine->join();
   }

};

IPCServer::IPCServer(IPCChannelStatusCallback& callback)
{
#ifdef _WIN32
   WSADATA wsaData;
   auto result = WSAStartup(MAKEWORD(2, 2), &wsaData);
   if (result != NO_ERROR)
      throw std::runtime_error("WSAStartup failed");
#endif
   mImpl = std::make_unique<Impl>(callback);
}

IPCServer::~IPCServer() = default;

int IPCServer::GetConnectPort() const noexcept
{
   return mImpl->GetConnectPort();
}