1 /* Copyright (C) 2002 The gtkmm Development Team
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <glibmmconfig.h>
18 #ifndef GLIBMM_DISABLE_DEPRECATED
19 
20 #include <glibmm/threadpool.h>
21 #include <glibmm/exceptionhandler.h>
22 #include <glibmm/threads.h>
23 #include <glib.h>
24 #include <list>
25 
26 namespace Glib
27 {
28 
29 // internal
30 class ThreadPool::SlotList
31 {
32 public:
33   SlotList();
34   ~SlotList() noexcept;
35 
36   // noncopyable
37   SlotList(const ThreadPool::SlotList&) = delete;
38   ThreadPool::SlotList& operator=(const ThreadPool::SlotList&) = delete;
39 
40   sigc::slot<void>* push(const sigc::slot<void>& slot);
41   sigc::slot<void> pop(sigc::slot<void>* slot_ptr);
42 
43   void lock_and_unlock();
44 
45 private:
46   Glib::Threads::Mutex mutex_;
47   std::list<sigc::slot<void>> list_;
48 };
49 
SlotList()50 ThreadPool::SlotList::SlotList()
51 {
52 }
53 
~SlotList()54 ThreadPool::SlotList::~SlotList() noexcept
55 {
56 }
57 
58 sigc::slot<void>*
push(const sigc::slot<void> & slot)59 ThreadPool::SlotList::push(const sigc::slot<void>& slot)
60 {
61   Threads::Mutex::Lock lock(mutex_);
62 
63   list_.emplace_back(slot);
64   return &list_.back();
65 }
66 
67 sigc::slot<void>
pop(sigc::slot<void> * slot_ptr)68 ThreadPool::SlotList::pop(sigc::slot<void>* slot_ptr)
69 {
70   sigc::slot<void> slot;
71 
72   {
73     Threads::Mutex::Lock lock(mutex_);
74 
75     std::list<sigc::slot<void>>::iterator pslot = list_.begin();
76     while (pslot != list_.end() && slot_ptr != &*pslot)
77       ++pslot;
78 
79     if (pslot != list_.end())
80     {
81       slot = *pslot;
82       list_.erase(pslot);
83     }
84   }
85 
86   return slot;
87 }
88 
89 void
lock_and_unlock()90 ThreadPool::SlotList::lock_and_unlock()
91 {
92   mutex_.lock();
93   mutex_.unlock();
94 }
95 
96 } // namespace Glib
97 
98 namespace
99 {
100 
101 static void
call_thread_entry_slot(void * data,void * user_data)102 call_thread_entry_slot(void* data, void* user_data)
103 {
104   try
105   {
106     Glib::ThreadPool::SlotList* const slot_list =
107       static_cast<Glib::ThreadPool::SlotList*>(user_data);
108 
109     sigc::slot<void> slot(slot_list->pop(static_cast<sigc::slot<void>*>(data)));
110 
111     slot();
112   }
113   catch (Glib::Threads::Thread::Exit&)
114   {
115     // Just exit from the thread.  The Thread::Exit exception
116     // is our sane C++ replacement of g_thread_exit().
117   }
118   catch (...)
119   {
120     Glib::exception_handlers_invoke();
121   }
122 }
123 
124 } // anonymous namespace
125 
126 namespace Glib
127 {
128 
ThreadPool(int max_threads,bool exclusive)129 ThreadPool::ThreadPool(int max_threads, bool exclusive)
130 : gobject_(nullptr), slot_list_(new SlotList())
131 {
132   GError* error = nullptr;
133 
134   gobject_ = g_thread_pool_new(&call_thread_entry_slot, slot_list_, max_threads, exclusive, &error);
135 
136   if (error)
137   {
138     delete slot_list_;
139     slot_list_ = nullptr;
140     Glib::Error::throw_exception(error);
141   }
142 }
143 
~ThreadPool()144 ThreadPool::~ThreadPool() noexcept
145 {
146   if (gobject_)
147     g_thread_pool_free(gobject_, 1, 1);
148 
149   if (slot_list_)
150   {
151     slot_list_->lock_and_unlock();
152     delete slot_list_;
153   }
154 }
155 
156 void
push(const sigc::slot<void> & slot)157 ThreadPool::push(const sigc::slot<void>& slot)
158 {
159   sigc::slot<void>* const slot_ptr = slot_list_->push(slot);
160 
161   GError* error = nullptr;
162   g_thread_pool_push(gobject_, slot_ptr, &error);
163 
164   if (error)
165   {
166     slot_list_->pop(slot_ptr);
167     Glib::Error::throw_exception(error);
168   }
169 }
170 
171 void
set_max_threads(int max_threads)172 ThreadPool::set_max_threads(int max_threads)
173 {
174   GError* error = nullptr;
175   g_thread_pool_set_max_threads(gobject_, max_threads, &error);
176 
177   if (error)
178     Glib::Error::throw_exception(error);
179 }
180 
181 int
get_max_threads() const182 ThreadPool::get_max_threads() const
183 {
184   return g_thread_pool_get_max_threads(gobject_);
185 }
186 
187 unsigned int
get_num_threads() const188 ThreadPool::get_num_threads() const
189 {
190   return g_thread_pool_get_num_threads(gobject_);
191 }
192 
193 unsigned int
unprocessed() const194 ThreadPool::unprocessed() const
195 {
196   return g_thread_pool_unprocessed(gobject_);
197 }
198 
199 bool
get_exclusive() const200 ThreadPool::get_exclusive() const
201 {
202   g_return_val_if_fail(gobject_ != nullptr, false);
203 
204   return gobject_->exclusive;
205 }
206 
207 void
shutdown(bool immediately)208 ThreadPool::shutdown(bool immediately)
209 {
210   if (gobject_)
211   {
212     g_thread_pool_free(gobject_, immediately, 1);
213     gobject_ = nullptr;
214   }
215 
216   if (slot_list_)
217   {
218     slot_list_->lock_and_unlock();
219     delete slot_list_;
220     slot_list_ = nullptr;
221   }
222 }
223 
224 // static
225 void
set_max_unused_threads(int max_threads)226 ThreadPool::set_max_unused_threads(int max_threads)
227 {
228   g_thread_pool_set_max_unused_threads(max_threads);
229 }
230 
231 // static
232 int
get_max_unused_threads()233 ThreadPool::get_max_unused_threads()
234 {
235   return g_thread_pool_get_max_unused_threads();
236 }
237 
238 // static
239 unsigned int
get_num_unused_threads()240 ThreadPool::get_num_unused_threads()
241 {
242   return g_thread_pool_get_num_unused_threads();
243 }
244 
245 // static
246 void
stop_unused_threads()247 ThreadPool::stop_unused_threads()
248 {
249   g_thread_pool_stop_unused_threads();
250 }
251 
252 } // namespace Glib
253 
254 #endif // GLIBMM_DISABLE_DEPRECATED
255