2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/network/common/network-performance-server.h>
22 #include <dali/internal/system/common/performance-marker.h>
30 namespace // un-named namespace
32 const unsigned int SERVER_PORT = 3031;
33 const unsigned int MAXIMUM_PORTS_TO_TRY = 10; ///< if port in use, try up to SERVER_PORT + 10
34 const unsigned int CONNECTION_BACKLOG = 2; ///< maximum length of the queue of pending connections.
35 const unsigned int SOCKET_READ_BUFFER_SIZE = 4096;
36 typedef Vector<NetworkPerformanceClient*> ClientList;
39 * POD passed to client thread on startup
41 struct ClientThreadInfo
43 NetworkPerformanceServer* server;
44 NetworkPerformanceClient* client;
48 NetworkPerformanceServer::NetworkPerformanceServer(AdaptorInternalServices& adaptorServices,
49 const EnvironmentOptions& logOptions)
50 : mSocketFactory(adaptorServices.GetSocketFactoryInterface()),
51 mTrigger(new EventThreadCallback(MakeCallback(this, &NetworkPerformanceServer::AutomationCallback))),
52 mLogOptions(logOptions),
54 mListeningSocket(NULL),
57 mLogFunctionInstalled(false)
61 NetworkPerformanceServer::~NetworkPerformanceServer()
65 if(mLogFunctionInstalled)
67 mLogOptions.UnInstallLogFunction();
71 void NetworkPerformanceServer::Start()
73 // start a thread to listen for incoming connection requests
78 mSocketFactory.DestroySocket(mListeningSocket);
80 mListeningSocket = mSocketFactory.NewSocket(SocketInterface::TCP);
81 mListeningSocket->ReuseAddress(true);
84 unsigned int basePort = 0;
86 // try a small range of ports, so if multiple Dali apps are running you can select
87 // which one to connect to
88 while(!bound && (basePort < MAXIMUM_PORTS_TO_TRY))
90 bound = mListeningSocket->Bind(SERVER_PORT + basePort);
98 DALI_LOG_ERROR("Failed to bind to a port \n");
102 mListeningSocket->Listen(CONNECTION_BACKLOG);
104 // start a thread which will block waiting for new connections
105 int error = pthread_create(&mServerThread, NULL, ConnectionListenerFunc, this);
106 DALI_ASSERT_ALWAYS(!error && "pthread create failed");
108 Dali::Integration::Log::LogMessage(Integration::Log::INFO, "~~~ NetworkPerformanceServer started on port %d ~~~ \n", SERVER_PORT + basePort);
111 void NetworkPerformanceServer::Stop()
120 // close the server thread to prevent any new connections
121 mListeningSocket->ExitSelect();
124 // wait for the thread to exit.
126 pthread_join(mServerThread, &exitValue);
131 mListeningSocket->CloseSocket();
134 mSocketFactory.DestroySocket(mListeningSocket);
136 mListeningSocket = NULL;
138 // this will tell all client threads to quit
142 bool NetworkPerformanceServer::IsRunning() const
151 void NetworkPerformanceServer::ClientThread(NetworkPerformanceClient* client)
155 SocketInterface& socket(client->GetSocket());
159 SocketInterface::SelectReturn ret = socket.Select();
161 if(ret == SocketInterface::DATA_AVAILABLE)
164 char buffer[SOCKET_READ_BUFFER_SIZE];
165 unsigned int bytesRead;
167 bool ok = socket.Read(buffer, sizeof(buffer), bytesRead);
168 if(ok && (bytesRead > 0) && (bytesRead <= SOCKET_READ_BUFFER_SIZE))
170 client->ProcessCommand(buffer, bytesRead);
172 else // if bytesRead == 0, then client closed connection, if ok == false then an error
174 DeleteClient(client);
178 else // ret == QUIT or ERROR
180 DeleteClient(client);
186 void NetworkPerformanceServer::ConnectionListener()
188 // install Dali logging function for this thread
189 if(!mLogFunctionInstalled)
191 mLogOptions.InstallLogFunction();
192 mLogFunctionInstalled = true;
197 // this will block, waiting for a client to connect
198 // or for mListeningSocket->ExitSelect() to be called
200 SocketInterface::SelectReturn ret = mListeningSocket->Select();
202 if(ret == SocketInterface::DATA_AVAILABLE)
204 SocketInterface* clientSocket = mListeningSocket->Accept();
206 // new connection made, spawn a thread to handle it
207 pthread_t* clientThread = new pthread_t();
209 NetworkPerformanceClient* client = AddClient(clientSocket, clientThread);
211 ClientThreadInfo* info = new ClientThreadInfo;
212 info->client = client;
215 int error = pthread_create(clientThread, NULL, ClientThreadFunc, info);
216 DALI_ASSERT_ALWAYS(!error && "pthread create failed");
218 else // ret == SocketInterface::QUIT or SocketInterface::ERROR
225 void* NetworkPerformanceServer::ClientThreadFunc(void* data)
227 ClientThreadInfo* info = static_cast<ClientThreadInfo*>(data);
228 info->server->ClientThread(info->client);
233 NetworkPerformanceClient* NetworkPerformanceServer::AddClient(SocketInterface* clientSocket, pthread_t* clientThread)
235 // This function is only called from the listening thread
236 NetworkPerformanceClient* client = new NetworkPerformanceClient(clientThread,
242 // protect the mClients list which can be accessed from multiple threads.
243 Mutex::ScopedLock lock(mClientListMutex);
245 mClients.PushBack(client);
250 void NetworkPerformanceServer::DeleteClient(NetworkPerformanceClient* client)
252 // protect the mClients list while modifying
253 Mutex::ScopedLock lock(mClientListMutex);
255 // remove from the list, and delete it
256 for(ClientList::Iterator iter = mClients.Begin(); iter != mClients.End(); ++iter)
258 if((*iter) == client)
260 mClients.Erase(iter);
263 // if there server is shutting down, it waits for client count to hit zero
271 void NetworkPerformanceServer::SendData(const char* const data, unsigned int bufferSizeInBytes, unsigned int clientId)
278 // prevent clients been added / deleted while transmiting data
279 Mutex::ScopedLock lock(mClientListMutex);
281 for(ClientList::Iterator iter = mClients.Begin(); iter != mClients.End(); ++iter)
283 NetworkPerformanceClient* client = (*iter);
284 if(client->GetId() == clientId)
286 client->WriteSocket(data, bufferSizeInBytes);
292 void NetworkPerformanceServer::TransmitMarker(const PerformanceMarker& marker, const char* const description)
298 // prevent clients been added / deleted while transmiting data
299 Mutex::ScopedLock lock(mClientListMutex);
301 for(ClientList::Iterator iter = mClients.Begin(); iter != mClients.End(); ++iter)
303 NetworkPerformanceClient* client = (*iter);
304 client->TransmitMarker(marker, description);
308 void NetworkPerformanceServer::TriggerMainThreadAutomation(CallbackBase* callback)
310 // Called from client thread.
311 mClientCallback = callback;
315 void NetworkPerformanceServer::AutomationCallback()
319 Dali::CallbackBase::Execute(*mClientCallback);
323 void NetworkPerformanceServer::StopClients()
325 // prevent clients been added / deleted while stopping all clients
326 Mutex::ScopedLock lock(mClientListMutex);
328 for(ClientList::Iterator iter = mClients.Begin(); iter != mClients.End(); ++iter)
330 NetworkPerformanceClient* client = (*iter);
331 // stop the client from waiting for new commands, and exit from it's thread
332 client->ExitSelect();
336 } // namespace Adaptor
338 } // namespace Internal