1 /*=========================================================================
2 *
3 * Copyright Insight Software Consortium
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0.txt
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *=========================================================================*/
18 /*=========================================================================
19 *
20 * Portions of this file are subject to the VTK Toolkit Version 3 copyright.
21 *
22 * Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
23 *
24 * For complete copyright, license and disclaimer of warranty information
25 * please refer to the NOTICE file at the top of the ITK source tree.
26 *
27 *=========================================================================*/
28 #include "itkPlatformMultiThreader.h"
29 #include "itkNumericTraits.h"
30 #include <algorithm>
31 #include <iostream>
32 #include <string>
33 #include <algorithm>
34
35 #if defined(ITK_USE_PTHREADS)
36 #include "itkPlatformMultiThreaderPosix.cxx"
37 #elif defined(ITK_USE_WIN32_THREADS)
38 #include "itkPlatformMultiThreaderWindows.cxx"
39 #else
40 #include "itkPlatformMultiThreaderSingle.cxx"
41 #endif
42
43 namespace itk
44 {
45
PlatformMultiThreader()46 PlatformMultiThreader::PlatformMultiThreader()
47 {
48 for( ThreadIdType i = 0; i < ITK_MAX_THREADS; ++i )
49 {
50 m_ThreadInfoArray[i].WorkUnitID = i;
51 m_ThreadInfoArray[i].ActiveFlag = nullptr;
52 m_ThreadInfoArray[i].ActiveFlagLock = nullptr;
53
54 #if !defined( ITK_LEGACY_REMOVE )
55 m_MultipleMethod[i] = nullptr;
56 m_MultipleData[i] = nullptr;
57 #endif
58
59 m_SpawnedThreadActiveFlag[i] = 0;
60 m_SpawnedThreadActiveFlagLock[i] = nullptr;
61 m_SpawnedThreadInfoArray[i].WorkUnitID = i;
62 }
63 }
64
65 PlatformMultiThreader::~PlatformMultiThreader() = default;
66
SetMaximumNumberOfThreads(ThreadIdType numberOfThreads)67 void PlatformMultiThreader::SetMaximumNumberOfThreads( ThreadIdType numberOfThreads )
68 {
69 Superclass::SetMaximumNumberOfThreads( numberOfThreads );
70 Superclass::SetNumberOfWorkUnits( this->GetMaximumNumberOfThreads() );
71 }
72
SetNumberOfWorkUnits(ThreadIdType numberOfWorkUnits)73 void PlatformMultiThreader::SetNumberOfWorkUnits( ThreadIdType numberOfWorkUnits )
74 {
75 this->SetMaximumNumberOfThreads( numberOfWorkUnits );
76 }
77
SetSingleMethod(ThreadFunctionType f,void * data)78 void PlatformMultiThreader::SetSingleMethod(ThreadFunctionType f, void *data)
79 {
80 m_SingleMethod = f;
81 m_SingleData = data;
82 }
83
84 #if !defined ( ITK_LEGACY_REMOVE )
85 // Set one of the user defined methods that will be run on NumberOfWorkUnits
86 // threads when MultipleMethodExecute is called. This method should be
87 // called with index = 0, 1, .., NumberOfWorkUnits-1 to set up all the
88 // required user defined methods
SetMultipleMethod(ThreadIdType index,ThreadFunctionType f,void * data)89 void PlatformMultiThreader::SetMultipleMethod(ThreadIdType index, ThreadFunctionType f, void *data)
90 {
91 // You can only set the method for 0 through NumberOfWorkUnits-1
92 if( index >= m_NumberOfWorkUnits )
93 {
94 itkExceptionMacro(<< "Can't set method " << index << " with a thread count of " << m_NumberOfWorkUnits);
95 }
96 else
97 {
98 m_MultipleMethod[index] = f;
99 m_MultipleData[index] = data;
100 }
101 }
102 #endif
103
SingleMethodExecute()104 void PlatformMultiThreader::SingleMethodExecute()
105 {
106 ThreadIdType thread_loop = 0;
107 ThreadProcessIdType process_id[ITK_MAX_THREADS];
108
109 if( !m_SingleMethod )
110 {
111 itkExceptionMacro(<< "No single method set!");
112 }
113
114 // obey the global maximum number of threads limit
115 m_NumberOfWorkUnits = std::min( MultiThreaderBase::GetGlobalMaximumNumberOfThreads(), m_NumberOfWorkUnits );
116
117 // Init process_id table because a valid process_id (i.e., non-zero), is
118 // checked in the WaitForSingleMethodThread loops
119 for( thread_loop = 1; thread_loop < m_NumberOfWorkUnits; ++thread_loop )
120 {
121 process_id[thread_loop] = ITK_DEFAULT_THREAD_ID;
122 }
123
124 // Spawn a set of threads through the SingleMethodProxy. Exceptions
125 // thrown from a thread will be caught by the SingleMethodProxy. A
126 // naive mechanism is in place for determining whether a thread
127 // threw an exception.
128 //
129 // Thanks to Hannu Helminen for suggestions on how to catch
130 // exceptions thrown by threads.
131 bool exceptionOccurred = false;
132 std::string exceptionDetails;
133 try
134 {
135 for( thread_loop = 1; thread_loop < m_NumberOfWorkUnits; ++thread_loop )
136 {
137 m_ThreadInfoArray[thread_loop].UserData = m_SingleData;
138 m_ThreadInfoArray[thread_loop].NumberOfWorkUnits = m_NumberOfWorkUnits;
139 m_ThreadInfoArray[thread_loop].ThreadFunction = m_SingleMethod;
140
141 process_id[thread_loop] =
142 this->SpawnDispatchSingleMethodThread(&m_ThreadInfoArray[thread_loop]);
143 }
144 }
145 catch( std::exception & e )
146 {
147 // get the details of the exception to rethrow them
148 exceptionDetails = e.what();
149 // If creation of any thread failed, we must make sure that all
150 // threads are correctly cleaned
151 exceptionOccurred = true;
152 }
153 catch( ... )
154 {
155 // If creation of any thread failed, we must make sure that all
156 // threads are correctly cleaned
157 exceptionOccurred = true;
158 }
159
160 // Now, the parent thread calls this->SingleMethod() itself
161 //
162 //
163 try
164 {
165 m_ThreadInfoArray[0].UserData = m_SingleData;
166 m_ThreadInfoArray[0].NumberOfWorkUnits = m_NumberOfWorkUnits;
167 m_SingleMethod( (void *)( &m_ThreadInfoArray[0] ) );
168 }
169 catch( ProcessAborted & )
170 {
171 // Need cleanup and rethrow ProcessAborted
172 // close down other threads
173 for( thread_loop = 1; thread_loop < m_NumberOfWorkUnits; ++thread_loop )
174 {
175 try
176 {
177
178 this->SpawnWaitForSingleMethodThread(process_id[thread_loop]);
179
180 }
181 catch( ... )
182 {
183 }
184 }
185 // rethrow
186 throw;
187 }
188 catch( std::exception & e )
189 {
190 // get the details of the exception to rethrow them
191 exceptionDetails = e.what();
192 // if this method fails, we must make sure all threads are
193 // correctly cleaned
194 exceptionOccurred = true;
195 }
196 catch( ... )
197 {
198 // if this method fails, we must make sure all threads are
199 // correctly cleaned
200 exceptionOccurred = true;
201 }
202 // The parent thread has finished this->SingleMethod() - so now it
203 // waits for each of the other processes to exit
204 for( thread_loop = 1; thread_loop < m_NumberOfWorkUnits; ++thread_loop )
205 {
206 try
207 {
208
209 this->SpawnWaitForSingleMethodThread(process_id[thread_loop]);
210
211 if( m_ThreadInfoArray[thread_loop].ThreadExitCode
212 != WorkUnitInfo::SUCCESS )
213 {
214 exceptionOccurred = true;
215 }
216 }
217 catch( std::exception & e )
218 {
219 // get the details of the exception to rethrow them
220 exceptionDetails = e.what();
221 exceptionOccurred = true;
222 }
223 catch( ... )
224 {
225 exceptionOccurred = true;
226 }
227 }
228
229 if( exceptionOccurred )
230 {
231 if( exceptionDetails.empty() )
232 {
233 itkExceptionMacro("Exception occurred during SingleMethodExecute");
234 }
235 else
236 {
237 itkExceptionMacro(<< "Exception occurred during SingleMethodExecute" << std::endl << exceptionDetails);
238 }
239 }
240 }
241
242 // Print method for the multithreader
PrintSelf(std::ostream & os,Indent indent) const243 void PlatformMultiThreader::PrintSelf(std::ostream & os, Indent indent) const
244 {
245 Superclass::PrintSelf(os, indent);
246 }
247
248 }
249