xref: /haiku/src/servers/registrar/mime/RegistrarThreadManager.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
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(%ld) failed with "
203 						"error code 0x%lx\n", (*i)->Id(), err);
204 			}
205 			DBG(OUT("RegistrarThreadManager::KillThreads(): Cleaning up thread %ld\n",
206 				(*i)->Id()));
207 			RemoveThread(i);
208 				// adjusts i
209 		} else {
210 			OUT("WARNING: RegistrarThreadManager::KillThreads(): NULL mime_update_thread_shared_data "
211 				"pointer found in and removed from RegistrarThreadManager::fThreads list\n");
212 			i = fThreads.erase(i);
213 		}
214 	}
215 	return B_OK;
216 }
217 
218 // ThreadCount
219 /*! \brief Returns the number of threads currently under management
220 
221 	This is not necessarily a count of how many threads are actually running,
222 	as threads may remain in the thread list that are finished and waiting
223 	to be cleaned up.
224 
225 	\return The number of threads currently under management
226 */
227 uint
228 RegistrarThreadManager::ThreadCount() const
229 {
230 	return fThreadCount;
231 }
232 
233 // RemoveThread
234 /*! \brief Deletes the given thread and removes it from the thread list.
235 */
236 std::list<RegistrarThread*>::iterator&
237 RegistrarThreadManager::RemoveThread(std::list<RegistrarThread*>::iterator &i)
238 {
239 	delete *i;
240 	atomic_add(&fThreadCount, -1);
241 	return (i = fThreads.erase(i));
242 }
243 
244