Browse Source

URHO3D_THREADING build option. Closes #939.

Lasse Öörni 10 years ago
parent
commit
af7ed744f0

+ 6 - 0
CMake/Modules/Urho3D-CMake-common.cmake

@@ -134,6 +134,7 @@ endif ()
 option (URHO3D_PACKAGING "Enable resources packaging support, on Emscripten default to 1, on other platforms default to 0" ${EMSCRIPTEN})
 option (URHO3D_PROFILING "Enable profiling support" TRUE)
 option (URHO3D_LOGGING "Enable logging support" TRUE)
+cmake_dependent_option (URHO3D_THREADING "Enable threading support" TRUE "NOT EMSCRIPTEN" FALSE)
 option (URHO3D_TESTING "Enable testing support")
 if (URHO3D_TESTING)
     if (EMSCRIPTEN)
@@ -342,6 +343,11 @@ if (URHO3D_LOGGING)
     add_definitions (-DURHO3D_LOGGING)
 endif ()
 
+# Enable threading by default, except for Emscripten.
+if (URHO3D_THREADING)
+    add_definitions (-DURHO3D_THREADING)
+endif ()
+
 # If not on Windows platform, enable Unix mode for kNet library
 if (NOT WIN32)
     add_definitions (-DKNET_UNIX)

+ 1 - 0
Docs/GettingStarted.dox

@@ -97,6 +97,7 @@ A number of build options can be defined when invoking the build scripts or when
 |URHO3D_PACKAGING     |*|Enable resources packaging support, on Emscripten default to 1, on other platforms default to 0|
 |URHO3D_PROFILING     |1|Enable profiling support|
 |URHO3D_LOGGING       |1|Enable logging support|
+|URHO3D_THREADING     |*|Enable thread support, on Emscripten default to 0, on other platforms default to 1|
 |URHO3D_TESTING       |0|Enable testing support|
 |URHO3D_TEST_TIMEOUT  |*|Number of seconds to test run the executables (when testing support is enabled only), default to 10 on Emscripten platform and 5 on other platforms|
 |URHO3D_OPENGL        |0|Use OpenGL instead of Direct3D (Windows platform only)|

+ 16 - 10
Source/Urho3D/Core/Thread.cpp

@@ -35,6 +35,7 @@
 namespace Urho3D
 {
 
+#ifdef URHO3D_THREADING
 #ifdef WIN32
 
 DWORD WINAPI ThreadFunctionStatic(void* data)
@@ -50,14 +51,11 @@ void* ThreadFunctionStatic(void* data)
 {
     Thread* thread = static_cast<Thread*>(data);
     thread->ThreadFunction();
-#ifdef __EMSCRIPTEN__
-    // note: emscripten doesn't have this function but doesn't use threading anyway
-    // so #ifdef it out to prevent linker warnings
     pthread_exit((void*)0);
-#endif
     return 0;
 }
 
+#endif
 #endif
 
 ThreadID Thread::mainThreadID;
@@ -75,6 +73,7 @@ Thread::~Thread()
 
 bool Thread::Run()
 {
+#ifdef URHO3D_THREADING
     // Check if already running
     if (handle_)
         return false;
@@ -82,9 +81,7 @@ bool Thread::Run()
     shouldRun_ = true;
 #ifdef WIN32
     handle_ = CreateThread(0, 0, ThreadFunctionStatic, this, 0, 0);
-#elif !defined(__EMSCRIPTEN__)
-    // note: emscripten doesn't have this function but doesn't use
-    // threading anyway so #ifdef it out to prevent linker warnings
+#else
     handle_ = new pthread_t;
     pthread_attr_t type;
     pthread_attr_init(&type);
@@ -92,10 +89,14 @@ bool Thread::Run()
     pthread_create((pthread_t*)handle_, &type, ThreadFunctionStatic, this);
 #endif
     return handle_ != 0;
+#else
+    return false;
+#endif
 }
 
 void Thread::Stop()
 {
+#ifdef URHO3D_THREADING
     // Check if already stopped
     if (!handle_)
         return;
@@ -104,19 +105,19 @@ void Thread::Stop()
 #ifdef WIN32
     WaitForSingleObject((HANDLE)handle_, INFINITE);
     CloseHandle((HANDLE)handle_);
-#elif !defined(__EMSCRIPTEN__)
-    // note: emscripten doesn't have this function but doesn't use
-    // threading anyway so #ifdef it out to prevent linker warnings
+#else
     pthread_t* thread = (pthread_t*)handle_;
     if (thread)
         pthread_join(*thread, 0);
     delete thread;
 #endif
     handle_ = 0;
+#endif
 }
 
 void Thread::SetPriority(int priority)
 {
+#ifdef URHO3D_THREADING
 #ifdef WIN32
     if (handle_)
         SetThreadPriority((HANDLE)handle_, priority);
@@ -126,6 +127,7 @@ void Thread::SetPriority(int priority)
     if (thread)
         pthread_setschedprio(*thread, priority);
 #endif
+#endif
 }
 
 void Thread::SetMainThread()
@@ -144,7 +146,11 @@ ThreadID Thread::GetCurrentThreadID()
 
 bool Thread::IsMainThread()
 {
+#ifdef URHO3D_THREADING
     return GetCurrentThreadID() == mainThreadID;
+#else
+    return true;
+#endif
 }
 
 }

