xref: /haiku/src/servers/registrar/mime/RegistrarThreadManager.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2001-2007, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Tyler Dauwalder
7  */
8 
9 
10 #include "RegistrarThreadManager.h"
11 
12 #include <RegistrarDefs.h>
13 
14 #include <stdio.h>
15 
16 #include "RegistrarThread.h"
17 
18 
19 //#define DBG(x) x
20 #define DBG(x)
21 #define OUT printf
22 
23 using namespace BPrivate;
24 
25 /*!
26 	\class RegistrarThreadManager
27 	\brief RegistrarThreadManager is the master of all threads spawned by the
28 		registrar
29 */
30 
31 //! Creates a new RegistrarThreadManager object
32 RegistrarThreadManager::RegistrarThreadManager()
33 	: fThreadCount(0)
34 {
35 }
36 
37 // destructor
38 /*! \brief Destroys the RegistrarThreadManager object, killing and deleting any
39 	still running threads.
40 */
41 RegistrarThreadManager::~RegistrarThreadManager()
42 {
43 	KillThreads();
44 }
45 
46 // MessageReceived
47 /*! \brief Handles \c B_REG_MIME_UPDATE_THREAD_FINISHED messages, passing on all
48 	others.
49 
50 	Each \c B_REG_MIME_UPDATE_THREAD_FINISHED message triggers a call to
51 	CleanupThreads().
52 */
53 void
54 RegistrarThreadManager::MessageReceived(BMessage* message)
55 {
56 	switch (message->what) {
57 		case B_REG_MIME_UPDATE_THREAD_FINISHED:
58 		{
59 			CleanupThreads();
60 			break;
61 		}
62 
63 		default:
64 		{
65 			BHandler::MessageReceived(message);
66 			break;
67 		}
68 	}
69 }
70 
71 // LaunchThread
72 /*! \brief Launches the given thread, passing responsiblity for it onto the
73 	RegistrarThreadManager object.
74 
75 	\param thread Pointer to a newly allocated \c RegistrarThread object.
76 
77 	If the result of the function is \c B_OK, the \c RegistrarThreadManager
78 	object assumes ownership of \a thread; if the result is an error code, it
79 	does not.
80 
81 	\return
82 	- \c B_OK: success
83 	- \c B_NO_MORE_THREADS: the number of concurrently allowed threads (defined
84 							by RegistrarThreadManager::kThreadLimit) has
85 							already been reached
86 	- \c B_BAD_THREAD_STATE: the thread has already been launched
87 	- other error code: failure
88 */
89 status_t
90 RegistrarThreadManager::LaunchThread(RegistrarThread *thread)
91 {
92 	status_t err = thread ? B_OK : B_BAD_VALUE;
93 	if (!err) {
94 		if (atomic_add(&fThreadCount, 1) >= kThreadLimit) {
95 			err = B_NO_MORE_THREADS;
96 			atomic_add(&fThreadCount, -1);
97 		}
98 	}
99 
100 	if (!err) {
101 		fThreads.push_back(thread);
102 		err = thread->Run();
103 		if (err) {
104 			std::list<RegistrarThread*>::iterator i;
105 			for (i = fThreads.begin(); i != fThreads.end();) {
106 				if ((*i) == thread) {
107 					i = fThreads.erase(i);
108 					atomic_add(&fThreadCount, -1);
109 					break;
110 				} else
111 					++i;
112 			}
113 		}
114 	}
115 	if (!err)
116 		DBG(OUT("RegistrarThreadManager::LaunchThread(): launched new '%s'"
117 			" thread, id %ld\n", thread->Name(), thread->Id()));
118 	return err;
119 }
120 
121 // CleanupThreads
122 /*! \brief Frees the resources of any threads that are no longer running
123 
124 	\todo This function should perhaps be triggered periodically by a
125 	BMessageRunner once we have our own BMessageRunner implementation.
126 */
127 status_t
128 RegistrarThreadManager::CleanupThreads()
129 {
130 	std::list<RegistrarThread*>::iterator i;
131 	for (i = fThreads.begin(); i != fThreads.end(); ) {
132 		if (*i) {
133 			if ((*i)->IsFinished()) {
134 				DBG(OUT("RegistrarThreadManager::CleanupThreads(): Cleaning up"
135 					" thread %ld\n", (*i)->Id()));
136 				RemoveThread(i);
137 					// adjusts i
138 			} else
139 				++i;
140 		} else {
141 			OUT("WARNING: RegistrarThreadManager::CleanupThreads(): NULL"
142 				" mime_update_thread_shared_data pointer found in and removed"
143 				" from RegistrarThreadManager::fThreads list\n");
144 			i = fThreads.erase(i);
145 		}
146 	}
147 	return B_OK;
148 }
149 
150 // ShutdownThreads
151 /*! \brief Requests that any running threads quit and frees the resources
152 	of any threads that are no longer running.
153 
154 	\todo This function should be called during system shutdown around
155 	the same time as global B_QUIT_REQUESTED messages are sent out.
156 */
157 status_t
158 RegistrarThreadManager::ShutdownThreads()
159 {
160 	std::list<RegistrarThread*>::iterator i;
161 	for (i = fThreads.begin(); i != fThreads.end(); ) {
162 		if (*i) {
163 			if ((*i)->IsFinished()) {
164 				DBG(OUT("RegistrarThreadManager::ShutdownThreads(): Cleaning up"
165 					" thread %ld\n", (*i)->Id()));
166 				RemoveThread(i);
167 					// adjusts i
168 			} else {
169 				DBG(OUT("RegistrarThreadManager::ShutdownThreads(): Shutting"
170 					" down thread %ld\n", (*i)->Id()));
171 				(*i)->AskToExit();
172 				++i;
173 			}
174 		} else {
175 			OUT("WARNING: RegistrarThreadManager::ShutdownThreads(): NULL"
176 				" mime_update_thread_shared_data pointer found in and removed"
177 				" from RegistrarThreadManager::fThreads list\n");
178 			i = fThreads.erase(i);
179 		}
180 	}
181 
182 	/*! \todo We may want to iterate back through the list at this point,
183 		snooze for a moment if find an unfinished thread, and kill it if
184 		it still isn't finished by the time we're done snoozing.
185 	*/
186 
187 	return B_OK;
188 }
189 
190 // KillThreads
191 /*! \brief Kills any running threads and frees their resources.
192 
193 	\todo This function should probably be called during system shutdown
194 	just before kernel shutdown begins.
195 */
196 status_t
197 RegistrarThreadManager::KillThreads()
198 {
199 	std::list<RegistrarThread*>::iterator i;
200 	for (i = fThreads.begin(); i != fThreads.end(); ) {
201 		if (*i) {
202 			if (!(*i)->IsFinished()) {
203 				DBG(OUT("RegistrarThreadManager::KillThreads(): Killing thread"
204 					" %ld\n", (*i)->Id()));
205 				status_t err = kill_thread((*i)->Id());
206 				if (err)
207 					OUT("WARNING: RegistrarThreadManager::KillThreads():"
208 						" kill_thread(%" B_PRId32 ") failed with error code"
209 						" 0x%" B_PRIx32 "\n", (*i)->Id(), err);
210 			}
211 			DBG(OUT("RegistrarThreadManager::KillThreads(): Cleaning up thread"
212 				" %ld\n", (*i)->Id()));
213 			RemoveThread(i);
214 				// adjusts i
215 		} else {
216 			OUT("WARNING: RegistrarThreadManager::KillThreads(): NULL"
217 				" mime_update_thread_shared_data pointer found in and removed"
218 				" from RegistrarThreadManager::fThreads list\n");
219 			i = fThreads.erase(i);
220 		}
221 	}
222 	return B_OK;
223 }
224 
225 // ThreadCount
226 /*! \brief Returns the number of threads currently under management
227 
228 	This is not necessarily a count of how many threads are actually running,
229 	as threads may remain in the thread list that are finished and waiting
230 	to be cleaned up.
231 
232 	\return The number of threads currently under management
233 */
234 uint
235 RegistrarThreadManager::ThreadCount() const
236 {
237 	return fThreadCount;
238 }
239 
240 // RemoveThread
241 /*! \brief Deletes the given thread and removes it from the thread list.
242 */
243 std::list<RegistrarThread*>::iterator&
244 RegistrarThreadManager::RemoveThread(std::list<RegistrarThread*>::iterator &i)
245 {
246 	status_t dummy;
247 	wait_for_thread((*i)->Id(), &dummy);
248 
249 	delete *i;
250 	atomic_add(&fThreadCount, -1);
251 	return (i = fThreads.erase(i));
252 }
253 
254