2 * Copyright (c) 2018 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>
23 #include <dali/internal/system/common/performance-marker.h>
34 namespace // un-named namespace
36 const unsigned int SERVER_PORT = 3031;
37 const unsigned int MAXIMUM_PORTS_TO_TRY = 10; ///< if port in use, try up to SERVER_PORT + 10
38 const unsigned int CONNECTION_BACKLOG = 2; ///< maximum length of the queue of pending connections.
39 const unsigned int SOCKET_READ_BUFFER_SIZE = 4096;
40 typedef Vector< NetworkPerformanceClient*> ClientList;
43 * POD passed to client thread on startup
45 struct ClientThreadInfo
47 NetworkPerformanceServer* server;
48 NetworkPerformanceClient* client;
52 NetworkPerformanceServer::NetworkPerformanceServer( AdaptorInternalServices& adaptorServices,
53 const EnvironmentOptions& logOptions )
54 : mSocketFactory( adaptorServices.GetSocketFactoryInterface() ),
55 mLogOptions( logOptions ),
57 mListeningSocket( NULL ),
60 mLogFunctionInstalled( false )
64 NetworkPerformanceServer::~NetworkPerformanceServer()
68 if( mLogFunctionInstalled )
70 mLogOptions.UnInstallLogFunction();
74 void NetworkPerformanceServer::Start()
76 // start a thread to listen for incoming connection requests
79 if( mListeningSocket )
81 mSocketFactory.DestroySocket( mListeningSocket );
83 mListeningSocket = mSocketFactory.NewSocket( SocketInterface::TCP);
84 mListeningSocket->ReuseAddress( true );
87 unsigned int basePort = 0;
89 // try a small range of ports, so if multiple Dali apps are running you can select
90 // which one to connect to
91 while( !bound && ( basePort < MAXIMUM_PORTS_TO_TRY ))
93 bound = mListeningSocket->Bind( SERVER_PORT + basePort );
101 DALI_LOG_ERROR("Failed to bind to a port \n");
105 mListeningSocket->Listen( CONNECTION_BACKLOG );
107 // start a thread which will block waiting for new connections
108 int error = pthread_create( &mServerThread, NULL, ConnectionListenerFunc, this );
109 DALI_ASSERT_ALWAYS( !error && "pthread create failed" );
111 Dali::Integration::Log::LogMessage(Integration::Log::DebugInfo, "~~~ NetworkPerformanceServer started on port %d ~~~ \n", SERVER_PORT + basePort);
115 void NetworkPerformanceServer::Stop()
122 if( mListeningSocket )
124 // close the server thread to prevent any new connections
125 mListeningSocket->ExitSelect();
128 // wait for the thread to exit.
130 pthread_join( mServerThread, &exitValue );
132 if( mListeningSocket )
135 mListeningSocket->CloseSocket();
138 mSocketFactory.DestroySocket( mListeningSocket );
140 mListeningSocket = NULL;
142 // this will tell all client threads to quit
147 bool NetworkPerformanceServer::IsRunning() const
156 void NetworkPerformanceServer::ClientThread( NetworkPerformanceClient* client )
160 SocketInterface& socket( client->GetSocket() );
164 SocketInterface::SelectReturn ret = socket.Select();
166 if( ret == SocketInterface::DATA_AVAILABLE )
169 char buffer[ SOCKET_READ_BUFFER_SIZE ];
170 unsigned int bytesRead;
172 bool ok = socket.Read( buffer, sizeof( buffer ) , bytesRead);
173 if( ok && ( bytesRead > 0) )
175 client->ProcessCommand( buffer, bytesRead );
177 else // if bytesRead == 0, then client closed connection, if ok == false then an error
179 DeleteClient( client );
183 else // ret == QUIT or ERROR
185 DeleteClient( client);
191 void NetworkPerformanceServer::ConnectionListener()
193 // install Dali logging function for this thread
194 if( !mLogFunctionInstalled )
196 mLogOptions.InstallLogFunction();
197 mLogFunctionInstalled = true;
202 // this will block, waiting for a client to connect
203 // or for mListeningSocket->ExitSelect() to be called
205 SocketInterface::SelectReturn ret = mListeningSocket->Select();
207 if( ret == SocketInterface::DATA_AVAILABLE )
209 SocketInterface* clientSocket = mListeningSocket->Accept();
211 // new connection made, spawn a thread to handle it
212 pthread_t* clientThread = new pthread_t();
214 NetworkPerformanceClient* client = AddClient( clientSocket, clientThread );
216 ClientThreadInfo* info = new ClientThreadInfo;
217 info->client = client;
220 int error = pthread_create( clientThread, NULL, ClientThreadFunc, info );
221 DALI_ASSERT_ALWAYS( !error && "pthread create failed" );
224 else // ret == SocketInterface::QUIT or SocketInterface::ERROR
231 void* NetworkPerformanceServer::ClientThreadFunc( void* data )
233 ClientThreadInfo* info = static_cast<ClientThreadInfo*>( data );
234 info->server->ClientThread( info->client );
239 NetworkPerformanceClient* NetworkPerformanceServer::AddClient( SocketInterface* clientSocket, pthread_t* clientThread )
241 // This function is only called from the listening thread
242 NetworkPerformanceClient* client= new NetworkPerformanceClient( clientThread,
248 // protect the mClients list which can be accessed from multiple threads.
249 Mutex::ScopedLock lock( mClientListMutex );
251 mClients.PushBack( client );
256 void NetworkPerformanceServer::DeleteClient( NetworkPerformanceClient* client )
258 // protect the mClients list while modifying
259 Mutex::ScopedLock lock( mClientListMutex );
261 // remove from the list, and delete it
262 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
264 if( (*iter) == client )
266 mClients.Erase( iter );
269 // if there server is shutting down, it waits for client count to hit zero
277 void NetworkPerformanceServer::SendData( const char* const data, unsigned int bufferSizeInBytes,unsigned int clientId )
284 // prevent clients been added / deleted while transmiting data
285 Mutex::ScopedLock lock( mClientListMutex );
287 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
289 NetworkPerformanceClient* client = (*iter);
290 if( client->GetId() == clientId )
292 client->WriteSocket(data ,bufferSizeInBytes);
298 void NetworkPerformanceServer::TransmitMarker( const PerformanceMarker& marker, const char* const description )
304 // prevent clients been added / deleted while transmiting data
305 Mutex::ScopedLock lock( mClientListMutex );
307 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
309 NetworkPerformanceClient* client = (*iter);
310 client->TransmitMarker( marker, description );
315 void NetworkPerformanceServer::StopClients()
317 // prevent clients been added / deleted while stopping all clients
318 Mutex::ScopedLock lock( mClientListMutex );
320 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
322 NetworkPerformanceClient* client = (*iter);
323 // stop the client from waiting for new commands, and exit from it's thread
324 client->ExitSelect();
328 } // namespace Internal
330 } // namespace Adaptor