Add a simple configuration file implementation.

This is very loosely based on the legacy SwiftConfig, but
it does not come with a web server-based alternative. The
configuration is read from SwiftShader.ini, if the file exists.
This simplifies things and allows us to keep a single Configuration
object around, as the configuration is static. A more complex
solution can be added later when deemed to be necessary.

For now, the only option supported is ThreadCount, which is
used when creating the marl scheduler. To add new options in the
future, one can simply update the Configuration struct and
the parsing code in SwiftConfig.cpp.

Bug: b/216019572
Change-Id: I8458a99fc4c3e2d180bbc391b0e12d789dea5dcf
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/61628
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Daniele Vettorel <dvet@google.com>
Commit-Queue: Daniele Vettorel <dvet@google.com>
diff --git a/src/System/BUILD.gn b/src/System/BUILD.gn
index b614529..b48c927 100644
--- a/src/System/BUILD.gn
+++ b/src/System/BUILD.gn
@@ -26,6 +26,7 @@
     "Memory.hpp",
     "Socket.cpp",
     "Socket.hpp",
+    "SwiftConfig.hpp",
     "Timer.hpp",
   ]
   if (is_linux || is_chromeos || is_android) {
@@ -44,6 +45,7 @@
     "Half.cpp",
     "Math.cpp",
     "Memory.cpp",
+    "SwiftConfig.cpp",
     "Timer.cpp",
   ]
   if (is_linux || is_chromeos || is_android) {
diff --git a/src/System/CMakeLists.txt b/src/System/CMakeLists.txt
index 5c7e93e..18d7b94 100644
--- a/src/System/CMakeLists.txt
+++ b/src/System/CMakeLists.txt
@@ -37,6 +37,8 @@
     Socket.cpp
     Socket.hpp
     Synchronization.hpp
+    SwiftConfig.cpp
+    SwiftConfig.hpp
     Timer.cpp
     Timer.hpp
     Types.hpp
diff --git a/src/System/Configurator.cpp b/src/System/Configurator.cpp
index fb17fde..79744c4 100644
--- a/src/System/Configurator.cpp
+++ b/src/System/Configurator.cpp
@@ -14,14 +14,11 @@
 
 #include "Configurator.hpp"
 
-#include <fstream>
-#include <iostream>
-
-using namespace std;
-
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <fstream>
+#include <iostream>
 
 #if defined(__unix__)
 #	include <unistd.h>
@@ -29,7 +26,7 @@
 
 namespace sw {
 
-Configurator::Configurator(string iniPath)
+Configurator::Configurator(const std::string &iniPath)
 {
 	path = iniPath;
 
@@ -49,11 +46,11 @@
 	}
 #endif
 
-	fstream file(path.c_str(), ios::in);
+	std::fstream file(path.c_str(), std::ios::in);
 	if(file.fail()) return false;
 
-	string line;
-	string keyName;
+	std::string line;
+	std::string keyName;
 
 	while(getline(file, line))
 	{
@@ -71,17 +68,17 @@
 				return false;
 			}
 
-			string::size_type pLeft = line.find_first_of(";#[=");
+			std::string::size_type pLeft = line.find_first_of(";#[=");
 
-			if(pLeft != string::npos)
+			if(pLeft != std::string::npos)
 			{
 				switch(line[pLeft])
 				{
 				case '[':
 					{
-						string::size_type pRight = line.find_last_of("]");
+						std::string::size_type pRight = line.find_last_of("]");
 
-						if(pRight != string::npos && pRight > pLeft)
+						if(pRight != std::string::npos && pRight > pLeft)
 						{
 							keyName = line.substr(pLeft + 1, pRight - pLeft - 1);
 							addKeyName(keyName);
@@ -90,8 +87,8 @@
 					break;
 				case '=':
 					{
-						string valueName = line.substr(0, pLeft);
-						string value = line.substr(pLeft + 1);
+						std::string valueName = line.substr(0, pLeft);
+						std::string value = line.substr(pLeft + 1);
 						addValue(keyName, valueName, value);
 					}
 					break;
@@ -114,7 +111,7 @@
 	return false;
 }
 
-void Configurator::writeFile(std::string title)
+void Configurator::writeFile(const std::string &title)
 {
 #if defined(__unix__)
 	if(access(path.c_str(), W_OK) != 0)
@@ -123,28 +120,28 @@
 	}
 #endif
 
-	fstream file(path.c_str(), ios::out);
+	std::fstream file(path.c_str(), std::ios::out);
 	if(file.fail()) return;
 
-	file << "; " << title << endl
-	     << endl;
+	file << "; " << title << std::endl
+	     << std::endl;
 
 	for(unsigned int keyID = 0; keyID < sections.size(); keyID++)
 	{
-		file << "[" << names[keyID] << "]" << endl;
+		file << "[" << names[keyID] << "]" << std::endl;
 
 		for(unsigned int valueID = 0; valueID < sections[keyID].names.size(); valueID++)
 		{
-			file << sections[keyID].names[valueID] << "=" << sections[keyID].values[valueID] << endl;
+			file << sections[keyID].names[valueID] << "=" << sections[keyID].values[valueID] << std::endl;
 		}
 
-		file << endl;
+		file << std::endl;
 	}
 
 	file.close();
 }
 
-int Configurator::findKey(string keyName) const
+int Configurator::findKey(const std::string &keyName) const
 {
 	for(unsigned int keyID = 0; keyID < names.size(); keyID++)
 	{
@@ -157,7 +154,7 @@
 	return -1;
 }
 
-int Configurator::findValue(unsigned int keyID, string valueName) const
+int Configurator::findValue(unsigned int keyID, const std::string &valueName) const
 {
 	if(!sections.size() || keyID >= sections.size())
 	{
@@ -175,14 +172,14 @@
 	return -1;
 }
 
-unsigned int Configurator::addKeyName(string keyName)
+unsigned int Configurator::addKeyName(const std::string &keyName)
 {
 	names.resize(names.size() + 1, keyName);
 	sections.resize(sections.size() + 1);
 	return (unsigned int)names.size() - 1;
 }
 
-void Configurator::addValue(string const keyName, string const valueName, string const value)
+void Configurator::addValue(const std::string &keyName, const std::string &valueName, const std::string &value)
 {
 	int keyID = findKey(keyName);
 
@@ -204,7 +201,7 @@
 	}
 }
 
-string Configurator::getValue(string keyName, string valueName, string defaultValue) const
+std::string Configurator::getValue(const std::string &keyName, const std::string &valueName, const std::string &defaultValue) const
 {
 	int keyID = findKey(keyName);
 	if(keyID == -1) return defaultValue;
@@ -214,7 +211,7 @@
 	return sections[keyID].values[valueID];
 }
 
-int Configurator::getInteger(string keyName, string valueName, int defaultValue) const
+int Configurator::getInteger(const std::string &keyName, const std::string &valueName, int defaultValue) const
 {
 	char svalue[256];
 
@@ -223,12 +220,12 @@
 	return atoi(getValue(keyName, valueName, svalue).c_str());
 }
 
-bool Configurator::getBoolean(string keyName, string valueName, bool defaultValue) const
+bool Configurator::getBoolean(const std::string &keyName, const std::string &valueName, bool defaultValue) const
 {
 	return getInteger(keyName, valueName, (int)defaultValue) != 0;
 }
 
-double Configurator::getFloat(string keyName, string valueName, double defaultValue) const
+double Configurator::getFloat(const std::string &keyName, const std::string &valueName, double defaultValue) const
 {
 	char svalue[256];
 
@@ -237,13 +234,13 @@
 	return atof(getValue(keyName, valueName, svalue).c_str());
 }
 
-unsigned int Configurator::getFormatted(string keyName, string valueName, char *format,
+unsigned int Configurator::getFormatted(const std::string &keyName, const std::string &valueName, char *format,
                                         void *v1, void *v2, void *v3, void *v4,
                                         void *v5, void *v6, void *v7, void *v8,
                                         void *v9, void *v10, void *v11, void *v12,
                                         void *v13, void *v14, void *v15, void *v16)
 {
-	string value = getValue(keyName, valueName);
+	std::string value = getValue(keyName, valueName);
 
 	if(!value.length()) return false;
 
diff --git a/src/System/Configurator.hpp b/src/System/Configurator.hpp
index 1cd8f7c..2d8a61a 100644
--- a/src/System/Configurator.hpp
+++ b/src/System/Configurator.hpp
@@ -25,30 +25,30 @@
 class Configurator
 {
 public:
-	Configurator(std::string iniPath = "");
+	Configurator(const std::string &iniPath = "");
 
 	~Configurator();
 
-	std::string getValue(std::string sectionName, std::string valueName, std::string defaultValue = "") const;
-	int getInteger(std::string sectionName, std::string valueName, int defaultValue = 0) const;
-	bool getBoolean(std::string sectionName, std::string valueName, bool defaultValue = false) const;
-	double getFloat(std::string sectionName, std::string valueName, double defaultValue = 0.0) const;
-	unsigned int getFormatted(std::string sectionName, std::string valueName, char *format,
+	std::string getValue(const std::string &sectionName, const std::string &valueName, const std::string &defaultValue = "") const;
+	int getInteger(const std::string &sectionName, const std::string &valueName, int defaultValue = 0) const;
+	bool getBoolean(const std::string &sectionName, const std::string &valueName, bool defaultValue = false) const;
+	double getFloat(const std::string &sectionName, const std::string &valueName, double defaultValue = 0.0) const;
+	unsigned int getFormatted(const std::string &sectionName, const std::string &valueName, char *format,
 	                          void *v1 = 0, void *v2 = 0, void *v3 = 0, void *v4 = 0,
 	                          void *v5 = 0, void *v6 = 0, void *v7 = 0, void *v8 = 0,
 	                          void *v9 = 0, void *v10 = 0, void *v11 = 0, void *v12 = 0,
 	                          void *v13 = 0, void *v14 = 0, void *v15 = 0, void *v16 = 0);
 
-	void addValue(std::string sectionName, std::string valueName, std::string value);
+	void addValue(const std::string &sectionName, const std::string &valueName, const std::string &value);
 
-	void writeFile(std::string title = "Configuration File");
+	void writeFile(const std::string &title = "Configuration File");
 
 private:
 	bool readFile();
 
-	unsigned int addKeyName(std::string sectionName);
-	int findKey(std::string sectionName) const;
-	int findValue(unsigned int sectionID, std::string valueName) const;
+	unsigned int addKeyName(const std::string &sectionName);
+	int findKey(const std::string &sectionName) const;
+	int findValue(unsigned int sectionID, const std::string &valueName) const;
 
 	std::string path;
 
diff --git a/src/System/SwiftConfig.cpp b/src/System/SwiftConfig.cpp
new file mode 100644
index 0000000..81ba15b
--- /dev/null
+++ b/src/System/SwiftConfig.cpp
@@ -0,0 +1,33 @@
+// Copyright 2022 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "SwiftConfig.hpp"
+#include "Configurator.hpp"
+
+namespace sw {
+Configuration readConfigurationFromFile()
+{
+	Configurator ini("SwiftShader.ini");
+	Configuration config{};
+	config.threadCount = ini.getInteger("Processor", "ThreadCount", 0);
+
+	return config;
+}
+
+const Configuration &getConfiguration()
+{
+	static Configuration config = readConfigurationFromFile();
+	return config;
+}
+}  // namespace sw
\ No newline at end of file
diff --git a/src/System/SwiftConfig.hpp b/src/System/SwiftConfig.hpp
new file mode 100644
index 0000000..b5b61a7
--- /dev/null
+++ b/src/System/SwiftConfig.hpp
@@ -0,0 +1,28 @@
+// Copyright 2022 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef sw_SwiftConfig_hpp
+#define sw_SwiftConfig_hpp
+
+namespace sw {
+struct Configuration
+{
+	int threadCount;
+};
+
+// Get the configuration as parsed from a configuration file.
+const Configuration &getConfiguration();
+}  // namespace sw
+
+#endif
\ No newline at end of file
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index d19f990..8f2e58d 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -48,6 +48,7 @@
 #include "Reactor/Nucleus.hpp"
 #include "System/CPUID.hpp"
 #include "System/Debug.hpp"
+#include "System/SwiftConfig.hpp"
 #include "WSI/HeadlessSurfaceKHR.hpp"
 #include "WSI/VkSwapchainKHR.hpp"
 
@@ -136,12 +137,16 @@
 
 	static Scheduler scheduler;  // TODO(b/208256248): Avoid exit-time destructor.
 
+	const sw::Configuration &config = sw::getConfiguration();
+	int threadCount = config.threadCount;
+
 	marl::lock lock(scheduler.mutex);
 	auto sptr = scheduler.weakptr.lock();
 	if(!sptr)
 	{
 		marl::Scheduler::Config cfg;
-		cfg.setWorkerThreadCount(std::min<size_t>(marl::Thread::numLogicalCPUs(), 16));
+		cfg.setWorkerThreadCount(threadCount == 0 ? std::min<size_t>(marl::Thread::numLogicalCPUs(), 16)
+		                                          : threadCount);
 		cfg.setWorkerThreadInitializer([](int) {
 			sw::CPUID::setFlushToZero(true);
 			sw::CPUID::setDenormalsAreZero(true);