Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6085486 authored by that's avatar that
Browse files

Run some actions in a separate thread



Some actions need to be threaded so we will run those in a
separate thread and deny requests to thread more actions if new
requests come in while a thread is already running.

Change-Id: I966c538e67860a6d8fe556e5a2eb7f7d1a987e74
Signed-off-by: default avatarVojtech Bocek <vbocek@gmail.com>
parent 41ae1248
Loading
Loading
Loading
Loading
+192 −154
Original line number Diff line number Diff line
@@ -66,11 +66,70 @@ extern blanktimer blankTimer;
void curtainClose(void);

GUIAction::mapFunc GUIAction::mf;
std::set<string> GUIAction::setActionsRunningInCallerThread;
static string zip_queue[10];
static int zip_queue_index;
static pthread_t terminal_command;
pid_t sideload_child_pid;

static ActionThread action_thread;

static void *ActionThread_work_wrapper(void *data)
{
	action_thread.run(data);
	return NULL;
}

ActionThread::ActionThread()
{
	m_thread_running = false;
	pthread_mutex_init(&m_act_lock, NULL);
}

ActionThread::~ActionThread()
{
	pthread_mutex_lock(&m_act_lock);
	if(m_thread_running) {
		pthread_mutex_unlock(&m_act_lock);
		pthread_join(m_thread, NULL);
	} else {
		pthread_mutex_unlock(&m_act_lock);
	}
	pthread_mutex_destroy(&m_act_lock);
}

void ActionThread::threadActions(GUIAction *act, size_t start_index)
{
	pthread_mutex_lock(&m_act_lock);
	if (m_thread_running) {
		pthread_mutex_unlock(&m_act_lock);
		LOGERR("Another threaded action is already running -- not running actions '%s' and following\n", act->mActions[start_index].mFunction.c_str());
	} else {
		m_thread_running = true;
		pthread_mutex_unlock(&m_act_lock);
		ThreadData *d = new ThreadData;
		d->act = act;
		d->start_index = start_index;

		pthread_create(&m_thread, NULL, &ActionThread_work_wrapper, d);
	}
}

void ActionThread::run(void *data)
{
	ThreadData *d = (ThreadData*)data;
	GUIAction* act = d->act;

	std::vector<GUIAction::Action>::iterator it;
	for (it = act->mActions.begin() + d->start_index; it != act->mActions.end(); ++it)
		act->doAction(*it);

	pthread_mutex_lock(&m_act_lock);
	m_thread_running = false;
	pthread_mutex_unlock(&m_act_lock);
	delete d;
}

