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;
26 using boost::scoped_ptr;
31 // Initial values for the members tracking which resources have been cancelled.
32 // They start out with different values so that if the first load executed is
33 // synchronous, it won't be erroneously cancelled.
34 const Integration::ResourceId NO_REQUEST_IN_FLIGHT = Integration::ResourceId(0) - 1;
35 const Integration::ResourceId NO_REQUEST_CANCELLED = Integration::ResourceId(0) - 2;
42 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.
43 } // unnamed namespace
45 /** Thrown by InterruptionPoint() to abort a request early. */
46 class CancelRequestException {};
48 ResourceThreadBase::ResourceThreadBase( ResourceLoader& resourceLoader ) :
49 mResourceLoader( resourceLoader ),
50 mCurrentRequestId( NO_REQUEST_IN_FLIGHT ),
51 mCancelRequestId( NO_REQUEST_CANCELLED ),
54 #if defined(DEBUG_ENABLED)
55 mLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_RESOURCE_THREAD_BASE");
58 mThread = new boost::thread(boost::bind(&ResourceThreadBase::ThreadLoop, this));
61 ResourceThreadBase::~ResourceThreadBase()
65 #if defined(DEBUG_ENABLED)
70 void ResourceThreadBase::TerminateThread()
75 mCondition.notify_all();
76 // wait for thread to exit
78 // delete thread instance
80 // mark thread terminated
85 void ResourceThreadBase::AddRequest(const ResourceRequest& request, const RequestType type)
87 bool wasEmpty = false;
88 bool wasPaused = false;
91 // Lock while adding to the request queue
92 unique_lock<mutex> lock( mMutex );
94 wasEmpty = mQueue.empty();
97 mQueue.push_back( std::make_pair(request, type) );
100 if( wasEmpty && !wasPaused )
102 // Wake-up the thread
103 mCondition.notify_all();
107 // Called from outer thread.
108 void ResourceThreadBase::CancelRequest( const Integration::ResourceId resourceId )
111 DALI_LOG_INFO( mLogFilter, Debug::Verbose, "%s: %u.\n", __FUNCTION__, unsigned(resourceId) );
113 // Eliminate the cancelled request from the request queue if it is in there:
115 // Lock while searching and removing from the request queue:
116 unique_lock<mutex> lock( mMutex );
118 for( RequestQueueIter iterator = mQueue.begin();
119 iterator != mQueue.end();
122 if( ((*iterator).first).GetId() == resourceId )
124 iterator = mQueue.erase( iterator );
131 // Remember the cancelled id for the worker thread to poll at one of its points
135 Dali::Internal::AtomicWriteToCacheableAlignedAddress( &mCancelRequestId, resourceId );
136 DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Cancelling in-flight resource (%u).\n", __FUNCTION__, unsigned(resourceId) );
140 // Called from worker thread.
141 void ResourceThreadBase::InterruptionPoint() const
143 const Integration::ResourceId cancelled = Dali::Internal::AtomicReadFromCacheableAlignedAddress( &mCancelRequestId );
144 const Integration::ResourceId current = mCurrentRequestId;
146 if( current == cancelled )
148 DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Cancelled in-flight resource (%u).\n", __FUNCTION__, unsigned(cancelled) );
149 throw CancelRequestException();
153 void ResourceThreadBase::Pause()
155 unique_lock<mutex> lock( mMutex );
159 void ResourceThreadBase::Resume()
161 // Clear the paused flag and if we weren't running already, also wake up the background thread:
162 bool wasPaused = false;
164 unique_lock<mutex> lock( mMutex );
169 // If we were paused, wake up the background thread and give it a
170 // chance to do some work:
173 mCondition.notify_all();
177 //----------------- Called from separate thread (mThread) -----------------
179 void ResourceThreadBase::ThreadLoop()
181 // TODO: Use Environment Options
182 const char* threadPriorityIdleRequired = std::getenv( IDLE_PRIORITY_ENVIRONMENT_VARIABLE_NAME );
183 if( threadPriorityIdleRequired )
185 // if the parameter exists then set up an idle priority for this thread
186 struct sched_param sp;
187 sp.sched_priority = 0;
188 sched_setscheduler(0, SCHED_IDLE, &sp);
189 ///@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
194 while( !mResourceLoader.IsTerminating() )
200 if ( !mResourceLoader.IsTerminating() )
202 ProcessNextRequest();
206 catch( CancelRequestException& ex )
208 // No problem: a derived class deliberately threw to abort an in-flight request
209 // that was cancelled.
210 DALI_LOG_INFO( mLogFilter, Debug::Concise, "%s: Caught cancellation exception for resource (%u).\n", __FUNCTION__, unsigned(mCurrentRequestId) );
211 CancelRequestException* disableUnusedVarWarning = &ex;
212 ex = *disableUnusedVarWarning;
215 // Catch all exceptions to avoid killing the process, and log the error:
216 catch( std::exception& ex )
218 const char * const what = ex.what();
219 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" );
221 catch( Dali::DaliException& ex )
223 // Probably a failed assert-always:
224 DALI_LOG_ERROR( "DaliException caught in resource thread. Aborting request with id %u. Location: \"%s\". Condition: \"%s\".\n", unsigned(mCurrentRequestId), ex.mLocation.c_str(), ex.mCondition.c_str() );
228 DALI_LOG_ERROR( "Unknown exception caught in resource thread. Aborting request with id %u.\n", unsigned(mCurrentRequestId) );
233 void ResourceThreadBase::WaitForRequests()
235 unique_lock<mutex> lock( mMutex );
237 if( mQueue.empty() || mPaused == true )
239 // Waiting for a wake up from resource loader control thread
240 // This will be to process a new request or terminate
241 mCondition.wait(lock);
245 void ResourceThreadBase::ProcessNextRequest()
247 ResourceRequest* request(NULL);
248 RequestType type(RequestLoad);
251 // lock the queue and extract the next request
252 unique_lock<mutex> lock(mMutex);
256 const RequestInfo & front = mQueue.front();
257 request = new ResourceRequest( front.first );
259 mCurrentRequestId = front.first.GetId();
262 } // unlock the queue
264 // process request outside of lock
265 if ( NULL != request )
267 std::auto_ptr<ResourceRequest> deleter( request );
291 void ResourceThreadBase::InstallLogging()
293 // resource loading thread will send its logs to SLP Platform's LogMessage handler.
294 Dali::Integration::Log::InstallLogFunction(Dali::SlpPlatform::LogMessage);
297 void ResourceThreadBase::UninstallLogging()
299 // uninstall it on resource loading thread.
300 Dali::Integration::Log::UninstallLogFunction();
303 void ResourceThreadBase::Decode(const Integration::ResourceRequest& request)
305 DALI_LOG_TRACE_METHOD(mLogFilter);
306 DALI_LOG_WARNING("Resource Decoding from a memory buffer not supported for this type.");
307 ///! If you need this for a subclassed thread, look to ResourceThreadImage::Decode() for an example implementation.
310 } // namespace SlpPlatform