PFUNC 1.0
pfunc/thread.hpp
Go to the documentation of this file.
00001 #ifndef PFUNC_THREAD_HPP
00002 #define PFUNC_THREAD_HPP
00003   
00009 #include <pfunc/config.h>
00010 #include <pfunc/pfunc_common.h>
00011 #if PFUNC_HAVE_WINDOWS_THREADS == 1
00012 #include <Windows.h>
00013 #elif PFUNC_HAVE_PTHREADS == 1
00014 #include<pthread.h>
00015 #if PFUNC_HAVE_SCHED_AFFINITY == 1
00016 #include <sched.h>
00017 #endif
00018 #else
00019 #error "Windows threads or pthreads are required"
00020 #endif
00021 
00022 #include <pfunc/no_copy.hpp>
00023 #include <pfunc/exception.hpp>
00024 #include <pfunc/mutex.hpp>
00025 
00026 #if PFUNC_HAVE_TLS == 1
00027 #include <vector>
00028 __thread volatile unsigned int pfunc_thread_self_id;
00029 #elif PFUNC_HAVE_HASH_MAP_H == 1
00030 #include <ext/hash_map>
00031 namespace std { using namespace __gnu_cxx; }
00032 #else
00033 #include <map>
00034 #endif
00035 
00036 #if PFUNC_DARWIN == 1 && PFUNC_HAVE_SYSCTL_H == 1
00037 #include <sys/sysctl.h>
00038 #endif
00039 
00040 namespace pfunc { namespace detail {
00041   static const unsigned int  PFUNC_STACK_MIN = 2048*2048;
00042   static const unsigned int  PFUNC_STACK_MAX = 4096*4096;
00043   static const unsigned int  PFUNC_STACK_AVG = 2048*4096;
00044   static const unsigned int  PFUNC_NO_AFFINITY = ~0x0;
00045 
00054   struct thread_attr : no_copy {
00055     private:
00056     const unsigned int stack_size; 
00057     const unsigned int thread_id; 
00058     const unsigned int thread_affinity; 
00059     const unsigned int task_queue_number; 
00060     void* user_data; 
00062     public:
00073     thread_attr (const unsigned int& stack_size,
00074                  const unsigned int& thread_id,
00075                  const unsigned int& thread_affinity,
00076                  const unsigned int& task_queue_number)  :
00077                             stack_size (stack_size),
00078                             thread_id (thread_id),
00079                             thread_affinity (thread_affinity),
00080                             task_queue_number (task_queue_number) {}
00081  
00085     unsigned int get_stack_size () const  { return stack_size; }
00086 
00090     unsigned int get_thread_id () const   { return thread_id; }
00091 
00095     unsigned int get_thread_affinity () const  {return thread_affinity; }
00096 
00100     unsigned int get_task_queue_number () const  {
00101       return task_queue_number;
00102     }
00103   };
00104 
00105 /*****************************************************************************************/
00112   struct thread : public no_copy {
00113 #if PFUNC_HAVE_WINDOWS_THREADS == 1
00114     typedef HANDLE thread_handle_type;
00115     typedef DWORD (__stdcall *start_func_type)(void*); 
00116     typedef DWORD native_thread_id_type; 
00117 #elif PFUNC_HAVE_PTHREADS == 1
00118     typedef pthread_t thread_handle_type; 
00119     typedef void* (*start_func_type)(void*); 
00120     typedef pthread_t native_thread_id_type; 
00121 #endif
00122 
00123 #if PFUNC_HAVE_TLS == 1
00124     typedef std::vector<thread_attr*> tls_attribute_map_type; 
00125 #elif PFUNC_HAVE_HASH_MAP_H == 1
00126 
00130     struct my_hash_function {
00131       typedef size_t result_type; 
00132       std::hash<size_t> my_hash; 
00142       result_type operator () (const native_thread_id_type& key) const {
00143 #if PFUNC_DARWIN == 1
00144         return my_hash(reinterpret_cast<size_t>(key));
00145 #else
00146         return my_hash(key);
00147 #endif /* if defined(__DARWIN__) */
00148       }
00149     };
00150     typedef std::hash_map <native_thread_id_type, 
00151                            thread_attr*, 
00152                            my_hash_function> tls_attribute_map_type; 
00153 #else
00154     typedef std::map <native_thread_id_type, thread_attr*> 
00155                                              tls_attribute_map_type; 
00156 #endif
00157 
00158     typedef tls_attribute_map_type::value_type tls_value_type; 
00159     tls_attribute_map_type tls_attr_map; 
00161 #if PFUNC_HAVE_TLS == 1
00162 
00167     void initialize (const unsigned int& nthreads) {
00168       tls_attr_map.resize (nthreads);
00169     }
00170 #endif
00171 
00181     void create_thread (thread_handle_type& handle,
00182                         thread_attr* attr,
00183                         start_func_type start_func_ptr,
00184                         void* start_func_arg)  {
00185 
00186 #if PFUNC_USE_EXCEPTIONS == 1
00187 
00190       if (NULL == attr)
00191         throw exception_generic_impl ("pfunc::detail::thread::create_thread",
00192                                       "NULL attribute provided",
00193                                       PFUNC_INVALID_ATTR);
00194 
00196       if (NULL == start_func_ptr)
00197         throw exception_generic_impl ("pfunc::detail::thread::create_thread",
00198                                       "NULL start function for thread",
00199                                       PFUNC_INVALID_ARGUMENTS);
00200 
00202       if (NULL == start_func_arg)
00203         throw exception_generic_impl ("pfunc::detail::thread::create_thread",
00204                                       "NULL argument for thread start function",
00205                                       PFUNC_INVALID_ARGUMENTS);
00206 #endif
00207 
00208 #if PFUNC_HAVE_WINDOWS_THREADS == 1
00209       handle = CreateThread (NULL, /* Security Attributes */
00210                              attr->get_stack_size(), /* dwStackSize */
00211                              start_func_ptr, /* StartRoutine */
00212                              start_func_arg, /* Function Argument */
00213                              0, /* CreationFlags */
00214                              NULL); /* ThreadId */
00215 #if PFUNC_USE_EXCEPTIONS == 1
00216 
00217       if (NULL == handle)
00218         throw exception_generic_impl
00219                   ("pfunc::detail::thread::create_thread::CreateThread",
00220                    "Could not create the thread",
00221                    GetLastError ());
00222 #endif
00223 
00224 #elif PFUNC_HAVE_PTHREADS == 1
00225       error_code_type error;
00226 
00227       /* The only thing that we need to set is the stacksize */
00228       pthread_attr_t my_attr;
00229 
00230       /* Iniitalize the attribute */
00231       error = pthread_attr_init (&my_attr);
00232 #if PFUNC_USE_EXCEPTIONS == 1
00233       if (error)
00234         throw exception_generic_impl
00235                 ("pfunc::detail::thread::create_thread::pthread_attr_init",
00236                  "Could not initialize the attribute",
00237                  error);
00238 #endif
00239      
00240       size_t stack_size = static_cast <size_t> (attr->get_stack_size());
00241       error = pthread_attr_setstacksize (&my_attr, stack_size);
00242 #if PFUNC_USE_EXCEPTIONS == 1
00243       if (error)
00244         throw exception_generic_impl
00245                 ("pfunc::detail::thread::create_thread::pthread_attr_init",
00246                  "Could not set the stacksize",
00247                  error);
00248 #endif
00249      
00250       error = pthread_attr_setscope (&my_attr, PTHREAD_SCOPE_SYSTEM);
00251 #if PFUNC_USE_EXCEPTIONS == 1
00252       if (error)
00253         throw exception_generic_impl
00254                 ("pfunc::detail::thread::create_thread::pthread_attr_init",
00255                  "Could not set thread scope",
00256                  error);
00257 #endif
00258 
00259       /* Create the thread now */
00260       error = pthread_create 
00261                     (&handle, &my_attr, start_func_ptr, start_func_arg);
00262 #if PFUNC_USE_EXCEPTIONS == 1
00263       if (error)
00264         throw exception_generic_impl
00265                 ("pfunc::detail::thread::create_thread::pthread_attr_init",
00266                  "Could not create the thread",
00267                  error);
00268 #endif
00269      
00270       /* Free the attribute */
00271       error = pthread_attr_destroy (&my_attr);
00272 #if PFUNC_USE_EXCEPTIONS == 1
00273       if (error)
00274         throw exception_generic_impl
00275                 ("pfunc::detail::thread::create_thread::pthread_attr_init",
00276                  "Could not destroy the attribute",
00277                  error);
00278 #endif
00279 
00280 #else
00281 #error "Windows threads or pthreads are required"
00282 #endif
00283     }
00284 
00288     void exit_thread () const  {
00289 #if PFUNC_HAVE_WINDOWS_THREADS
00290       ExitThread (0);
00291 #elif PFUNC_HAVE_PTHREADS
00292       pthread_exit (NULL);
00293 #else
00294 #error "Windows threads or pthreads are required"
00295 #endif
00296     }
00297 
00303     void join_thread (const thread_handle_type& handle) const  {
00304 #if PFUNC_HAVE_WINDOWS_THREADS == 1
00305       WaitForSingleObject (handle, INFINITE);
00306 #elif PFUNC_HAVE_PTHREADS == 1
00307       PFUNC_CAPTURE_RETURN_VALUE(error) pthread_join (handle, NULL);
00308 #if PFUNC_USE_EXCEPTIONS == 1
00309       if (error)
00310         throw exception_generic_impl
00311                 ("pfunc::detail::thread::join_thread::pthread_join",
00312                  "Could not join on the thread",
00313                  error);
00314 #endif
00315 #else
00316 #error "Windows threads or pthreads are required"
00317 #endif
00318     }
00319 
00329     native_thread_id_type get_native_id(const thread_handle_type& handle)const{ 
00330 #if PFUNC_HAVE_WINDOWS_THREADS == 1
00331       return GetThreadId (handle);
00332 #elif PFUNC_HAVE_PTHREADS == 1
00333       return handle;
00334 #else
00335 #error "Windows threads or pthreads are required"
00336 #endif
00337     }
00338  
00344     native_thread_id_type get_native_id () const  { 
00345 #if PFUNC_HAVE_WINDOWS_THREADS == 1
00346       return GetCurrentThreadId ();
00347 #elif PFUNC_HAVE_PTHREADS == 1
00348       return pthread_self (); 
00349 #else
00350 #error "Windows threads or pthreads are required"
00351 #endif
00352     }
00353  
00359     void tls_set (thread_attr* attr)  {
00360 #if PFUNC_HAVE_TLS == 1
00361       tls_attr_map[pfunc_thread_self_id] = attr;
00362 #else
00363       tls_attr_map [get_native_id()] = attr; 
00364 #endif
00365     }
00366 
00372     thread_attr* tls_get ()  { 
00373 #if PFUNC_HAVE_TLS == 1
00374       return tls_attr_map [pfunc_thread_self_id];
00375 #else
00376       return tls_attr_map [get_native_id ()];
00377 #endif 
00378     }
00379 
00380 #if PFUNC_HAVE_SCHED_AFFINITY == 1 && PFUNC_HAVE_SCHED_H == 1
00381 
00387     int get_num_procs () const  {
00388       int num_procs = 0;
00389       error_code_type error;
00390       cpu_set_t orig_set;
00391       cpu_set_t my_set;
00392       if (0 > (error=sched_getaffinity (0, sizeof(my_set), &orig_set))) 
00393 #if PFUNC_USE_EXCEPTIONS == 1
00394         throw exception_generic_impl
00395                 ("pfunc::detail::thread::get_num_procs:",
00396                  "Could not get the current thread affinities",
00397                  error);
00398 #else
00399         return num_procs;
00400 #endif
00401       for (num_procs=0; ;++num_procs) {
00402         CPU_ZERO (&my_set);
00403         CPU_SET(num_procs, &my_set);
00404         if (0 > sched_setaffinity (0, sizeof(my_set), &my_set)) break;
00405       }
00406       error = sched_setaffinity (0, sizeof(my_set), &orig_set);
00407 #if PFUNC_USE_EXCEPTIONS == 1
00408       if (error)
00409         throw exception_generic_impl
00410                 ("pfunc::detail::thread::get_num_procs:",
00411                  "Could not reset the affinities back to the original",
00412                  error);
00413 #endif
00414         return num_procs;
00415     }
00416 
00422     void set_affinity (const int& proc_id) const  {
00423       error_code_type error;
00424       cpu_set_t my_set;
00425       CPU_ZERO (&my_set);
00426       CPU_SET (proc_id, &my_set);
00427       error = sched_setaffinity (0, sizeof(my_set), &my_set);
00428 #if PFUNC_USE_EXCEPTIONS == 1
00429         if (error)
00430           throw exception_generic_impl
00431                 ("pfunc::detail::thread::get_num_procs:",
00432                  "Could not set thread affinity",
00433                  error);
00434 #endif
00435     }
00436 
00437 #elif PFUNC_DARWIN == 1 && PFUNC_HAVE_SYSCTL_H == 1
00438 
00444     int get_num_procs () const  {
00445       int num_procs = 0;
00446       error_code_type error;
00447       int mib[] = {CTL_HW, HW_NCPU};
00448       size_t len = sizeof(int);
00449       error = sysctl(mib, 2, &num_procs, &len, NULL, 0);
00450 #if PFUNC_USE_EXCEPTIONS == 1
00451       if (error)
00452         throw exception_generic_impl
00453                 ("pfunc::detail::thread::get_num_procs:",
00454                  "Could not get the number of processors",
00455                  error);
00456 #endif
00457       return num_procs;
00458     }
00459 
00465     void set_affinity (const int& proc_id) const  {
00467     }
00468 
00469 #elif PFUNC_WINDOWS == 1
00470 
00476     int get_num_procs () const  {
00477       int num_procs = 0;
00478       error_code_type error;
00479       num_procs = error = GetActiveProcessorCount (ALL_PROCESSOR_GROUPS);
00480 #if PFUNC_USE_EXCEPTIONS == 1
00481     if (0 == error)
00482         throw exception_generic_impl
00483                 ("pfunc::detail::thread::get_num_procs:",
00484                  "Could not get the number of processors",
00485                  error);
00486 #endif
00487     }
00488 
00494     void set_affinity (const int& proc_id) const  {
00496     }
00497 
00498 #else /* NONE of the systems that we know of */
00499 
00506     int get_num_procs () const {
00507       return 0;
00508     }
00509 
00515     void set_affinity (const int& proc_id) const  {
00517     }
00518 #endif
00519 
00520 
00524     void yield () {
00525 #if PFUNC_LINUX == 1 
00526       pthread_yield();
00527 #elif PFUNC_DARWIN == 1
00528       pthread_yield_np();
00529 #elif PFUNC_WINDOWS == 1
00530       SwitchToThread();
00531 #elif PFUNC_AIX == 1 && PFUNC_HAVE_SCHED_H
00532       sched_yield();
00533 #else
00534 
00535 #endif
00536     }
00537   };
00538 } /* namespace detail */ } /* namespace pfunc */
00539 
00540 #endif // PFUNC_THREAD_HPP