+ 4 - 0
Source/Urho3D/Core/WorkQueue.cpp

@@ -85,6 +85,7 @@ WorkQueue::~WorkQueue()
 
 void WorkQueue::CreateThreads(unsigned numThreads)
 {
+#ifdef URHO3D_THREADING
     // Other subsystems may initialize themselves according to the number of threads.
     // Therefore allow creating the threads only once, after which the amount is fixed
     if (!threads_.Empty())
@@ -99,6 +100,9 @@ void WorkQueue::CreateThreads(unsigned numThreads)
         thread->Run();
         threads_.Push(thread);
     }
+#else
+    LOGERROR("Can not create worker threads as threading is disabled");
+#endif
 }
 
 SharedPtr<WorkItem> WorkQueue::GetFreeItem()

+ 2 - 0
Source/Urho3D/Engine/Engine.cpp

@@ -198,6 +198,7 @@ bool Engine::Initialize(const VariantMap& parameters)
 
     // Set amount of worker threads according to the available physical CPU cores. Using also hyperthreaded cores results in
     // unpredictable extra synchronization overhead. Also reserve one core for the main thread
+#ifdef URHO3D_THREADING
     unsigned numThreads = GetParameter(parameters, "WorkerThreads", true).GetBool() ? GetNumPhysicalCPUs() - 1 : 0;
     if (numThreads)
     {
@@ -205,6 +206,7 @@ bool Engine::Initialize(const VariantMap& parameters)
 
         LOGINFOF("Created %u worker thread%s", numThreads, numThreads > 1 ? "s" : "");
     }
+#endif
 
     // Add resource paths
     ResourceCache* cache = GetSubsystem<ResourceCache>();

+ 10 - 0
Source/Urho3D/IO/FileSystem.cpp

@@ -383,6 +383,7 @@ int FileSystem::SystemRun(const String& fileName, const Vector<String>& argument
 
 unsigned FileSystem::SystemCommandAsync(const String& commandLine)
 {
+#ifdef URHO3D_THREADING
     if (allowedPaths_.Empty())
     {
         unsigned requestID = nextAsyncExecID_;
@@ -395,10 +396,15 @@ unsigned FileSystem::SystemCommandAsync(const String& commandLine)
         LOGERROR("Executing an external command is not allowed");
         return M_MAX_UNSIGNED;
     }
+#else
+    LOGERROR("Can not execute an asynchronous command as threading is disabled");
+    return M_MAX_UNSIGNED;
+#endif
 }
 
 unsigned FileSystem::SystemRunAsync(const String& fileName, const Vector<String>& arguments)
 {
+#ifdef URHO3D_THREADING
     if (allowedPaths_.Empty())
     {
         unsigned requestID = nextAsyncExecID_;
@@ -411,6 +417,10 @@ unsigned FileSystem::SystemRunAsync(const String& fileName, const Vector<String>
         LOGERROR("Executing an external command is not allowed");
         return M_MAX_UNSIGNED;
     }
+#else
+    LOGERROR("Can not run asynchronously as threading is disabled");
+    return M_MAX_UNSIGNED;
+#endif
 }
 
 bool FileSystem::SystemOpen(const String& fileName, const String& mode)

+ 1 - 1
Source/Urho3D/IO/FileWatcher.cpp

@@ -85,7 +85,7 @@ bool FileWatcher::StartWatching(const String& pathName, bool watchSubDirs)
     // Stop any previous watching
     StopWatching();
 
-#if defined(URHO3D_FILEWATCHER)
+#if defined(URHO3D_FILEWATCHER) && defined(URHO3D_THREADING)
 #if defined(WIN32)
     String nativePath = GetNativePath(RemoveTrailingSlash(pathName));
     

+ 9 - 0
Source/Urho3D/Network/HttpRequest.cpp

@@ -53,8 +53,12 @@ HttpRequest::HttpRequest(const String& url, const String& verb, const Vector<Str
 
     LOGDEBUG("HTTP " + verb_ + " request to URL " + url_);
 
+#ifdef URHO3D_THREADING
     // Start the worker thread to actually create the connection and read the response data.
     Run();
+#else
+    LOGERROR("HTTP request will not execute as threading is disabled");
+#endif
 }
 
 HttpRequest::~HttpRequest()
@@ -194,6 +198,7 @@ void HttpRequest::ThreadFunction()
 
 unsigned HttpRequest::Read(void* dest, unsigned size)
 {
+#ifdef URHO3D_THREADING
     mutex_.Acquire();
 
     unsigned char* destPtr = (unsigned char*)dest;
@@ -246,6 +251,10 @@ unsigned HttpRequest::Read(void* dest, unsigned size)
     CheckEofAndAvailableSize();
     mutex_.Release();
     return totalRead;
+#else
+    // Threading disabled, nothing to read
+    return 0;
+#endif
 }
 
 unsigned HttpRequest::Seek(unsigned position)

+ 4 - 0
Source/Urho3D/Resource/BackgroundLoader.cpp

@@ -20,6 +20,8 @@
 // THE SOFTWARE.
 //
 
+#ifdef URHO3D_THREADING
+
 #include "../Precompiled.h"
 
 #include "../Core/Context.h"
@@ -298,3 +300,5 @@ void BackgroundLoader::FinishBackgroundLoading(BackgroundLoadItem& item)
 }
 
 }
