PFUNC  1.0
pfunc/mutex.hpp
Go to the documentation of this file.
00001 #ifndef PFUNC_MUTEX_HPP
00002 #define PFUNC_MUTEX_HPP
00003   
00010 #include <pfunc/config.h>
00011 #include <pfunc/pfunc_common.h>
00012 #include <pfunc/no_copy.hpp>
00013 #include <pfunc/exception.hpp>
00014 #include <pfunc/pfunc_atomics.h>
00015 #include <pfunc/environ.hpp>
00016 
00017 #if PFUNC_HAVE_ERRNO_H == 1
00018 #include <errno.h>
00019 #endif
00020 
00021 #if PFUNC_HAVE_FUTEX == 1
00022 #include <pfunc/futex.h>
00023 
00024 /*
00025  * TODO: There is an assumption here that int is 32-bits. Need to change this
00026  */
00027 
00028 namespace pfunc {
00029   struct mutex : public detail::no_copy {
00030     private:
00031     ALIGN64 int val;
00032     static const int PFUNC_MUTEX_FREE = 0x0;
00033     static const int PFUNC_MUTEX_LOCKED = 0x1;
00034     static const int PFUNC_MUTEX_LOCKED_WITH_WAITERS = 0x2;
00035     static const int PFUNC_MAX_RELAXATIONS = 2*1024*1024; /* 2M */
00036 
00037     public:
00038     mutex () : val (PFUNC_MUTEX_FREE) {}
00039 
00040     void lock () {
00041       if (PFUNC_MUTEX_FREE != pfunc_compare_and_swap_32 (&val, 
00042                                               PFUNC_MUTEX_LOCKED, 
00043                                               PFUNC_MUTEX_FREE)) {
00044         do {
00045           int oldval = pfunc_compare_and_swap_32 (&val, 
00046                                        PFUNC_MUTEX_LOCKED_WITH_WAITERS, 
00047                                        PFUNC_MUTEX_LOCKED);
00048           if (PFUNC_MUTEX_FREE != oldval) {
00050             for (int i = 0; i < PFUNC_MAX_RELAXATIONS; i++) {
00051               if (PFUNC_MUTEX_LOCKED_WITH_WAITERS == val) cpu_relax ();
00052               else break;
00053             }
00055             futex_wait (reinterpret_cast<int*>(&val), 
00056                         PFUNC_MUTEX_LOCKED_WITH_WAITERS);
00057           }
00058         } while (PFUNC_MUTEX_FREE != pfunc_compare_and_swap_32 (&val, 
00059                                            PFUNC_MUTEX_LOCKED_WITH_WAITERS, 
00060                                            PFUNC_MUTEX_FREE));
00061       }
00062     }
00063 
00064     bool trylock () {
00065       if (PFUNC_MUTEX_FREE != pfunc_compare_and_swap_32 (&val, 
00066                                           PFUNC_MUTEX_LOCKED, 
00067                                           PFUNC_MUTEX_FREE)) return true;
00068       else return false;
00069     }
00070 
00071     void unlock () { 
00072       int oldval = pfunc_fetch_and_store_32 (&val, PFUNC_MUTEX_FREE);
00073       if (oldval <= PFUNC_MUTEX_LOCKED) return;
00074       else futex_wake (&val, PFUNC_MUTEX_LOCKED); 
00075     }
00076   };
00077 } /* namespace pfunc */
00078 
00079 #elif PFUNC_HAVE_PTHREADS == 1
00080 
00081 namespace pfunc {
00082   struct mutex : public detail::no_copy {
00083     typedef pthread_mutex_t mutex_type; /*< Mutex type */
00084     mutex_type mtx; 
00089     mutex () {
00090       PFUNC_CAPTURE_RETURN_VALUE(error) pthread_mutex_init (&mtx, NULL);
00091 #if PFUNC_USE_EXCEPTIONS == 1
00092       if (error)
00093         throw exception_generic_impl
00094     ("pfunc::detail::mutex::mutex::pthread_mutex_init at " FILE_AND_LINE(),
00095      "Error initializing the mutex",
00096      error);
00097 #endif
00098     }
00099 
00103     ~mutex () {
00104       PFUNC_CAPTURE_RETURN_VALUE(error) pthread_mutex_destroy (&mtx);
00105 #if PFUNC_USE_EXCEPTIONS == 1
00106       if (error)
00107         throw exception_generic_impl
00108     ("pfunc::detail::mutex::mutex::pthread_mutex_destroy at " FILE_AND_LINE(),
00109      "Error destroying the mutex",
00110      error);
00111 #endif
00112     }
00113 
00117     void lock () {
00118       PFUNC_CAPTURE_RETURN_VALUE(error) pthread_mutex_lock (&mtx);
00119 #if PFUNC_USE_EXCEPTIONS == 1
00120       if (error)
00121         throw exception_generic_impl
00122     ("pfunc::detail::mutex::mutex::pthread_mutex_lock at " FILE_AND_LINE(),
00123      "Error locking the mutex",
00124      error);
00125 #endif
00126     }
00127 
00134     bool trylock () {
00135       error_code_type error = pthread_mutex_lock (&mtx);
00136       bool ret_val = false;
00137       if (0 == error) ret_val = true;
00139 #if PFUNC_HAVE_ERRNO_H == 1
00140       else if (EBUSY == error) return false;
00141 #if PFUNC_USE_EXCEPTIONS == 1
00142       else
00143         throw exception_generic_impl
00144     ("pfunc::detail::mutex::mutex::pthread_mutex_trylock at " FILE_AND_LINE(),
00145      "Error trying to lock the mutex",
00146      error);
00147 #endif
00148 #endif
00149       return ret_val;
00150     }
00151 
00155     void unlock () {
00156       PFUNC_CAPTURE_RETURN_VALUE(error) pthread_mutex_unlock (&mtx);
00157 #if PFUNC_USE_EXCEPTIONS == 1
00158       if (error)
00159         throw exception_generic_impl
00160     ("pfunc::detail::mutex::mutex::pthread_mutex_unlock at " FILE_AND_LINE(),
00161      "Error unlocking the mutex",
00162      error);
00163 #endif
00164     }
00165 
00171     mutex_type& get_internal_mutex ()  { return mtx; }
00172   };
00173 
00174 } /* namespace pfunc */
00175 
00176 #elif PFUNC_HAVE_WINDOWS_THREADS == 1
00177 
00178 namespace pfunc {
00179   struct mutex : public detail::no_copy {
00180     typedef HANDLE  mutex_type; /*< Mutex type */
00181     mutex_type mtx; 
00186     mutex () {
00187       mtx = CreateMutex (NULL, /* No security */
00188                          FALSE,/* Unowned */
00189                          NULL);/* Unnamed */
00190 #if PFUNC_USE_EXCEPTIONS == 1
00191       if (NULL == mtx)
00192         throw exception_generic_impl
00193       ("pfunc::detail::mutex::mutex::CreateMutex at " FILE_AND_LINE(),
00194        "Error initializing the mutex",
00195        GetLastError());
00196 #endif
00197     }
00198 
00202     ~mutex () {
00203       BOOL ret_val = CloseHandle(mtx);
00204 #if PFUNC_USE_EXCEPTIONS == 1
00205       if (FALSE == ret_val) 
00206         throw exception_generic_impl
00207       ("pfunc::detail::mutex::mutex::CloseHandle at " FILE_AND_LINE(),
00208        "Error destroying the mutex",
00209        GetLastError());
00210 #endif
00211     }
00212 
00216     void lock () {
00217       error_code_type error = WaitForSingleObject (mtx, INFINITE);
00218 #if PFUNC_USE_EXCEPTIONS == 1
00219       if (WAIT_OBJECT_0 != error) 
00220         throw exception_generic_impl 
00221   ("pfunc::detail::mutex::lock::WaitForSingleObject at " FILE_AND_LINE(),
00222    "Error locking the mutex",
00223    GetLastError());
00224 #endif
00225     }
00226 
00233     bool trylock () {
00234       DWORD ret_val = WaitForSingleObject (mtx, 0);
00235       if (ret_val == WAIT_OBJECT_0) return true;
00236       else if (ret_val == WAIT_TIMEOUT) return false;
00237       else {
00238 #if PFUNC_USE_EXCEPTIONS == 1
00239 
00240       throw exception_generic_impl 
00241  ("pfunc::detail::mutex::lock::WaitForSingleObject at " FILE_AND_LINE(),
00242   "Error locking the mutex",
00243   GetLastError());
00244 #else
00245 
00246       return false;
00247 #endif
00248       }
00249     }
00250 
00254     void unlock () {
00255       BOOL ret_val = ReleaseMutex (mtx);
00256 #if PFUNC_USE_EXCEPTIONS == 1
00257       if (FALSE == ret_val)
00258         throw exception_generic_impl 
00259   ("pfunc::detail::mutex::lock::ReleaseMutex at " FILE_AND_LINE(),
00260    "Error unlocking the mutex",
00261    GetLastError());
00262 #endif
00263     }
00264 
00268     mutex_type& get_internal_mutex ()  { return mtx; }
00269   };
00270 } /* namespace pfunc */
00271 #else
00272 #error "pthreads, windows threads or futexes are required"
00273 #endif 
00274 
00275 #endif // PFUNC_MUTEX_HPP