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