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