+
+#endif

+ 17 - 0
Source/Urho3D/Resource/ResourceCache.cpp

@@ -76,8 +76,10 @@ ResourceCache::ResourceCache(Context* context) :
     // Register Resource library object factories
     RegisterResourceLibrary(context_);
 
+#ifdef URHO3D_THREADING
     // Create resource background loader. Its thread will start on the first background request
     backgroundLoader_ = new BackgroundLoader(this);
+#endif
 
     // Subscribe BeginFrame for handling directory watchers and background loaded resource finalization
     SubscribeToEvent(E_BEGINFRAME, HANDLER(ResourceCache, HandleBeginFrame));
@@ -85,8 +87,10 @@ ResourceCache::ResourceCache(Context* context) :
 
 ResourceCache::~ResourceCache()
 {
+#ifdef URHO3D_THREADING
     // Shut down the background loader first
     backgroundLoader_.Reset();
+#endif
 }
 
 bool ResourceCache::AddResourceDir(const String& pathName, unsigned priority)
@@ -566,8 +570,10 @@ Resource* ResourceCache::GetResource(StringHash type, const String& nameIn, bool
 
     StringHash nameHash(name);
 
+#ifdef URHO3D_THREADING
     // Check if the resource is being background loaded but is now needed immediately
     backgroundLoader_->WaitForResource(type, nameHash);
+#endif
 
     const SharedPtr<Resource>& existing = FindResource(type, nameHash);
     if (existing)
@@ -626,6 +632,7 @@ Resource* ResourceCache::GetResource(StringHash type, const String& nameIn, bool
 
 bool ResourceCache::BackgroundLoadResource(StringHash type, const String& nameIn, bool sendEventOnFailure, Resource* caller)
 {
+#ifdef URHO3D_THREADING
     // If empty name, fail immediately
     String name = SanitateResourceName(nameIn);
     if (name.Empty())
@@ -637,6 +644,10 @@ bool ResourceCache::BackgroundLoadResource(StringHash type, const String& nameIn
         return false;
 
     return backgroundLoader_->QueueResource(type, name, sendEventOnFailure, caller);
+#else
+    // When threading not supported, fall back to synchronous loading
+    return GetResource(type, nameIn, sendEventOnFailure);
+#endif
 }
 
 SharedPtr<Resource> ResourceCache::GetTempResource(StringHash type, const String& nameIn, bool sendEventOnFailure)
@@ -694,7 +705,11 @@ SharedPtr<Resource> ResourceCache::GetTempResource(StringHash type, const String
 
 unsigned ResourceCache::GetNumBackgroundLoadResources() const
 {
+#ifdef URHO3D_THREADING
     return backgroundLoader_->GetNumQueuedResources();
+#else
+    return 0;
+#endif
 }
 
 void ResourceCache::GetResources(PODVector<Resource*>& result, StringHash type) const
@@ -1010,10 +1025,12 @@ void ResourceCache::HandleBeginFrame(StringHash eventType, VariantMap& eventData
     }
 
     // Check for background loaded resources that can be finished
+#ifdef URHO3D_THREADING
     {
         PROFILE(FinishBackgroundResources);
         backgroundLoader_->FinishResources(finishBackgroundResourcesMs_);
     }
+#endif
 }
 
 File* ResourceCache::SearchResourceDirs(const String& nameIn)

+ 6 - 0
Source/Urho3D/Scene/Scene.cpp

@@ -1076,6 +1076,8 @@ void Scene::FinishSaving(Serializer* dest) const
 
 void Scene::PreloadResources(File* file, bool isSceneFile)
 {
+    // If not threaded, can not background load resources, so rather load synchronously later when needed
+#ifdef URHO3D_THREADING
     ResourceCache* cache = GetSubsystem<ResourceCache>();
 
     // Read node ID (not needed)
@@ -1145,10 +1147,13 @@ void Scene::PreloadResources(File* file, bool isSceneFile)
     unsigned numChildren = file->ReadVLE();
     for (unsigned i = 0; i < numChildren; ++i)
         PreloadResources(file, false);
+#endif
 }
 
 void Scene::PreloadResourcesXML(const XMLElement& element)
 {
+    // If not threaded, can not background load resources, so rather load synchronously later when needed
+#ifdef URHO3D_THREADING
     ResourceCache* cache = GetSubsystem<ResourceCache>();
 
     // Node or Scene attributes do not include any resources; therefore skip to the components
@@ -1222,6 +1227,7 @@ void Scene::PreloadResourcesXML(const XMLElement& element)
         PreloadResourcesXML(childElem);
         childElem = childElem.GetNext("node");
     }
+#endif
 }
 
 void RegisterSceneLibrary(Context* context)