Audacity 3.2.0
IPCServer.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file IPCServer.cpp
6
7 @author Vitaly Sverchinsky
8
9 Part of lib-ipc library
10
11**********************************************************************/
12
13#include "IPCServer.h"
14#include "IPCChannel.h"
15
16#include <thread>
17#include <mutex>
18#include <stdexcept>
19
20#include "internal/ipc-types.h"
23
25{
26 bool mTryConnect{true};
27 std::mutex mSync;
28 std::unique_ptr<BufferedIPCChannel> mChannel;
29 std::unique_ptr<std::thread> mConnectionRoutine;
31
33public:
34
36 {
37 mListenSocket = socket_guard { socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) };
38 if(!mListenSocket)
39 throw std::runtime_error("cannot create socket");
40
41 sockaddr_in addrhint{};
42 addrhint.sin_family = AF_INET;
43 addrhint.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
44 addrhint.sin_port = htons(INADDR_ANY);
45
46 static const int yes { 1 };
47 if(setsockopt(*mListenSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&yes), sizeof(yes)) == SOCKET_ERROR)
48 throw std::runtime_error("cannot configure listen socket");
49
50 if(bind(*mListenSocket, reinterpret_cast<const sockaddr*>(&addrhint), sizeof(addrhint)) == SOCKET_ERROR)
51 throw std::runtime_error("socket bind error");
52
53 if(listen(*mListenSocket, 1) == SOCKET_ERROR)
54 throw std::runtime_error("socket listen error");
55
56 sockaddr_in addr{};
57 socklen_t addr_len { sizeof (addr) };
58 if(getsockname(*mListenSocket, reinterpret_cast<sockaddr*>(&addr), &addr_len) == SOCKET_ERROR)
59 throw std::runtime_error("failed to get socket name");
60
61 mConnectPort = ntohs(addr.sin_port);
62
63 mChannel = std::make_unique<BufferedIPCChannel>();
64 mConnectionRoutine = std::make_unique<std::thread>([this, &callback]
65 {
66 socket_guard connfd;
67
68 while(true)
69 {
70 {
71 //combine flag check and internal state initialization within a single lock...
72
73 std::lock_guard lck {mSync};
74 if(!mTryConnect)
75 return;
76
77 if(connfd)
78 {
79 mListenSocket.reset();//do not need that any more
80 try
81 {
82 mChannel->StartConversation(connfd.release(), callback);
83 }
84 catch(...)
85 {
86 callback.OnConnectionError();
87 }
88 //initialization finished, we can leave now
89 break;
90 }
91 }
92
93 fd_set readfds, exceptfds;
94 FD_ZERO(&readfds);
95 FD_ZERO(&exceptfds);
96 FD_SET(*mListenSocket, &readfds);
97 FD_SET(*mListenSocket, &exceptfds);
98
99 auto ret = select(NFDS(*mListenSocket), &readfds, nullptr, &exceptfds, nullptr);
100 if(ret == 1)
101 {
102 connfd = socket_guard { accept(*mListenSocket, nullptr, nullptr) };
103 if(!connfd)
104 {
105 callback.OnConnectionError();
106 break;
107 }
108 //connection created, finish initialization during next loop iteration under guarded section
109 }
110 else//SOCKET_ERROR
111 {
112 callback.OnConnectionError();
113 break;
114 }
115 }
116 });
117 }
118
119 int GetConnectPort() const noexcept { return mConnectPort; }
120
122 {
123 {
124 std::lock_guard lck{mSync};
125 mTryConnect = false;
126 //this will also interrupt select in connection thread
128 mChannel.reset();
129 }
131 mConnectionRoutine->join();
132 }
133
134};
135
137{
138#ifdef _WIN32
139 WSADATA wsaData;
140 auto result = WSAStartup(MAKEWORD(2, 2), &wsaData);
141 if (result != NO_ERROR)
142 throw std::runtime_error("WSAStartup failed");
143#endif
144 mImpl = std::make_unique<Impl>(callback);
145}
146
147IPCServer::~IPCServer() = default;
148
149int IPCServer::GetConnectPort() const noexcept
150{
151 return mImpl->GetConnectPort();
152}
153
154
Interface for listening connection status changes.
Definition: IPCChannel.h:37
virtual void OnConnectionError() noexcept=0
Called when connection attempt fails.
socket_guard mListenSocket
Definition: IPCServer.cpp:32
int GetConnectPort() const noexcept
Definition: IPCServer.cpp:119
std::mutex mSync
Definition: IPCServer.cpp:27
std::unique_ptr< BufferedIPCChannel > mChannel
Definition: IPCServer.cpp:28
Impl(IPCChannelStatusCallback &callback)
Definition: IPCServer.cpp:35
std::unique_ptr< std::thread > mConnectionRoutine
Definition: IPCServer.cpp:29
std::unique_ptr< Impl > mImpl
Definition: IPCServer.h:26
IPCServer(IPCChannelStatusCallback &callback)
May fail with exception or with call to IPCChannelStatusCallback::OnConnectionError....
Definition: IPCServer.cpp:136
~IPCServer()
Closes connection if any.
int GetConnectPort() const noexcept
Definition: IPCServer.cpp:149
RAII-style socket wrapper. Since socket is closed on wrapper destruction, initializing multiple guard...
Definition: socket_guard.h:24
void reset() noexcept
Closes the socket.
Definition: socket_guard.h:71
SOCKET release() noexcept
Returns the socket descriptor and releases ownership.
Definition: socket_guard.h:63
#define NFDS(x)
Definition: ipc-types.h:32
#define SOCKET_ERROR
Definition: ipc-types.h:29