mirror of
				https://github.com/Zygo/bees.git
				synced 2025-10-31 18:20: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. | ||||
| 		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 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. | ||||
| 		/// If a Task is already scheduled, run() does nothing. | ||||
| 		/// If a Task is already running, run() reschedules the | ||||
| 		/// task after the currently running instance returns. | ||||
| 		/// If a Task is already running when a new instance reaches | ||||
| 		/// the front of the queue, the new instance will execute | ||||
| 		/// after the current instance exits. | ||||
| 		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; | ||||
|  | ||||
| 		/// 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 | ||||
| 		list<TaskStatePtr>			m_post_exec_queue; | ||||
|  | ||||
| 		/// Set when task is waiting to execute. | ||||
| 		/// Cleared when exec() begins. | ||||
| 		bool					m_is_waiting = false; | ||||
| 		/// Incremented by run() and append().  Decremented by exec(). | ||||
| 		size_t					m_run_count = 0; | ||||
|  | ||||
| 		/// Set when task starts execution by exec(). | ||||
| 		/// Cleared when exec() ends. | ||||
| @@ -95,15 +94,16 @@ namespace crucible { | ||||
| 		~TaskState(); | ||||
| 		TaskState(string title, function<void()> exec_fn); | ||||
|  | ||||
| 		/// Run the task at least once.  If task is already running, appends | ||||
| 		/// a self-reference to its after queue.  If task is not running | ||||
| 		/// and task is not waiting, adds task to a master queue and marks | ||||
| 		/// the task waiting. | ||||
| 		/// Run the task at most one more time.  If task has | ||||
| 		/// already started running, a new instance is scheduled. | ||||
| 		/// If an instance is already scheduled by run() or | ||||
| 		/// append(), does nothing.  Otherwise, schedules a new | ||||
| 		/// instance at the end of TaskMaster's global queue. | ||||
| 		void run(); | ||||
|  | ||||
| 		/// 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 | ||||
| 		/// is true, the thread that set m_is_running will requeue the task. | ||||
| 		/// executing in another thread; otherwise, append the current task | ||||
| 		/// to itself to be executed immediately in the other thread. | ||||
| 		void exec(); | ||||
|  | ||||
| 		/// Return title of task. | ||||
| @@ -112,9 +112,8 @@ namespace crucible { | ||||
| 		/// Return ID of task. | ||||
| 		TaskId id() const; | ||||
|  | ||||
| 		/// Queue task to execute after current task finishes executing. | ||||
| 		/// If current task is neither running nor waiting, this | ||||
| 		/// places the argument task on a worker queue immediately. | ||||
| 		/// Queue task to execute after current task finishes executing | ||||
| 		/// or is destroyed. | ||||
| 		void append(const TaskStatePtr &task); | ||||
|  | ||||
| 		/// 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. | ||||
| 			TaskStatePtr rescue_task(make_shared<TaskState>("rescue_task", [](){})); | ||||
| 			swap(rescue_task->m_post_exec_queue, queue); | ||||
| 			rescue_task->m_is_waiting; | ||||
| 			TaskQueue tq_one { rescue_task }; | ||||
| 			TaskMasterState::push_front(tq_one); | ||||
| 		} | ||||
| @@ -259,19 +257,20 @@ namespace crucible { | ||||
| 	void | ||||
| 	TaskState::append_nolock(const TaskStatePtr &task) | ||||
| 	{ | ||||
| 		task->m_is_waiting = true; | ||||
| 		THROW_CHECK0(invalid_argument, task); | ||||
| 		m_post_exec_queue.push_back(task); | ||||
| 	} | ||||
|  | ||||
| 	void | ||||
| 	TaskState::append(const TaskStatePtr &task) | ||||
| 	{ | ||||
| 		if (!task) { | ||||
| 			return; | ||||
| 		} | ||||
| 		THROW_CHECK0(invalid_argument, task); | ||||
| 		PairLock lock(m_mutex, task->m_mutex); | ||||
| 		if (!task->m_run_count) { | ||||
| 			++task->m_run_count; | ||||
| 			append_nolock(task); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void | ||||
| 	TaskState::exec() | ||||
| @@ -280,11 +279,11 @@ namespace crucible { | ||||
| 		THROW_CHECK0(invalid_argument, !m_title.empty()); | ||||
|  | ||||
| 		unique_lock<mutex> lock(m_mutex); | ||||
| 		m_is_waiting = false; | ||||
| 		if (m_is_running) { | ||||
| 			append_nolock(shared_from_this()); | ||||
| 			return; | ||||
| 		} else { | ||||
| 			--m_run_count; | ||||
| 			m_is_running = true; | ||||
| 		} | ||||
| 		lock.unlock(); | ||||
| @@ -327,10 +326,11 @@ namespace crucible { | ||||
| 	TaskState::run() | ||||
| 	{ | ||||
| 		unique_lock<mutex> lock(m_mutex); | ||||
| 		if (!m_is_waiting) { | ||||
| 			TaskMasterState::push_back(shared_from_this()); | ||||
| 			m_is_waiting = true; | ||||
| 		if (m_run_count) { | ||||
| 			return; | ||||
| 		} | ||||
| 		++m_run_count; | ||||
| 		TaskMasterState::push_back(shared_from_this()); | ||||
| 	} | ||||
|  | ||||
| 	TaskMasterState::TaskMasterState(size_t thread_max) : | ||||
| @@ -636,9 +636,7 @@ namespace crucible { | ||||
| 	Task::append(const Task &that) const | ||||
| 	{ | ||||
| 		THROW_CHECK0(runtime_error, m_task_state); | ||||
| 		if (!that) { | ||||
| 			return; | ||||
| 		} | ||||
| 		THROW_CHECK0(runtime_error, that); | ||||
| 		m_task_state->append(that.m_task_state); | ||||
| 	} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user