Dali 3D User Interface Engine
resource-thread-base.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17 
18 // EXTERNAL INCLUDES
19 #include <memory>
21 
22 // INTERNAL INCLUDES
23 #include "resource-thread-base.h"
24 #include "tizen-logging.h"
25 #include "atomics.h"
26 
27 using namespace Dali::Integration;
28 
29 namespace Dali
30 {
31 
32 // Initial values for the members tracking which resources have been cancelled.
33 // They start out with different values so that if the first load executed is
34 // synchronous, it won't be erroneously cancelled.
37 
38 namespace TizenPlatform
39 {
40 
41 namespace
42 {
43 const char * const IDLE_PRIORITY_ENVIRONMENT_VARIABLE_NAME = "DALI_RESOURCE_THREAD_IDLE_PRIORITY";
44 } // unnamed namespace
45 
48 
49 ResourceThreadBase::ResourceThreadBase( ResourceLoader& resourceLoader ) :
50  mResourceLoader( resourceLoader ),
51  mThread( 0 ),
52  mCurrentRequestId( NO_REQUEST_IN_FLIGHT ),
53  mCancelRequestId( NO_REQUEST_CANCELLED ),
54  mPaused( false )
55 {
56 #if defined(DEBUG_ENABLED)
57  mLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_RESOURCE_THREAD_BASE");
58 #endif
59 
60  int error = pthread_create( &mThread, NULL, InternalThreadEntryFunc, this );
61  DALI_ASSERT_ALWAYS( !error && "Error in pthread_create()" );
62 }
63 
65 {
67 
68 #if defined(DEBUG_ENABLED)
69  delete mLogFilter;
70 #endif
71 }
72 
74 {
75  if (mThread)
76  {
77  // wake thread
79 
80  // wait for thread to exit
81  pthread_join( mThread, NULL );
82 
83  mThread = 0;
84  }
85 }
86 
88 {
89  bool wasEmpty = false;
90  bool wasPaused = false;
91 
92  {
93  // Lock while adding to the request queue
95 
96  wasEmpty = mQueue.empty();
97  wasPaused = mPaused;
98 
99  mQueue.push_back( std::make_pair(request, type) );
100  }
101 
102  if( wasEmpty && !wasPaused )
103  {
104  // Wake-up the thread
105  mCondition.Notify();
106  }
107 }
108 
109 // Called from outer thread.
111 {
112  bool found = false;
113  DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s: %u.\n", __FUNCTION__, unsigned(resourceId) );
114 
115  // Eliminate the cancelled request from the request queue if it is in there:
116  {
117  // Lock while searching and removing from the request queue:
119 
120  for( RequestQueueIter iterator = mQueue.begin();
121  iterator != mQueue.end();
122  ++iterator )
123  {
124  if( ((*iterator).first).GetId() == resourceId )
125  {
126  iterator = mQueue.erase( iterator );
127  found = true;
128  break;
129  }
130  }
131  }
132 
133  // Remember the cancelled id for the worker thread to poll at one of its points
134  // of interruption:
135  if( !found )
136  {
138  DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Cancelling in-flight resource (%u).\n", __FUNCTION__, unsigned(resourceId) );
139  }
140 }
141 
142 // Called from worker thread.
144 {
147 
148  if( current == cancelled )
149  {
150  DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Cancelled in-flight resource (%u).\n", __FUNCTION__, unsigned(cancelled) );
151  throw CancelRequestException();
152  }
153 }
154 
156 {
157  ( static_cast<ResourceThreadBase*>( This ) )->ThreadLoop();
158  return NULL;
159 }
160 
162 {
164  mPaused = true;
165 }
166 
168 {
169  // Clear the paused flag and if we weren't running already, also wake up the background thread:
170  bool wasPaused = false;
171  {
173  wasPaused = mPaused;
174  mPaused = false;
175  }
176 
177  // If we were paused, wake up the background thread and give it a
178  // chance to do some work:
179  if( wasPaused )
180  {
181  mCondition.Notify();
182  }
183 }
184 
185 //----------------- Called from separate thread (mThread) -----------------
186 
188 {
189  // TODO: Use Environment Options
190  const char* threadPriorityIdleRequired = std::getenv( IDLE_PRIORITY_ENVIRONMENT_VARIABLE_NAME );
191  if( threadPriorityIdleRequired )
192  {
193  // if the parameter exists then set up an idle priority for this thread
194  struct sched_param sp;
195  sp.sched_priority = 0;
196  sched_setscheduler(0, SCHED_IDLE, &sp);
198  }
199 
200  InstallLogging();
201 
202  while( !mResourceLoader.IsTerminating() )
203  {
204  try
205  {
206  WaitForRequests();
207 
209  {
211  }
212  }
213 
214  catch( CancelRequestException& ex )
215  {
216  // No problem: a derived class deliberately threw to abort an in-flight request
217  // that was cancelled.
218  DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Caught cancellation exception for resource (%u).\n", __FUNCTION__, unsigned(mCurrentRequestId) );
219  CancelRequestException* disableUnusedVarWarning = &ex;
220  ex = *disableUnusedVarWarning;
221  }
222 
223  // Catch all exceptions to avoid killing the process, and log the error:
224  catch( std::exception& ex )
225  {
226  const char * const what = ex.what();
227  DALI_LOG_ERROR( "std::exception caught in resource thread. Aborting request with id %u because of std::exception with reason, \"%s\".\n", unsigned(mCurrentRequestId), what ? what : "null" );
228  }
229  catch( Dali::DaliException& ex )
230  {
231  // Probably a failed assert-always:
232  DALI_LOG_ERROR( "DaliException caught in resource thread. Aborting request with id %u. Location: \"%s\". Condition: \"%s\".\n", unsigned(mCurrentRequestId), ex.location, ex.condition );
233  }
234  catch( ... )
235  {
236  DALI_LOG_ERROR( "Unknown exception caught in resource thread. Aborting request with id %u.\n", unsigned(mCurrentRequestId) );
237  }
238  }
239 }
240 
242 {
244 
245  if( mQueue.empty() || mPaused == true )
246  {
247  // Waiting for a wake up from resource loader control thread
248  // This will be to process a new request or terminate
249  mCondition.Wait( lock );
250  }
251 }
252 
254 {
255  ResourceRequest* request(NULL);
256  RequestType type(RequestLoad);
257 
258  {
259  // lock the queue and extract the next request
261 
262  if (!mQueue.empty())
263  {
264  const RequestInfo & front = mQueue.front();
265  request = new ResourceRequest( front.first );
266  type = front.second;
267  mCurrentRequestId = front.first.GetId();
268  mQueue.pop_front();
269  }
270  } // unlock the queue
271 
272  // process request outside of lock
273  if ( NULL != request )
274  {
275  std::auto_ptr<ResourceRequest> deleter( request );
276  switch( type )
277  {
278  case RequestLoad:
279  {
280  Load(*request);
281  }
282  break;
283 
284  case RequestDownload:
285  {
286  Download(*request);
287  }
288  break;
289 
290  case RequestDecode:
291  {
292  Decode(*request);
293  }
294  break;
295  }
296  }
297 }
298 
300 {
301  // resource loading thread will send its logs to TIZEN Platform's LogMessage handler.
303 }
304 
306 {
307  // uninstall it on resource loading thread.
309 }
310 
312 {
313  DALI_LOG_TRACE_METHOD(mLogFilter);
314  DALI_LOG_WARNING("Resource Downloading from a remote server not supported for this type.");
316 }
317 
319 {
320  DALI_LOG_TRACE_METHOD(mLogFilter);
321  DALI_LOG_WARNING("Resource Decoding from a memory buffer not supported for this type.");
323 }
324 
325 } // namespace TizenPlatform
326 
327 } // namespace Dali
Dali Docs Home
Read more about Dali