blocxx
ThreadOnce.hpp
Go to the documentation of this file.
1 /*******************************************************************************
2 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
3 * Copyright (C) 2006, Novell, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of
14 * Vintela, Inc.,
15 * nor Novell, Inc.,
16 * nor the names of its contributors or employees may be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *******************************************************************************/
32 
33 
38 #ifndef BLOCXX_THREAD_ONCE_HPP_INCLUDE_GUARD_
39 #define BLOCXX_THREAD_ONCE_HPP_INCLUDE_GUARD_
40 #include "blocxx/BLOCXX_config.h"
41 #include "blocxx/Assertion.hpp"
42 
43 #if defined(BLOCXX_USE_PTHREAD)
44 #include <pthread.h>
45 #include <csignal> // for sig_atomic_t
46 #include <cassert>
47 #include "blocxx/MemoryBarrier.hpp"
48 #elif defined(BLOCXX_WIN32)
49 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
50 #include <wtypes.h>
51 #endif
52 
53 namespace BLOCXX_NAMESPACE
54 {
55 
56 #ifdef BLOCXX_NCR
57 #ifndef PTHREAD_MUTEX_INITIALIZER
58 #define PTHREAD_MUTEX_INITIALIZER {NULL, 0, 0}
59 #endif
60 #endif
61 
62 #if defined(BLOCXX_USE_PTHREAD)
63 
64 struct OnceFlag
65 {
66  volatile ::sig_atomic_t flag;
67  ::pthread_mutex_t mtx;
68 };
69 
70 #define BLOCXX_ONCE_INIT {0, PTHREAD_MUTEX_INITIALIZER}
71 
72 #elif defined(BLOCXX_WIN32)
73 
74 typedef long OnceFlag;
75 #define BLOCXX_ONCE_INIT 0
76 
77 #else
78 #error "Port me!"
79 #endif
80 
85 template <typename FuncT>
86 void BLOCXX_COMMON_API callOnce(OnceFlag& flag, FuncT F);
87 
88 
89 
90 #if defined(BLOCXX_USE_PTHREAD)
91 
92 class CallOnce_pthread_MutexLock
93 {
94 public:
95  CallOnce_pthread_MutexLock(::pthread_mutex_t* mtx)
96  : m_mtx(mtx)
97  {
98 #ifdef BLOCXX_NCR //we get coredump without initialization
99  if (m_mtx->field1 == NULL)
100  {
101  pthread_mutexattr_t attr;
102  int ret = pthread_mutexattr_create(&attr);
103  assert(ret == 0);
104  ret = pthread_mutex_init(m_mtx, attr);
105  assert(ret == 0);
106  pthread_mutexattr_delete(&attr);
107  }
108 #endif
109 
110 #ifndef NDEBUG
111  int res =
112 #endif // Avoid unused variable warnings
113  pthread_mutex_lock(m_mtx);
114  assert(res == 0);
115  }
116  ~CallOnce_pthread_MutexLock()
117  {
118 #ifndef NDEBUG
119  int res =
120 #endif // Avoid unused variable warnings
121  pthread_mutex_unlock(m_mtx);
122  assert(res == 0);
123 
124 #ifdef BLOCXX_NCR
125  pthread_mutex_destroy(m_mtx);
126 #endif
127  }
128 private:
129  ::pthread_mutex_t* m_mtx;
130 };
131 
132 template <typename FuncT>
133 inline void callOnce(OnceFlag& flag, FuncT f)
134 {
136  if (flag.flag == 0)
137  {
138  CallOnce_pthread_MutexLock lock(&flag.mtx);
139  if (flag.flag == 0)
140  {
141  f();
142  flag.flag = 1;
143  }
144  }
145 }
146 
147 #endif
148 
149 #if defined(BLOCXX_WIN32)
150 
151 template <typename FuncT>
152 inline void callOnce(OnceFlag& flag, FuncT f)
153 {
154  // this is the double-checked locking pattern, but with a bit more strength than normally implemented :-)
155  if (InterlockedCompareExchange(&flag, 1, 1) == 0)
156  {
157  wchar_t mutexName[MAX_PATH];
158  _snwprintf(mutexName, MAX_PATH, L"%X-%p-587ccea9-c95a-4e81-ac51-ab0ddc6cef63", GetCurrentProcessId(), &flag);
159  mutexName[MAX_PATH - 1] = 0;
160 
161  HANDLE mutex = CreateMutexW(NULL, FALSE, mutexName);
162  BLOCXX_ASSERT(mutex != NULL);
163 
164  int res = 0;
165  res = WaitForSingleObject(mutex, INFINITE);
166  BLOCXX_ASSERT(res == WAIT_OBJECT_0);
167 
168  if (InterlockedCompareExchange(&flag, 1, 1) == 0)
169  {
170  try
171  {
172  f();
173  }
174  catch (...)
175  {
176  res = ReleaseMutex(mutex);
177  BLOCXX_ASSERT(res);
178  res = CloseHandle(mutex);
179  BLOCXX_ASSERT(res);
180  throw;
181  }
182  InterlockedExchange(&flag, 1);
183  }
184 
185  res = ReleaseMutex(mutex);
186  BLOCXX_ASSERT(res);
187  res = CloseHandle(mutex);
188  BLOCXX_ASSERT(res);
189  }
190 }
191 
192 #endif
193 
194 } // end namespace BLOCXX_NAMESPACE
195 
196 #endif
197