1 /*
2  *  TaskManager.cpp
3  *  OpenLieroX
4  *
5  *  Created by Albert Zeyer on 09.02.09.
6  *  code under LGPL
7  *
8  */
9 
10 #include "ThreadPool.h"
11 #include "TaskManager.h"
12 #include "Debug.h"
13 #include "ReadWriteLock.h"
14 #include "Command.h"
15 
16 TaskManager* taskManager = NULL;
17 
InitTaskManager()18 void InitTaskManager() {
19 	if(!taskManager) {
20 		taskManager = new TaskManager();
21 	}
22 }
23 
UnInitTaskManager()24 void UnInitTaskManager() {
25 	if(taskManager) {
26 		delete taskManager;
27 		taskManager = NULL;
28 	}
29 }
30 
31 
TaskManager()32 TaskManager::TaskManager() {
33 	quitSignal = false;
34 	mutex = SDL_CreateMutex();
35 	taskFinished = SDL_CreateCond();
36 	queueThreadWakeup = SDL_CreateCond();
37 
38 	struct QueuedTaskHandler : Action {
39 		TaskManager* manager;
40 		QueuedTaskHandler(TaskManager* m) : manager(m) {}
41 		int handle() {
42 			ScopedLock lock(manager->mutex);
43 			while(true) {
44 				if(manager->queuedTasks.size() > 0) {
45 					Action* act = manager->queuedTasks.front();
46 					manager->queuedTasks.pop_front();
47 					SDL_mutexV(manager->mutex);
48 					act->handle();
49 					delete act;
50 					SDL_mutexP(manager->mutex);
51 					continue;
52 				}
53 				if(manager->quitSignal) {
54 					return 0;
55 				}
56 				SDL_CondWait(manager->queueThreadWakeup, manager->mutex);
57 			}
58 		}
59 	};
60 	queueThread = threadPool->start(new QueuedTaskHandler(this), "queued task handler");
61 }
62 
~TaskManager()63 TaskManager::~TaskManager() {
64 	SDL_mutexP(mutex);
65 	while(runningTasks.size() > 0) {
66 		notes << "waiting for " << runningTasks.size() << " task(s) to finish:" << endl;
67 		for(std::set<Task*>::iterator i = runningTasks.begin(); i != runningTasks.end(); ++i) {
68 			notes << "  " << (*i)->name << endl;
69 		}
70 		SDL_CondWait(taskFinished, mutex);
71 	}
72 	SDL_mutexV(mutex);
73 
74 	finishQueuedTasks();
75 
76 	SDL_DestroyMutex(mutex);
77 	SDL_DestroyCond(taskFinished);
78 	SDL_DestroyCond(queueThreadWakeup);
79 }
80 
81 
start(Task * t,bool queued)82 void TaskManager::start(Task* t, bool queued) {
83 	Mutex::ScopedLock tlock(t->mutex);
84 
85 	{
86 		ScopedLock lock(mutex);
87 		runningTasks.insert(t);
88 	}
89 
90 	struct TaskHandler : Action {
91 		Task* task;
92 		int handle() {
93 			{
94 				Mutex::ScopedLock lock(task->mutex);
95 				task->state = Task::TS_QUEUED ? Task::TS_RUNNINGQUEUED : Task::TS_RUNNING;
96 			}
97 			int ret = task->handle();
98 			if(task->manager == NULL) {
99 				errors << "TaskManager::TaskHandler: invalid task with unset manager" << endl;
100 				return ret;
101 			}
102 			SDL_mutexP(task->manager->mutex);
103 			task->manager->runningTasks.erase(task);
104 			SDL_CondSignal(task->manager->taskFinished);
105 			SDL_mutexV(task->manager->mutex);
106 			delete task;
107 			return ret;
108 		}
109 	};
110 	TaskHandler* handler = new TaskHandler();
111 	handler->task = t;
112 	if(t->manager != NULL)
113 		errors << "Task->manager must be NULL" << endl;
114 	t->manager = this;
115 
116 	if(!queued) {
117 		t->state = Task::TS_WAITFORIMMSTART;
118 		threadPool->start(handler, t->name + " handler", true);
119 	} else {
120 		ScopedLock lock(mutex);
121 		if(quitSignal) {
122 			warnings << "tried to start queued task " << t->name << " when queued task manager was already shutdown" << endl;
123 			return;
124 		}
125 		t->state = Task::TS_QUEUED;
126 		queuedTasks.push_back(handler);
127 		SDL_CondSignal(queueThreadWakeup);
128 	}
129 }
130 
finishQueuedTasks()131 void TaskManager::finishQueuedTasks() {
132 	{
133 		ScopedLock lock(mutex);
134 		if(quitSignal) // means that we have already finished
135 			return;
136 		quitSignal = true;
137 		SDL_CondSignal(queueThreadWakeup);
138 	}
139 
140 	threadPool->wait(queueThread);
141 	queueThread = NULL;
142 }
143 
144 
dumpState(CmdLineIntf & cli) const145 void TaskManager::dumpState(CmdLineIntf& cli) const {
146 	ScopedLock lock(mutex);
147 	for(std::set<Task*>::const_iterator i = runningTasks.begin(); i != runningTasks.end(); ++i) {
148 		Task::State state;
149 		{
150 			Mutex::ScopedLock lock((*i)->mutex);
151 			state = (*i)->state;
152 		}
153 		switch(state) {
154 			case Task::TS_QUEUED: cli.writeMsg("task '" + (*i)->name + "': queued for execution"); break;
155 			case Task::TS_WAITFORIMMSTART: cli.writeMsg("task '" + (*i)->name + "': wait for immediate start"); break;
156 			case Task::TS_RUNNINGQUEUED: cli.writeMsg("task '" + (*i)->name + "': running from queue"); break;
157 			case Task::TS_RUNNING: cli.writeMsg("task '" + (*i)->name + "': running independently"); break;
158 			case Task::TS_INVALID: cli.writeMsg("task '" + (*i)->name + "': invalid state"); break;
159 		}
160 	}
161 	if(runningTasks.size() == 0)
162 		cli.writeMsg("no tasks queued/running");
163 }
164 
165