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
23using 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
32RegistrarThreadManager::RegistrarThreadManager()
33	: fThreadCount(0)
34{
35}
36
37// destructor
38/*! \brief Destroys the RegistrarThreadManager object, killing and deleting any
39	still running threads.
40*/
41RegistrarThreadManager::~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*/
53void
54RegistrarThreadManager::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*/
89status_t
90RegistrarThreadManager::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*/
127status_t
128RegistrarThreadManager::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*/
157status_t
158RegistrarThreadManager::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*/
196status_t
197RegistrarThreadManager::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*/
234uint
235RegistrarThreadManager::ThreadCount() const
236{
237	return fThreadCount;
238}
239
240// RemoveThread
241/*! \brief Deletes the given thread and removes it from the thread list.
242*/
243std::list<RegistrarThread*>::iterator&
244RegistrarThreadManager::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