2 * Copyright (c) 2015 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 "network-performance-server.h"
23 #include <base/performance-logging/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 : mTriggerEventFactory( adaptorServices.GetTriggerEventFactoryInterface() ),
55 mSocketFactory( adaptorServices.GetSocketFactoryInterface() ),
56 mLogOptions( logOptions ),
58 mListeningSocket( NULL ),
61 mLogFunctionInstalled( false )
65 NetworkPerformanceServer::~NetworkPerformanceServer()
69 if( mLogFunctionInstalled )
71 mLogOptions.UnInstallLogFunction();
75 void NetworkPerformanceServer::Start()
77 // start a thread to listen for incoming connection requests
80 if( mListeningSocket )
82 mSocketFactory.DestroySocket( mListeningSocket );
84 mListeningSocket = mSocketFactory.NewSocket( SocketInterface::TCP);
85 mListeningSocket->ReuseAddress( true );
88 unsigned int basePort = 0;
90 // try a small range of ports, so if multiple Dali apps are running you can select
91 // which one to connect to
92 while( !bound && ( basePort < MAXIMUM_PORTS_TO_TRY ))
94 bound = mListeningSocket->Bind( SERVER_PORT + basePort );
102 DALI_LOG_ERROR("Failed to bind to a port \n");
106 mListeningSocket->Listen( CONNECTION_BACKLOG );
108 // start a thread which will block waiting for new connections
109 int error = pthread_create( &mServerThread, NULL, ConnectionListenerFunc, this );
110 DALI_ASSERT_ALWAYS( !error && "pthread create failed" );
112 Dali::Integration::Log::LogMessage(Integration::Log::DebugInfo, "~~~ NetworkPerformanceServer started on port %d ~~~ \n", SERVER_PORT + basePort);
116 void NetworkPerformanceServer::Stop()
122 // close the server thread to prevent any new connections
123 mListeningSocket->ExitSelect();
125 // wait for the thread to exit.
127 pthread_join( mServerThread, &exitValue );
130 mListeningSocket->CloseSocket();
132 mSocketFactory.DestroySocket( mListeningSocket );
134 mListeningSocket = NULL;
136 // 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) )
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" );
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,
239 mTriggerEventFactory,
243 // protect the mClients list which can be accessed from multiple threads.
244 Mutex::ScopedLock lock( mClientListMutex );
246 mClients.PushBack( client );
251 void NetworkPerformanceServer::DeleteClient( NetworkPerformanceClient* client )
253 // protect the mClients list while modifying
254 Mutex::ScopedLock lock( mClientListMutex );
256 // remove from the list, and delete it
257 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
259 if( (*iter) == client )
261 mClients.Erase( iter );
264 // if there server is shutting down, it waits for client count to hit zero
272 void NetworkPerformanceServer::SendData( const char* const data, unsigned int bufferSizeInBytes,unsigned int clientId )
279 // prevent clients been added / deleted while transmiting data
280 Mutex::ScopedLock lock( mClientListMutex );
282 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
284 NetworkPerformanceClient* client = (*iter);
285 if( client->GetId() == clientId )
287 client->WriteSocket(data ,bufferSizeInBytes);
293 void NetworkPerformanceServer::TransmitMarker( const PerformanceMarker& marker, const char* const description )
299 // prevent clients been added / deleted while transmiting data
300 Mutex::ScopedLock lock( mClientListMutex );
302 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
304 NetworkPerformanceClient* client = (*iter);
305 client->TransmitMarker( marker, description );
310 void NetworkPerformanceServer::StopClients()
312 // prevent clients been added / deleted while stopping all clients
313 Mutex::ScopedLock lock( mClientListMutex );
315 for( ClientList::Iterator iter = mClients.Begin(); iter != mClients.End() ; ++iter )
317 NetworkPerformanceClient* client = (*iter);
318 // stop the client from waiting for new commands, and exit from it's thread
319 client->ExitSelect();
322 pthread_t* thread = client->GetThread();
323 pthread_join( *thread, &exitValue );
329 } // namespace Internal
331 } // namespace Adaptor