2 * Copyright (c) 2014 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.
18 #include <dali/integration-api/debug.h>
19 #include "resource-thread-base.h"
20 #include "slp-logging.h"
23 using namespace Dali::Integration;
25 using boost::unique_lock;
30 // Initial values for the members tracking which resources have been cancelled.
31 // They start out with different values so that if the first load executed is
32 // synchronous, it won't be erroneously cancelled.
33 const Integration::ResourceId NO_REQUEST_IN_FLIGHT = Integration::ResourceId(0) - 1;
34 const Integration::ResourceId NO_REQUEST_CANCELLED = Integration::ResourceId(0) - 2;
41 const char * const IDLE_PRIORITY_ENVIRONMENT_VARIABLE_NAME = "DALI_RESOURCE_THREAD_IDLE_PRIORITY"; ///@Todo Move this to somewhere that other environment variables are declared and document it there.
42 } // unnamed namespace
44 /** Thrown by InterruptionPoint() to abort a request early. */
45 class CancelRequestException {};
47 ResourceThreadBase::ResourceThreadBase( ResourceLoader& resourceLoader ) :
48 mResourceLoader( resourceLoader ),
49 mCurrentRequestId( NO_REQUEST_IN_FLIGHT ),
50 mCancelRequestId( NO_REQUEST_CANCELLED ),
53 #if defined(DEBUG_ENABLED)
54 mLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_RESOURCE_THREAD_BASE");
57 mThread = new boost::thread(boost::bind(&ResourceThreadBase::ThreadLoop, this));
60 ResourceThreadBase::~ResourceThreadBase()
64 #if defined(DEBUG_ENABLED)
69 void ResourceThreadBase::TerminateThread()
74 mCondition.notify_all();
75 // wait for thread to exit
77 // delete thread instance
79 // mark thread terminated
84 void ResourceThreadBase::AddRequest(const ResourceRequest& request, const RequestType type)
86 bool wasEmpty = false;
87 bool wasPaused = false;
90 // Lock while adding to the request queue
91 unique_lock<mutex> lock( mMutex );
93 wasEmpty = mQueue.empty();
96 mQueue.push_back( std::make_pair(request, type) );
99 if( wasEmpty && !wasPaused )
101 // Wake-up the thread
102 mCondition.notify_all();
106 // Called from outer thread.
107 void ResourceThreadBase::CancelRequest( const Integration::ResourceId resourceId )
110 DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s: %u.\n", __FUNCTION__, unsigned(resourceId) );
112 // Eliminate the cancelled request from the request queue if it is in there:
114 // Lock while searching and removing from the request queue:
115 unique_lock<mutex> lock( mMutex );
117 for( RequestQueueIter iterator = mQueue.begin();
118 iterator != mQueue.end();
121 if( ((*iterator).first).GetId() == resourceId )
123 iterator = mQueue.erase( iterator );
130 // Remember the cancelled id for the worker thread to poll at one of its points
134 Dali::Internal::AtomicWriteToCacheableAlignedAddress( &mCancelRequestId, resourceId );
135 DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Cancelling in-flight resource (%u).\n", __FUNCTION__, unsigned(resourceId) );
139 // Called from worker thread.
140 void ResourceThreadBase::InterruptionPoint() const
142 const Integration::ResourceId cancelled = Dali::Internal::AtomicReadFromCacheableAlignedAddress( &mCancelRequestId );
143 const Integration::ResourceId current = mCurrentRequestId;
145 if( current == cancelled )
147 DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Cancelled in-flight resource (%u).\n", __FUNCTION__, unsigned(cancelled) );
148 throw CancelRequestException();
152 void ResourceThreadBase::Pause()
154 unique_lock<mutex> lock( mMutex );
158 void ResourceThreadBase::Resume()
160 // Clear the paused flag and if we weren't running already, also wake up the background thread:
161 bool wasPaused = false;
163 unique_lock<mutex> lock( mMutex );
168 // If we were paused, wake up the background thread and give it a
169 // chance to do some work:
172 mCondition.notify_all();
176 //----------------- Called from separate thread (mThread) -----------------
178 void ResourceThreadBase::ThreadLoop()
180 // TODO: Use Environment Options
181 const char* threadPriorityIdleRequired = std::getenv( IDLE_PRIORITY_ENVIRONMENT_VARIABLE_NAME );
182 if( threadPriorityIdleRequired )
184 // if the parameter exists then set up an idle priority for this thread
185 struct sched_param sp;
186 sp.sched_priority = 0;
187 sched_setscheduler(0, SCHED_IDLE, &sp);
188 ///@ToDo: change to the corresponding Pthreads call, not this POSIX.1-2001 one with a Linux-specific argument (SCHED_IDLE): int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param);, as specified in the docs for sched_setscheduler(): http://man7.org/linux/man-pages/man2/sched_setscheduler.2.html
193 while( !mResourceLoader.IsTerminating() )
199 if ( !mResourceLoader.IsTerminating() )
201 ProcessNextRequest();
205 catch( CancelRequestException& ex )
207 // No problem: a derived class deliberately threw to abort an in-flight request
208 // that was cancelled.
209 DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Caught cancellation exception for resource (%u).\n", __FUNCTION__, unsigned(mCurrentRequestId) );
210 CancelRequestException* disableUnusedVarWarning = &ex;
211 ex = *disableUnusedVarWarning;
214 // Catch all exceptions to avoid killing the process, and log the error:
215 catch( std::exception& ex )
217 const char * const what = ex.what();
218 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" );
220 catch( Dali::DaliException& ex )
222 // Probably a failed assert-always:
223 DALI_LOG_ERROR( "DaliException caught in resource thread. Aborting request with id %u. Location: \"%s\". Condition: \"%s\".\n", unsigned(mCurrentRequestId), ex.location, ex.condition );
227 DALI_LOG_ERROR( "Unknown exception caught in resource thread. Aborting request with id %u.\n", unsigned(mCurrentRequestId) );
232 void ResourceThreadBase::WaitForRequests()
234 unique_lock<mutex> lock( mMutex );
236 if( mQueue.empty() || mPaused == true )
238 // Waiting for a wake up from resource loader control thread
239 // This will be to process a new request or terminate
240 mCondition.wait(lock);
244 void ResourceThreadBase::ProcessNextRequest()
246 ResourceRequest* request(NULL);
247 RequestType type(RequestLoad);
250 // lock the queue and extract the next request
251 unique_lock<mutex> lock(mMutex);
255 const RequestInfo & front = mQueue.front();
256 request = new ResourceRequest( front.first );
258 mCurrentRequestId = front.first.GetId();
261 } // unlock the queue
263 // process request outside of lock
264 if ( NULL != request )
266 std::auto_ptr<ResourceRequest> deleter( request );
275 case RequestDownload:
296 void ResourceThreadBase::InstallLogging()
298 // resource loading thread will send its logs to SLP Platform's LogMessage handler.
299 Dali::Integration::Log::InstallLogFunction(Dali::SlpPlatform::LogMessage);
302 void ResourceThreadBase::UninstallLogging()
304 // uninstall it on resource loading thread.
305 Dali::Integration::Log::UninstallLogFunction();
308 void ResourceThreadBase::Download(const Integration::ResourceRequest& request)
310 DALI_LOG_TRACE_METHOD(mLogFilter);
311 DALI_LOG_WARNING("Resource Downloading from a remote server not supported for this type.");
312 ///! If you need this for a subclassed thread, look to ResourceThreadImage::Download() for an example implementation.
315 void ResourceThreadBase::Decode(const Integration::ResourceRequest& request)
317 DALI_LOG_TRACE_METHOD(mLogFilter);
318 DALI_LOG_WARNING("Resource Decoding from a memory buffer not supported for this type.");
319 ///! If you need this for a subclassed thread, look to ResourceThreadImage::Decode() for an example implementation.
322 } // namespace SlpPlatform