GUIAction::GUIAction(xml_node<>* node)
	: GUIObject(node)
{
@@ -81,6 +140,7 @@ GUIAction::GUIAction(xml_node<>* node)
	if (!node)  return;

	if (mf.empty()) {
		// These actions will be run in the caller's thread
		mf["reboot"] = &GUIAction::reboot;
		mf["home"] = &GUIAction::home;
		mf["key"] = &GUIAction::key;
@@ -108,9 +168,19 @@ GUIAction::GUIAction(xml_node<>* node)
		mf["getpartitiondetails"] = &GUIAction::getpartitiondetails;
		mf["screenshot"] = &GUIAction::screenshot;
		mf["setbrightness"] = &GUIAction::setbrightness;

		// threaded actions
		mf["fileexists"] = &GUIAction::fileexists;
		mf["killterminal"] = &GUIAction::killterminal;
		mf["checkbackupname"] = &GUIAction::checkbackupname;
		mf["adbsideloadcancel"] = &GUIAction::adbsideloadcancel;
		mf["fixsu"] = &GUIAction::fixsu;
		mf["startmtp"] = &GUIAction::startmtp;
		mf["stopmtp"] = &GUIAction::stopmtp;

		// remember actions that run in the caller thread
		for (mapFunc::const_iterator it = mf.begin(); it != mf.end(); ++it)
			setActionsRunningInCallerThread.insert(it->first);

		// These actions will run in a separate thread
		mf["flash"] = &GUIAction::flash;
		mf["wipe"] = &GUIAction::wipe;
		mf["refreshsizes"] = &GUIAction::refreshsizes;
@@ -123,20 +193,14 @@ GUIAction::GUIAction(xml_node<>* node)
		mf["htcdumlockreflashrecovery"] = &GUIAction::htcdumlockreflashrecovery;
		mf["cmd"] = &GUIAction::cmd;
		mf["terminalcommand"] = &GUIAction::terminalcommand;
		mf["killterminal"] = &GUIAction::killterminal;
		mf["reinjecttwrp"] = &GUIAction::reinjecttwrp;
		mf["checkbackupname"] = &GUIAction::checkbackupname;
		mf["decrypt"] = &GUIAction::decrypt;
		mf["adbsideload"] = &GUIAction::adbsideload;
		mf["adbsideloadcancel"] = &GUIAction::adbsideloadcancel;
		mf["openrecoveryscript"] = &GUIAction::openrecoveryscript;
		mf["installsu"] = &GUIAction::installsu;
		mf["fixsu"] = &GUIAction::fixsu;
		mf["decrypt_backup"] = &GUIAction::decrypt_backup;
		mf["repair"] = &GUIAction::repair;
		mf["changefilesystem"] = &GUIAction::changefilesystem;
		mf["startmtp"] = &GUIAction::startmtp;
		mf["stopmtp"] = &GUIAction::stopmtp;
	}

	// First, get the action
@@ -306,6 +370,11 @@ int GUIAction::flash_zip(std::string filename, std::string pageName, int* wipe_c
	return ret_val;
}

bool GUIAction::needsToRunInSeparateThread(const GUIAction::Action& action)
{
	return setActionsRunningInCallerThread.find(action.mFunction) == setActionsRunningInCallerThread.end();
}

int GUIAction::doActions()
{
	if (mActions.size() < 1)
@@ -313,7 +382,17 @@ int GUIAction::doActions()

	std::vector<Action>::iterator it;
	for (it = mActions.begin(); it != mActions.end(); ++it)
	{
		if (needsToRunInSeparateThread(*it))
		{
			// run all remaining actions in a separate thread
			action_thread.threadActions(this, it - mActions.begin());
			// ...and we're done here
			break;
		}

		doAction(*it);
	}

	return 0;
}
@@ -1168,16 +1247,52 @@ int GUIAction::terminalcommand(std::string arg)
	} else {
		command = "cd \"" + cmdpath + "\" && " + arg + " 2>&1";;
		LOGINFO("Actual command is: '%s'\n", command.c_str());
		DataManager::SetValue("tw_terminal_command_thread", command);
		DataManager::SetValue("tw_terminal_state", 1);
		DataManager::SetValue("tw_background_thread_running", 1);
		op_status = pthread_create(&terminal_command, NULL, command_thread, NULL);
		if (op_status != 0) {
			LOGERR("Error starting terminal command thread, %i.\n", op_status);
		FILE* fp;
		char line[512];

		fp = popen(command.c_str(), "r");
		if (fp == NULL) {
			LOGERR("Error opening command to run.\n");
		} else {
			int fd = fileno(fp), has_data = 0, check = 0, keep_going = -1, bytes_read = 0;
			struct timeval timeout;
			fd_set fdset;

			while(keep_going)
			{
				FD_ZERO(&fdset);
				FD_SET(fd, &fdset);
				timeout.tv_sec = 0;
				timeout.tv_usec = 400000;
				has_data = select(fd+1, &fdset, NULL, NULL, &timeout);
				if (has_data == 0) {
					// Timeout reached
					DataManager::GetValue("tw_terminal_state", check);
					if (check == 0) {
						keep_going = 0;
					}
				} else if (has_data < 0) {
					// End of execution
					keep_going = 0;
				} else {
					// Try to read output
					memset(line, 0, sizeof(line));
					bytes_read = read(fd, line, sizeof(line));
					if (bytes_read > 0)
						gui_print("%s", line); // Display output
					else
						keep_going = 0; // Done executing
				}
			}
			fclose(fp);
		}
		DataManager::SetValue("tw_operation_status", 0);
		DataManager::SetValue("tw_operation_state", 1);
		DataManager::SetValue("tw_terminal_state", 0);
		DataManager::SetValue("tw_background_thread_running", 0);
			operation_end(1);
		}
		DataManager::SetValue(TW_ACTION_BUSY, 0);
	}
	return 0;
}
@@ -1290,11 +1405,15 @@ int GUIAction::decrypt(std::string arg)
	return 0;
}

void* GUIAction::sideload_thread_fn(void *cookie)
int GUIAction::adbsideload(std::string arg)
{
	operation_start("Sideload");
	if (simulate) {
		simulate_progress_bar();
		operation_end(0);
	} else {
		gui_print("Starting ADB sideload feature...\n");
		bool mtp_was_enabled = TWFunc::Toggle_MTP(false);
	GUIAction* this_ = (GUIAction*) cookie;

		// wait for the adb connection
		int ret = apply_from_adb("/", &sideload_child_pid);
@@ -1330,25 +1449,8 @@ void* GUIAction::sideload_thread_fn(void *cookie)
		}

		TWFunc::Toggle_MTP(mtp_was_enabled);
	this_->reinject_after_flash();
	this_->operation_end(ret);
	return NULL;
}

int GUIAction::adbsideload(std::string arg)
{
	operation_start("Sideload");
	if (simulate) {
		simulate_progress_bar();
		operation_end(0);
	} else {
		// we need to start a thread to allow the operation to be cancelable
		pthread_t sideload_thread;
		int rc = pthread_create(&sideload_thread, NULL, sideload_thread_fn, this);
		if (rc != 0) {
			LOGERR("Error starting sideload thread, rc=%i.\n", rc);
			operation_end(1);
		}
		reinject_after_flash();
		operation_end(ret);
	}
	return 0;
}
@@ -1378,10 +1480,13 @@ int GUIAction::adbsideloadcancel(std::string arg)
	return 0;
}

