2 * Copyright (c) 2022 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 mLogOptions(logOptions),
53 mListeningSocket(NULL),
56 mLogFunctionInstalled(false)
60 NetworkPerformanceServer::~NetworkPerformanceServer()
64 if(mLogFunctionInstalled)
66 mLogOptions.UnInstallLogFunction();
70 void NetworkPerformanceServer::Start()
72 // start a thread to listen for incoming connection requests
77 mSocketFactory.DestroySocket(mListeningSocket);
79 mListeningSocket = mSocketFactory.NewSocket(SocketInterface::TCP);
80 mListeningSocket->ReuseAddress(true);
83 unsigned int basePort = 0;
85 // try a small range of ports, so if multiple Dali apps are running you can select
86 // which one to connect to
87 while(!bound && (basePort < MAXIMUM_PORTS_TO_TRY))
89 bound = mListeningSocket->Bind(SERVER_PORT + basePort);
97 DALI_LOG_ERROR("Failed to bind to a port \n");
101 mListeningSocket->Listen(CONNECTION_BACKLOG);
103 // start a thread which will block waiting for new connections
104 int error = pthread_create(&mServerThread, NULL, ConnectionListenerFunc, this);
105 DALI_ASSERT_ALWAYS(!error && "pthread create failed");
107 Dali::Integration::Log::LogMessage(Integration::Log::INFO, "~~~ NetworkPerformanceServer started on port %d ~~~ \n", SERVER_PORT + basePort);
110 void NetworkPerformanceServer::Stop()
119 // close the server thread to prevent any new connections
120 mListeningSocket->ExitSelect();
123 // wait for the thread to exit.
125 pthread_join(mServerThread, &exitValue);
130 mListeningSocket->CloseSocket();
133 mSocketFactory.DestroySocket(mListeningSocket);
135 mListeningSocket = NULL;
137 // this will tell all client threads to quit
141 bool NetworkPerformanceServer::IsRunning() const
150 void NetworkPerformanceServer::ClientThread(NetworkPerformanceClient* client)
154 SocketInterface& socket(client->GetSocket());
158 SocketInterface::SelectReturn ret = socket.Select();
160 if(ret == SocketInterface::DATA_AVAILABLE)
163 char buffer[SOCKET_READ_BUFFER_SIZE];
164 unsigned int bytesRead;
166 bool ok = socket.Read(buffer, sizeof(buffer), bytesRead);
167 if(ok && (bytesRead > 0) && (bytesRead <= SOCKET_READ_BUFFER_SIZE))
169 client->ProcessCommand(buffer, bytesRead);
171 else // if bytesRead == 0, then client closed connection, if ok == false then an error
173 DeleteClient(client);
177 else // ret == QUIT or ERROR
179 DeleteClient(client);
185 void NetworkPerformanceServer::ConnectionListener()
187 // install Dali logging function for this thread
188 if(!mLogFunctionInstalled)
190 mLogOptions.InstallLogFunction();
191 mLogFunctionInstalled = true;
196 // this will block, waiting for a client to connect
197 // or for mListeningSocket->ExitSelect() to be called
199 SocketInterface::SelectReturn ret = mListeningSocket->Select();
201 if(ret == SocketInterface::DATA_AVAILABLE)
203 SocketInterface* clientSocket = mListeningSocket->Accept();
205 // new connection made, spawn a thread to handle it
206 pthread_t* clientThread = new pthread_t();
208 NetworkPerformanceClient* client = AddClient(clientSocket, clientThread);
210 ClientThreadInfo* info = new ClientThreadInfo;
211 info->client = client;
214 int error = pthread_create(clientThread, NULL, ClientThreadFunc, info);
215 DALI_ASSERT_ALWAYS(!error && "pthread create failed");
217 else // ret == SocketInterface::QUIT or SocketInterface::ERROR
224 void* NetworkPerformanceServer::ClientThreadFunc(void* data)
226 ClientThreadInfo* info = static_cast<ClientThreadInfo*>(data);
227 info->server->ClientThread(info->client);
232 NetworkPerformanceClient* NetworkPerformanceServer::AddClient(SocketInterface* clientSocket, pthread_t* clientThread)
234 // This function is only called from the listening thread
235 NetworkPerformanceClient* client = new NetworkPerformanceClient(clientThread,
241 // protect the mClients list which can be accessed from multiple threads.
242 Mutex::ScopedLock lock(mClientListMutex);
244 mClients.PushBack(client);
249 void NetworkPerformanceServer::DeleteClient(NetworkPerformanceClient* client)
251 // protect the mClients list while modifying
252 Mutex::ScopedLock lock(mClientListMutex);
254 // remove from the list, and delete it
255 for(ClientList::Iterator iter = mClients.Begin(); iter != mClients.End(); ++iter)
257 if((*iter) == client)
259 mClients.Erase(iter);
262 // if there server is shutting down, it waits for client count to hit zero
270 void NetworkPerformanceServer::SendData(const char* const data, unsigned int bufferSizeInBytes, unsigned int clientId)
277 // prevent clients been added / deleted while transmiting data
278 Mutex::ScopedLock lock(mClientListMutex);
280 for(ClientList::Iterator iter = mClients.Begin(); iter != mClients.End(); ++iter)
282 NetworkPerformanceClient* client = (*iter);
283 if(client->GetId() == clientId)
285 client->WriteSocket(data, bufferSizeInBytes);
291 void NetworkPerformanceServer::TransmitMarker(const PerformanceMarker& marker, const char* const description)
297 // prevent clients been added / deleted while transmiting data
298 Mutex::ScopedLock lock(mClientListMutex);
300 for(ClientList::Iterator iter = mClients.Begin(); iter != mClients.End(); ++iter)
302 NetworkPerformanceClient* client = (*iter);
303 client->TransmitMarker(marker, description);
307 void NetworkPerformanceServer::StopClients()
309 // prevent clients been added / deleted while stopping all clients
310 Mutex::ScopedLock lock(mClientListMutex);
312 for(ClientList::Iterator iter = mClients.Begin(); iter != mClients.End(); ++iter)
314 NetworkPerformanceClient* client = (*iter);
315 // stop the client from waiting for new commands, and exit from it's thread
316 client->ExitSelect();
320 } // namespace Adaptor
322 } // namespace Internal