1 /*-------------------------------------------------------------------------
2 * Vulkan CTS Framework
3 * --------------------
4 *
5 * Copyright (c) 2021 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *-------------------------------------------------------------------------*/
20
21 #include "vksIPC.hpp"
22 #include "vksStore.hpp"
23 #include "vksClient.hpp"
24
25 #include <future>
26
27 namespace vksc_server
28 {
29
30 namespace ipc
31 {
32
33 constexpr int DefaultPortIPC = 57323;
34
35 struct ChildConnection
36 {
37 int id;
38 std::unique_ptr<de::Socket> socket;
39 std::atomic<bool> &appactive;
40 vector<u8> recvb;
41 };
42
43 struct PacketsLoop
44 {
45 ChildConnection client;
46 Store &fileStore;
47
48 template <typename T>
SendResponsevksc_server::ipc::PacketsLoop49 static void SendResponse(ChildConnection &c, T &data)
50 {
51 SendPayloadWithHeader(c.socket.get(), T::Type(), Serialize(data));
52 }
53
ProcessPacketsOnServervksc_server::ipc::PacketsLoop54 void ProcessPacketsOnServer(ChildConnection &c, u32 type, vector<u8> packet)
55 {
56 switch (type)
57 {
58 case StoreContentRequest::Type():
59 {
60 auto req = Deserialize<StoreContentRequest>(packet);
61 bool ok = fileStore.Set(req.name, req.data);
62
63 StoreContentResponse res;
64 res.status = ok;
65 SendResponse(c, res);
66 }
67 break;
68 case GetContentRequest::Type():
69 {
70 auto req = Deserialize<GetContentRequest>(packet);
71
72 vector<u8> content;
73 bool ok = fileStore.Get(req.path, content, req.removeAfter);
74
75 GetContentResponse res;
76 res.status = ok;
77 res.data = std::move(content);
78 SendResponse(c, res);
79 }
80 break;
81
82 default:
83 throw std::runtime_error("ipc communication error");
84 }
85 }
86
Loopvksc_server::ipc::PacketsLoop87 void Loop()
88 {
89 while (client.socket->isConnected() && client.appactive)
90 {
91 RecvSome(client.socket.get(), client.recvb);
92 auto interpret = [this](u32 type, vector<u8> packet)
93 { ProcessPacketsOnServer(client, type, std::move(packet)); };
94 while (ProccessNetworkData(client.recvb, interpret))
95 {
96 }
97 }
98 }
99
operator ()vksc_server::ipc::PacketsLoop100 void operator()()
101 {
102 try
103 {
104 Loop();
105 }
106 catch (const std::exception &)
107 {
108 client.socket->close();
109 }
110 }
111 };
112
113 struct ParentImpl
114 {
115 Store fileStore;
116 int m_portOffset;
117 std::thread listenerLoop;
118 std::atomic<bool> appActive{true};
119
ParentLoopvksc_server::ipc::ParentImpl120 void ParentLoop()
121 {
122 vector<std::future<void>> clients;
123 int id{};
124
125 try
126 {
127 de::SocketAddress addr;
128 addr.setHost("localhost");
129 addr.setPort(DefaultPortIPC + m_portOffset);
130 de::Socket listener;
131 listener.listen(addr);
132
133 while (appActive)
134 {
135 remove_erase_if(clients, [](const std::future<void> &c) { return is_ready(c); });
136 ChildConnection client{++id, std::unique_ptr<de::Socket>(listener.accept()), appActive, vector<u8>{}};
137 clients.push_back(CreateClientThread(std::move(client)));
138 }
139 }
140 catch (const std::exception &)
141 {
142 appActive = false;
143 }
144 }
145
__anon525feff80302vksc_server::ipc::ParentImpl146 ParentImpl(const int portOffset) : m_portOffset{portOffset}, listenerLoop{[this]() { ParentLoop(); }}
147 {
148 }
149
~ParentImplvksc_server::ipc::ParentImpl150 ~ParentImpl()
151 {
152 appActive = false;
153
154 // Dummy connection to trigger accept()
155 de::SocketAddress addr;
156 addr.setHost("localhost");
157 addr.setPort(DefaultPortIPC + m_portOffset);
158 de::Socket socket;
159
160 try
161 {
162 socket.connect(addr);
163 }
164 catch (const de::SocketError &)
165 {
166 }
167
168 try
169 {
170 socket.close();
171 }
172 catch (const de::SocketError &)
173 {
174 }
175
176 listenerLoop.join();
177 }
178
CreateClientThreadvksc_server::ipc::ParentImpl179 std::future<void> CreateClientThread(ChildConnection client)
180 {
181 return std::async(PacketsLoop{std::move(client), fileStore});
182 }
183 };
184
Parent(const int portOffset)185 Parent::Parent(const int portOffset)
186 {
187 impl.reset(new ParentImpl(portOffset));
188 }
189
~Parent()190 Parent::~Parent()
191 {
192 }
193
SetFile(const string & name,const std::vector<u8> & content)194 bool Parent::SetFile(const string &name, const std::vector<u8> &content)
195 {
196 return impl->fileStore.Set(name, content);
197 }
198
GetFile(const string & name)199 vector<u8> Parent::GetFile(const string &name)
200 {
201 vector<u8> content;
202 bool result = impl->fileStore.Get(name, content, false);
203 if (result)
204 return content;
205 else
206 return {};
207 }
208
209 struct ChildImpl
210 {
ChildImplvksc_server::ipc::ChildImpl211 ChildImpl(const int portOffset) : m_portOffset(portOffset)
212 {
213 connection.reset(new Server{"localhost:" + std::to_string(DefaultPortIPC + m_portOffset)});
214 }
215
216 int m_portOffset;
217 std::unique_ptr<Server> connection;
218 };
219
Child(const int portOffset)220 Child::Child(const int portOffset)
221 {
222 impl.reset(new ChildImpl(portOffset));
223 }
224
~Child()225 Child::~Child()
226 {
227 }
228
SetFile(const string & name,const std::vector<u8> & content)229 bool Child::SetFile(const string &name, const std::vector<u8> &content)
230 {
231 StoreContentRequest request;
232 request.name = name;
233 request.data = content;
234 StoreContentResponse response;
235 impl->connection->SendRequest(request, response);
236 return response.status;
237 }
238
GetFile(const string & name)239 std::vector<u8> Child::GetFile(const string &name)
240 {
241 GetContentRequest request;
242 request.path = name;
243 request.physicalFile = false;
244 request.removeAfter = false;
245 GetContentResponse response;
246 impl->connection->SendRequest(request, response);
247 if (response.status == true)
248 return response.data;
249 else
250 return {};
251 }
252
253 } // namespace ipc
254
255 } // namespace vksc_server
256