void* GUIAction::openrecoveryscript_thread_fn(void *cookie)
int GUIAction::openrecoveryscript(std::string arg)
{
	GUIAction* this_ = (GUIAction*) cookie;

	operation_start("OpenRecoveryScript");
	if (simulate) {
		simulate_progress_bar();
		operation_end(0);
	} else {
		// Check for the SCRIPT_FILE_TMP first as these are AOSP recovery commands
		// that we converted to ORS commands during boot in recovery.cpp.
		// Run those first.
@@ -1405,24 +1510,8 @@ void* GUIAction::openrecoveryscript_thread_fn(void *cookie)
		} else {
			DataManager::SetValue("tw_page_done", 1);
		}
	this_->operation_end(1);
	return NULL;
}

int GUIAction::openrecoveryscript(std::string arg)
{
	operation_start("OpenRecoveryScript");
	if (simulate) {
		simulate_progress_bar();
		operation_end(0);
	} else {
		// we need to start a thread to allow the action page to display properly
		pthread_t openrecoveryscript_thread;
		int rc = pthread_create(&openrecoveryscript_thread, NULL, openrecoveryscript_thread_fn, this);
		if (rc != 0) {
			LOGERR("Error starting sideload thread, rc=%i.\n", rc);
		operation_end(1);
		}
		return 0;
	}
	return 0;
}
@@ -1575,54 +1664,3 @@ int GUIAction::getKeyByName(std::string key)

	return atol(key.c_str());
}

void* GUIAction::command_thread(void *cookie)
{
	string command;
	FILE* fp;
	char line[512];

	DataManager::GetValue("tw_terminal_command_thread", command);
	fp = popen(command.c_str(), "r");
	if (fp == NULL) {
		LOGERR("Error opening command to run.\n");
	} else {
		int fd = fileno(fp), has_data = 0, check = 0, keep_going = -1, bytes_read = 0;
		struct timeval timeout;
		fd_set fdset;

		while(keep_going)
		{
			FD_ZERO(&fdset);
			FD_SET(fd, &fdset);
			timeout.tv_sec = 0;
			timeout.tv_usec = 400000;
			has_data = select(fd+1, &fdset, NULL, NULL, &timeout);
			if (has_data == 0) {
				// Timeout reached
				DataManager::GetValue("tw_terminal_state", check);
				if (check == 0) {
					keep_going = 0;
				}
			} else if (has_data < 0) {
				// End of execution
				keep_going = 0;
			} else {
				// Try to read output
				memset(line, 0, sizeof(line));
				bytes_read = read(fd, line, sizeof(line));
				if (bytes_read > 0)
					gui_print("%s", line); // Display output
				else
					keep_going = 0; // Done executing
			}
		}
		fclose(fp);
	}
	DataManager::SetValue("tw_operation_status", 0);
	DataManager::SetValue("tw_operation_state", 1);
	DataManager::SetValue("tw_terminal_state", 0);
	DataManager::SetValue("tw_background_thread_running", 0);
	DataManager::SetValue(TW_ACTION_BUSY, 0);
	return NULL;
}
+25 −4
Original line number Diff line number Diff line
@@ -257,6 +257,8 @@ protected:
// GUIAction - Used for standard actions
class GUIAction : public GUIObject, public ActionObject
{
	friend class ActionThread;

public:
	GUIAction(xml_node<>* node);

@@ -281,20 +283,19 @@ protected:
protected:
	int getKeyByName(std::string key);
	int doAction(Action action);
	bool needsToRunInSeparateThread(const Action& action);
	void simulate_progress_bar(void);
	int flash_zip(std::string filename, std::string pageName, int* wipe_cache);
	void reinject_after_flash();
	void operation_start(const string operation_name);
	void operation_end(const int operation_status);
	static void* command_thread(void *cookie);
	static void* sideload_thread_fn(void *cookie);
	static void* openrecoveryscript_thread_fn(void *cookie);
	time_t Start;

	// map action name to function pointer
	typedef int (GUIAction::*execFunction)(std::string);
	typedef std::map<std::string, execFunction> mapFunc;
	static mapFunc mf;
	static std::set<std::string> setActionsRunningInCallerThread;

	// GUI actions
	int reboot(std::string arg);
@@ -323,7 +324,7 @@ protected:
	int screenshot(std::string arg);
	int setbrightness(std::string arg);

	// threaded actions
	// (originally) threaded actions
	int fileexists(std::string arg);
	int flash(std::string arg);
	int wipe(std::string arg);
@@ -355,6 +356,26 @@ protected:
	int simulate;
};

class ActionThread
{
public:
	ActionThread();
	~ActionThread();

	void threadActions(GUIAction *act, size_t start_index);
	void run(void *data);
private:
	struct ThreadData
	{
		GUIAction *act;
		size_t start_index;
	};

	pthread_t m_thread;
	bool m_thread_running;
	pthread_mutex_t m_act_lock;
};

class GUIConsole : public GUIObject, public RenderObject, public ActionObject
{
public: