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 
30 #include "itkObjectFactory.h"
31 #include "itksys/SystemTools.hxx"
32 #include <unistd.h>
33 
34 #ifdef __APPLE__
35 #include <sys/types.h>
36 #include <sys/sysctl.h>
37 #endif
38 
39 namespace itk
40 {
41 extern "C"
42 {
43 typedef void *( *c_void_cast )(void *);
44 }
45 
46 #if !defined ( ITK_LEGACY_REMOVE )
MultipleMethodExecute()47 void PlatformMultiThreader::MultipleMethodExecute()
48 {
49 
50   pthread_t process_id[ITK_MAX_THREADS];
51 
52   // obey the global maximum number of threads limit
53   if( m_NumberOfWorkUnits > MultiThreaderBase::GetGlobalMaximumNumberOfThreads() )
54     {
55     m_NumberOfWorkUnits = MultiThreaderBase::GetGlobalMaximumNumberOfThreads();
56     }
57   for( ThreadIdType thread_loop = 0; thread_loop < m_NumberOfWorkUnits; ++thread_loop )
58     {
59     if( m_MultipleMethod[thread_loop] == (ThreadFunctionType)nullptr )
60       {
61       itkExceptionMacro(<< "No multiple method set for: " << thread_loop);
62       return;
63       }
64     }
65 
66   // Using POSIX threads
67   //
68   // We want to use pthread_create to start m_NumberOfWorkUnits - 1
69   // additional
70   // threads which will be used to call the NumberOfWorkUnits-1 methods
71   // defined in m_MultipleMethods[](). The parent thread
72   // will call m_MultipleMethods[NumberOfWorkUnits-1]().  When it is done,
73   // it will wait for all the children to finish.
74   //
75   // First, start up the m_NumberOfWorkUnits-1 processes.  Keep track
76   // of their process ids for use later in the pthread_join call
77 
78   pthread_attr_t attr;
79 
80   pthread_attr_init(&attr);
81 #ifndef __CYGWIN__
82   pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS);
83 #endif
84   for( ThreadIdType thread_loop = 1; thread_loop < m_NumberOfWorkUnits; ++thread_loop )
85     {
86     m_ThreadInfoArray[thread_loop].UserData =
87       m_MultipleData[thread_loop];
88     m_ThreadInfoArray[thread_loop].NumberOfWorkUnits = m_NumberOfWorkUnits;
89     int threadError = pthread_create( &( process_id[thread_loop] ),
90                                       &attr, reinterpret_cast<c_void_cast>( m_MultipleMethod[thread_loop] ),
91                                       ( (void *)( &m_ThreadInfoArray[thread_loop] ) ) );
92     if( threadError != 0 )
93       {
94       itkExceptionMacro(<< "Unable to create a thread.  pthread_create() returned "
95                         << threadError);
96       }
97     }
98 
99   // Now, the parent thread calls the last method itself
100   m_ThreadInfoArray[0].UserData = m_MultipleData[0];
101   m_ThreadInfoArray[0].NumberOfWorkUnits = m_NumberOfWorkUnits;
102   ( m_MultipleMethod[0] )( (void *)( &m_ThreadInfoArray[0] ) );
103   // The parent thread has finished its method - so now it
104   // waits for each of the other processes to exit
105   for( ThreadIdType thread_loop = 1; thread_loop < m_NumberOfWorkUnits; ++thread_loop )
106     {
107     pthread_join(process_id[thread_loop], nullptr);
108     }
109 
110 }
111 
SpawnThread(ThreadFunctionType f,void * UserData)112 ThreadIdType PlatformMultiThreader::SpawnThread(ThreadFunctionType f, void *UserData)
113 {
114   ThreadIdType id = 0;
115 
116   while( id < ITK_MAX_THREADS )
117     {
118     if( !m_SpawnedThreadActiveFlagLock[id]  )
119       {
120       m_SpawnedThreadActiveFlagLock[id] = std::make_shared< std::mutex >();
121       }
122     m_SpawnedThreadActiveFlagLock[id]->lock();
123     if( m_SpawnedThreadActiveFlag[id] == 0 )
124       {
125       // We've got a useable thread id, so grab it
126       m_SpawnedThreadActiveFlag[id] = 1;
127       m_SpawnedThreadActiveFlagLock[id]->unlock();
128       break;
129       }
130     m_SpawnedThreadActiveFlagLock[id]->unlock();
131 
132     id++;
133     }
134 
135   if( id >= ITK_MAX_THREADS )
136     {
137     itkExceptionMacro(<< "You have too many active threads!");
138     }
139 
140   m_SpawnedThreadInfoArray[id].UserData        = UserData;
141   m_SpawnedThreadInfoArray[id].NumberOfWorkUnits = 1;
142   m_SpawnedThreadInfoArray[id].ActiveFlag = &m_SpawnedThreadActiveFlag[id];
143   m_SpawnedThreadInfoArray[id].ActiveFlagLock = m_SpawnedThreadActiveFlagLock[id];
144 
145   pthread_attr_t attr;
146 
147   pthread_attr_init(&attr);
148 #ifndef __CYGWIN__
149   pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS);
150 #endif
151 
152   int threadError = pthread_create( &( m_SpawnedThreadProcessID[id] ),
153                                     &attr, reinterpret_cast<c_void_cast>( f ),
154                                     ( (void *)( &m_SpawnedThreadInfoArray[id] ) ) );
155   if( threadError != 0 )
156     {
157     itkExceptionMacro(<< "Unable to create a thread.  pthread_create() returned "
158                       << threadError);
159     }
160 
161   return id;
162 }
163 
TerminateThread(ThreadIdType WorkUnitID)164 void PlatformMultiThreader::TerminateThread(ThreadIdType WorkUnitID)
165 {
166   if( !m_SpawnedThreadActiveFlag[WorkUnitID] )
167     {
168     return;
169     }
170 
171   m_SpawnedThreadActiveFlagLock[WorkUnitID]->lock();
172   m_SpawnedThreadActiveFlag[WorkUnitID] = 0;
173   m_SpawnedThreadActiveFlagLock[WorkUnitID]->unlock();
174 
175   pthread_join(m_SpawnedThreadProcessID[WorkUnitID], nullptr);
176 
177   m_SpawnedThreadActiveFlagLock[WorkUnitID] = nullptr;
178   m_SpawnedThreadActiveFlagLock[WorkUnitID] = nullptr;
179 }
180 #endif
181 
182 void
183 PlatformMultiThreader
SpawnWaitForSingleMethodThread(ThreadProcessIdType threadHandle)184 ::SpawnWaitForSingleMethodThread(ThreadProcessIdType threadHandle)
185 {
186   // Using POSIX threads
187   if ( pthread_join(threadHandle, nullptr) )
188     {
189     itkExceptionMacro(<< "Unable to join thread.");
190     }
191 }
192 
193 ThreadProcessIdType
194 PlatformMultiThreader
SpawnDispatchSingleMethodThread(PlatformMultiThreader::WorkUnitInfo * threadInfo)195 ::SpawnDispatchSingleMethodThread(PlatformMultiThreader::WorkUnitInfo *threadInfo)
196 {
197   // Using POSIX threads
198   pthread_attr_t attr;
199   pthread_t      threadHandle;
200 
201   pthread_attr_init(&attr);
202 #if !defined( __CYGWIN__ )
203   pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
204 #endif
205 
206   int threadError;
207   threadError =
208     pthread_create( &threadHandle, &attr, reinterpret_cast< c_void_cast >( this->SingleMethodProxy ),
209                     reinterpret_cast< void * >( threadInfo ) );
210   if ( threadError != 0 )
211     {
212     itkExceptionMacro(<< "Unable to create a thread.  pthread_create() returned "
213                       << threadError);
214     }
215   return threadHandle;
216 }
217 
218 } // end namespace itk
219