1 // Copyright (C) 2007  Davis E. King (davis@dlib.net)
2 // License: Boost Software License   See LICENSE.txt for the full license.
3 #ifndef DLIB_MULTITHREADED_OBJECT_EXTENSIOn_CPP
4 #define DLIB_MULTITHREADED_OBJECT_EXTENSIOn_CPP
5 
6 #include "multithreaded_object_extension.h"
7 #include "create_new_thread_extension.h"
8 
9 
10 namespace dlib
11 {
12 
13 // ----------------------------------------------------------------------------------------
14 
15     multithreaded_object::
multithreaded_object()16     multithreaded_object (
17     ):
18         s(m_),
19         is_running_(false),
20         should_stop_(false),
21         threads_started(0)
22     {
23     }
24 
25 // ----------------------------------------------------------------------------------------
26 
27     multithreaded_object::
~multithreaded_object()28     ~multithreaded_object (
29     )
30     {
31         try
32         {
33             DLIB_ASSERT(number_of_threads_alive() == 0,
34                    "\tmultithreaded_object::~multithreaded_object()"
35                    << "\n\tYou have let a multithreaded object destruct itself before terminating its threads"
36                    << "\n\tthis: " << this
37             );
38         }
39         catch (std::exception& e)
40         {
41             std::cerr << e.what() << std::endl;
42             assert(false);
43             abort();
44         }
45     }
46 
47 // ----------------------------------------------------------------------------------------
48 
49     void multithreaded_object::
clear()50     clear (
51     )
52     {
53         auto_mutex M(m_);
54         stop();
55         wait();
56         dead_threads.clear();
57         is_running_ = false;
58         should_stop_ = false;
59     }
60 
61 // ----------------------------------------------------------------------------------------
62 
63     bool multithreaded_object::
is_running() const64     is_running (
65     ) const
66     {
67         auto_mutex M(m_);
68         return is_running_;
69     }
70 
71 // ----------------------------------------------------------------------------------------
72 
73     unsigned long multithreaded_object::
number_of_threads_registered() const74     number_of_threads_registered (
75     ) const
76     {
77         auto_mutex M(m_);
78         return thread_ids.size() + dead_threads.size();
79     }
80 
81 // ----------------------------------------------------------------------------------------
82 
83     unsigned long multithreaded_object::
number_of_threads_alive() const84     number_of_threads_alive (
85     ) const
86     {
87         auto_mutex M(m_);
88         return threads_started;
89     }
90 
91 // ----------------------------------------------------------------------------------------
92 
93     void multithreaded_object::
wait() const94     wait (
95     ) const
96     {
97         auto_mutex M(m_);
98 
99         DLIB_ASSERT(thread_ids.is_in_domain(get_thread_id()) == false,
100                "\tvoid multithreaded_object::wait()"
101                << "\n\tYou can NOT call this function from one of the threads registered in this object"
102                << "\n\tthis: " << this
103         );
104 
105         while (threads_started > 0)
106             s.wait();
107     }
108 
109 // ----------------------------------------------------------------------------------------
110 
111     void multithreaded_object::
start()112     start (
113     )
114     {
115         auto_mutex M(m_);
116         const unsigned long num_threads_registered = dead_threads.size() + thread_ids.size();
117         // start any dead threads
118         for (unsigned long i = threads_started; i < num_threads_registered; ++i)
119         {
120             if (create_new_thread<multithreaded_object,&multithreaded_object::thread_helper>(*this) == false)
121             {
122                 should_stop_ = true;
123                 is_running_ = false;
124                 throw thread_error();
125             }
126             ++threads_started;
127         }
128         is_running_ = true;
129         should_stop_ = false;
130         s.broadcast();
131     }
132 
133 // ----------------------------------------------------------------------------------------
134 
135     void multithreaded_object::
pause()136     pause (
137     )
138     {
139         auto_mutex M(m_);
140         is_running_ = false;
141     }
142 
143 // ----------------------------------------------------------------------------------------
144 
145     void multithreaded_object::
stop()146     stop (
147     )
148     {
149         auto_mutex M(m_);
150         should_stop_ = true;
151         is_running_ = false;
152         s.broadcast();
153     }
154 
155 // ----------------------------------------------------------------------------------------
156 
157     bool multithreaded_object::
should_stop() const158     should_stop (
159     ) const
160     {
161         auto_mutex M(m_);
162         DLIB_ASSERT(thread_ids.is_in_domain(get_thread_id()),
163                "\tbool multithreaded_object::should_stop()"
164                << "\n\tYou can only call this function from one of the registered threads in this object"
165                << "\n\tthis: " << this
166         );
167         while (is_running_ == false && should_stop_ == false)
168             s.wait();
169         return should_stop_;
170     }
171 
172 // ----------------------------------------------------------------------------------------
173 
174     multithreaded_object::raii_thread_helper::
raii_thread_helper(multithreaded_object & self_,thread_id_type id_)175     raii_thread_helper(
176         multithreaded_object& self_,
177         thread_id_type id_
178     ) : self(self_), id(id_){}
179 
180     multithreaded_object::raii_thread_helper::
~raii_thread_helper()181     ~raii_thread_helper()
182     {
183         auto_mutex M(self.m_);
184         if (self.thread_ids.is_in_domain(id))
185         {
186             mfp temp;
187             thread_id_type id_temp;
188             self.thread_ids.remove(id,id_temp,temp);
189             // put this thread's registered function back into the dead_threads queue
190             self.dead_threads.enqueue(temp);
191         }
192 
193         --self.threads_started;
194         // If this is the last thread to terminate then
195         // signal that that is the case.
196         if (self.threads_started == 0)
197         {
198             self.is_running_ = false;
199             self.should_stop_ = false;
200             self.s.broadcast();
201         }
202     }
203 
204 // ----------------------------------------------------------------------------------------
205 
206     void multithreaded_object::
thread_helper()207     thread_helper(
208     )
209     {
210         mfp mf;
211         thread_id_type id = get_thread_id();
212 
213         // this guy's destructor does all the necessary cleanup in this function
214         raii_thread_helper raii(*this, id);
215 
216         // if there is a dead_thread sitting around then pull it
217         // out and put it into mf
218         {
219             auto_mutex M(m_);
220             if (dead_threads.size() > 0)
221             {
222                 dead_threads.dequeue(mf);
223                 mfp temp(mf);
224                 thread_ids.add(id,temp);
225             }
226         }
227 
228         if (mf.is_set())
229         {
230             // call the registered thread function
231             mf();
232         }
233     }
234 
235 // ----------------------------------------------------------------------------------------
236 
237 }
238 
239 #endif // DLIB_MULTITHREADED_OBJECT_EXTENSIOn_CPP
240 
241 
242