mirror of
				https://github.com/Zygo/bees.git
				synced 2025-10-31 10:10:34 +01:00 
			
		
		
		
	task: replace waiting state with run/exec counter
Task::run() would schedule a new execution of Task, unless it was waiting on a queue for execution. This cannot be implemented with a bool, since a Task might be included in multiple queues, and should still be in waiting state even when executed in that case. Replace the bool with a counter. run() and append() (but not append_nolock) increment the counter, exec() decrements the counter. If the counter is non-zero when run() or append() is called, the Task is not scheduled. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
This commit is contained in:
		| @@ -27,17 +27,20 @@ namespace crucible { | |||||||
| 		/// Create Task object containing closure and description. | 		/// Create Task object containing closure and description. | ||||||
| 		Task(string title, function<void()> exec_fn); | 		Task(string title, function<void()> exec_fn); | ||||||
|  |  | ||||||
| 		/// Schedule Task for at least one future execution. | 		/// Schedule Task for at most one future execution. | ||||||
| 		/// May run Task in current thread or in other thread. | 		/// May run Task in current thread or in other thread. | ||||||
| 		/// May run Task before or after returning. | 		/// May run Task before or after returning. | ||||||
|  | 		/// Schedules Task at the end of the global execution queue. | ||||||
| 		/// | 		/// | ||||||
| 		/// Only one instance of a Task may execute at a time. | 		/// Only one instance of a Task may execute at a time. | ||||||
| 		/// If a Task is already scheduled, run() does nothing. | 		/// If a Task is already scheduled, run() does nothing. | ||||||
| 		/// If a Task is already running, run() reschedules the | 		/// If a Task is already running when a new instance reaches | ||||||
| 		/// task after the currently running instance returns. | 		/// the front of the queue, the new instance will execute | ||||||
|  | 		/// after the current instance exits. | ||||||
| 		void run() const; | 		void run() const; | ||||||
|  |  | ||||||
| 		/// Schedule Task to run after this task has run at least once. | 		/// Schedule Task to run after this Task has run or | ||||||
|  | 		/// been destroyed. | ||||||
| 		void append(const Task &task) const; | 		void append(const Task &task) const; | ||||||
|  |  | ||||||
| 		/// Describe Task as text. | 		/// Describe Task as text. | ||||||
|   | |||||||
							
								
								
									
										46
									
								
								lib/task.cc
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								lib/task.cc
									
									
									
									
									
								
							| @@ -54,9 +54,8 @@ namespace crucible { | |||||||
| 		/// Tasks to be executed after the current task is executed | 		/// Tasks to be executed after the current task is executed | ||||||
| 		list<TaskStatePtr>			m_post_exec_queue; | 		list<TaskStatePtr>			m_post_exec_queue; | ||||||
|  |  | ||||||
| 		/// Set when task is waiting to execute. | 		/// Incremented by run() and append().  Decremented by exec(). | ||||||
| 		/// Cleared when exec() begins. | 		size_t					m_run_count = 0; | ||||||
| 		bool					m_is_waiting = false; |  | ||||||
|  |  | ||||||
| 		/// Set when task starts execution by exec(). | 		/// Set when task starts execution by exec(). | ||||||
| 		/// Cleared when exec() ends. | 		/// Cleared when exec() ends. | ||||||
| @@ -95,15 +94,16 @@ namespace crucible { | |||||||
| 		~TaskState(); | 		~TaskState(); | ||||||
| 		TaskState(string title, function<void()> exec_fn); | 		TaskState(string title, function<void()> exec_fn); | ||||||
|  |  | ||||||
| 		/// Run the task at least once.  If task is already running, appends | 		/// Run the task at most one more time.  If task has | ||||||
| 		/// a self-reference to its after queue.  If task is not running | 		/// already started running, a new instance is scheduled. | ||||||
| 		/// and task is not waiting, adds task to a master queue and marks | 		/// If an instance is already scheduled by run() or | ||||||
| 		/// the task waiting. | 		/// append(), does nothing.  Otherwise, schedules a new | ||||||
|  | 		/// instance at the end of TaskMaster's global queue. | ||||||
| 		void run(); | 		void run(); | ||||||
|  |  | ||||||
| 		/// Execute task immediately in current thread if it is not already | 		/// Execute task immediately in current thread if it is not already | ||||||
| 		/// executing in another thread.  If m_run_again is set while m_is_running | 		/// executing in another thread; otherwise, append the current task | ||||||
| 		/// is true, the thread that set m_is_running will requeue the task. | 		/// to itself to be executed immediately in the other thread. | ||||||
| 		void exec(); | 		void exec(); | ||||||
|  |  | ||||||
| 		/// Return title of task. | 		/// Return title of task. | ||||||
| @@ -112,9 +112,8 @@ namespace crucible { | |||||||
| 		/// Return ID of task. | 		/// Return ID of task. | ||||||
| 		TaskId id() const; | 		TaskId id() const; | ||||||
|  |  | ||||||
| 		/// Queue task to execute after current task finishes executing. | 		/// Queue task to execute after current task finishes executing | ||||||
| 		/// If current task is neither running nor waiting, this | 		/// or is destroyed. | ||||||
| 		/// places the argument task on a worker queue immediately. |  | ||||||
| 		void append(const TaskStatePtr &task); | 		void append(const TaskStatePtr &task); | ||||||
|  |  | ||||||
| 		/// How masy Tasks are there?  Good for catching leaks | 		/// How masy Tasks are there?  Good for catching leaks | ||||||
| @@ -204,7 +203,6 @@ namespace crucible { | |||||||
| 			// then push it to the front of the global queue using normal locking methods. | 			// then push it to the front of the global queue using normal locking methods. | ||||||
| 			TaskStatePtr rescue_task(make_shared<TaskState>("rescue_task", [](){})); | 			TaskStatePtr rescue_task(make_shared<TaskState>("rescue_task", [](){})); | ||||||
| 			swap(rescue_task->m_post_exec_queue, queue); | 			swap(rescue_task->m_post_exec_queue, queue); | ||||||
| 			rescue_task->m_is_waiting; |  | ||||||
| 			TaskQueue tq_one { rescue_task }; | 			TaskQueue tq_one { rescue_task }; | ||||||
| 			TaskMasterState::push_front(tq_one); | 			TaskMasterState::push_front(tq_one); | ||||||
| 		} | 		} | ||||||
| @@ -259,19 +257,20 @@ namespace crucible { | |||||||
| 	void | 	void | ||||||
| 	TaskState::append_nolock(const TaskStatePtr &task) | 	TaskState::append_nolock(const TaskStatePtr &task) | ||||||
| 	{ | 	{ | ||||||
| 		task->m_is_waiting = true; | 		THROW_CHECK0(invalid_argument, task); | ||||||
| 		m_post_exec_queue.push_back(task); | 		m_post_exec_queue.push_back(task); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	void | 	void | ||||||
| 	TaskState::append(const TaskStatePtr &task) | 	TaskState::append(const TaskStatePtr &task) | ||||||
| 	{ | 	{ | ||||||
| 		if (!task) { | 		THROW_CHECK0(invalid_argument, task); | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 		PairLock lock(m_mutex, task->m_mutex); | 		PairLock lock(m_mutex, task->m_mutex); | ||||||
|  | 		if (!task->m_run_count) { | ||||||
|  | 			++task->m_run_count; | ||||||
| 			append_nolock(task); | 			append_nolock(task); | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	void | 	void | ||||||
| 	TaskState::exec() | 	TaskState::exec() | ||||||
| @@ -280,11 +279,11 @@ namespace crucible { | |||||||
| 		THROW_CHECK0(invalid_argument, !m_title.empty()); | 		THROW_CHECK0(invalid_argument, !m_title.empty()); | ||||||
|  |  | ||||||
| 		unique_lock<mutex> lock(m_mutex); | 		unique_lock<mutex> lock(m_mutex); | ||||||
| 		m_is_waiting = false; |  | ||||||
| 		if (m_is_running) { | 		if (m_is_running) { | ||||||
| 			append_nolock(shared_from_this()); | 			append_nolock(shared_from_this()); | ||||||
| 			return; | 			return; | ||||||
| 		} else { | 		} else { | ||||||
|  | 			--m_run_count; | ||||||
| 			m_is_running = true; | 			m_is_running = true; | ||||||
| 		} | 		} | ||||||
| 		lock.unlock(); | 		lock.unlock(); | ||||||
| @@ -327,10 +326,11 @@ namespace crucible { | |||||||
| 	TaskState::run() | 	TaskState::run() | ||||||
| 	{ | 	{ | ||||||
| 		unique_lock<mutex> lock(m_mutex); | 		unique_lock<mutex> lock(m_mutex); | ||||||
| 		if (!m_is_waiting) { | 		if (m_run_count) { | ||||||
| 			TaskMasterState::push_back(shared_from_this()); | 			return; | ||||||
| 			m_is_waiting = true; |  | ||||||
| 		} | 		} | ||||||
|  | 		++m_run_count; | ||||||
|  | 		TaskMasterState::push_back(shared_from_this()); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	TaskMasterState::TaskMasterState(size_t thread_max) : | 	TaskMasterState::TaskMasterState(size_t thread_max) : | ||||||
| @@ -636,9 +636,7 @@ namespace crucible { | |||||||
| 	Task::append(const Task &that) const | 	Task::append(const Task &that) const | ||||||
| 	{ | 	{ | ||||||
| 		THROW_CHECK0(runtime_error, m_task_state); | 		THROW_CHECK0(runtime_error, m_task_state); | ||||||
| 		if (!that) { | 		THROW_CHECK0(runtime_error, that); | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 		m_task_state->append(that.m_task_state); | 		m_task_state->append(that.m_task_state); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user