OLD | NEW |
| (Empty) |
1 // Copyright 2004-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 // | |
16 // Defines template class used to share data | |
17 // between processes. | |
18 // | |
19 | |
20 #ifndef OMAHA_COMMON_SHARED_MEMORY_PTR_H__ | |
21 #define OMAHA_COMMON_SHARED_MEMORY_PTR_H__ | |
22 | |
23 #include "base/debug.h" | |
24 #include "base/singleton.h" | |
25 #include "base/system_info.h" | |
26 #include "base/vista_utils.h" | |
27 | |
28 namespace omaha { | |
29 | |
30 // SharedMemoryPtr class is designed to allow seamless data sharing process boun
daries. | |
31 // All the data passed as a template parameter will be shared between processes. | |
32 // Very important to remember that for now - all shared data should be on stack. | |
33 // For example if the class A had stl vector as a member, the members of the vec
tor | |
34 // would be allocated not from shared memory and therefore will not be shared. | |
35 // That could be solved with allocators, but for now we don't need that. | |
36 // | |
37 // Here is a typical example of usage: | |
38 // Class A { | |
39 // int i_; | |
40 // double d_; | |
41 // ....... | |
42 // ........ | |
43 // | |
44 // public: | |
45 // set_double(double d){d_=d;} | |
46 // double get_double(){return d_}; | |
47 // | |
48 // }; | |
49 // | |
50 // ... Prosess one... | |
51 // SharedMemoryPtr<A> spA("ABC"); | |
52 // if (!spA) | |
53 // return false; | |
54 // | |
55 // spA->set_double(3.14); | |
56 // | |
57 // ... Process two ... | |
58 // | |
59 // | |
60 // SharedMemoryPtr<A> spA1("ABC"); | |
61 // if (!spA1) | |
62 // return false; | |
63 // | |
64 // process two will see the value set by process one. | |
65 // it will be 3.14 | |
66 // double d = spA1->get_double(); | |
67 // | |
68 | |
69 // You should implement a class member of SystemSharedData if the data you want | |
70 // to share is several hundred bytes. Always try this approach first before you
implement | |
71 // new class that is derived from SharedMemoryPtr. The main difference is that S
haredMemoryPtr | |
72 // will allocate a least page of shared memory. If your class is just a member o
f SystemSharedData | |
73 // memory mapped file will be shared between members. It is just more efficient. | |
74 // Look in system_shared_data.h , shared_data_member.h, and system_shared_data_m
embers.h | |
75 // for more details. | |
76 | |
77 // Forward declaration. | |
78 template <typename LockType, typename T> class SharedMemoryPtr; | |
79 | |
80 // During several code reviews it has been noticed that the same error gets repe
ated over and over. | |
81 // People create SharedMemoryPtr<SomeData>. And than the access to member functi
ons of SomeData | |
82 // is not synchronized by __mutexBlock or __mutexScope. So we need to somehow fi
nd a way to make | |
83 // automatic syncronization whenever people access shared data methods or membe
rs. | |
84 // Since by design the only way we can acess shared data is through operator ->
of SharedMemoryPtr | |
85 // we need to somehow invoke synchronization at the time of access. | |
86 // We can implement this mainly because of the mechanics of operator-> dictated
by C++ standard. | |
87 // When you apply operator-> to a type that's not a built-in pointer, the compil
er does an interesting thing. | |
88 // After looking up and applying the user-defined operator-> to that type, it ap
plies operator-> again to the result. | |
89 // The compiler keeps doing this recursively until it reaches a pointer to a bui
lt-in type, and only then proceeds with member access. | |
90 // It follows that a SharedMemoryPtr<T> operator-> does not have to return a poi
nter. | |
91 // It can return an object that in turn implements operator->, without changing
the use syntax. | |
92 // So we can implement: pre- and postfunction calls. (See Stroustrup 2000) | |
93 // If you return an object of some type X | |
94 // by value from operator->, the sequence of execution is as follows: | |
95 // 1. Constructor of type X | |
96 // 2. X::operator-> called; returns a pointer to an object of type T of SharedM
emoryPtr | |
97 // 3. Member access | |
98 // 4. Destructor of X | |
99 // In a nutshell, we have a way of implementing locked function calls. | |
100 | |
101 template <typename LockType, typename T> | |
102 class SharedDataLockingProxy { | |
103 public: | |
104 // Lock on construction. | |
105 SharedDataLockingProxy(SharedMemoryPtr<LockType, T> * mem_ptr, T* shared_data) | |
106 : mem_ptr_(mem_ptr), shared_data_(shared_data) { | |
107 mem_ptr_->Lock(); | |
108 } | |
109 // Unlock on destruction. | |
110 ~SharedDataLockingProxy() { | |
111 mem_ptr_->Unlock(); | |
112 } | |
113 // operator | |
114 T* operator->() const { | |
115 ASSERT(shared_data_ != NULL, (L"NULL object pointer being dereferenced")); | |
116 return shared_data_; | |
117 } | |
118 private: | |
119 SharedDataLockingProxy& operator=(const SharedDataLockingProxy&); | |
120 SharedMemoryPtr<LockType, T>* mem_ptr_; | |
121 T* shared_data_; | |
122 // To allow this implicit locking - copy constructor must be | |
123 // enabled. hence, no DISALLOW_EVIL_CONSTRUCTORS | |
124 }; | |
125 | |
126 template <typename LockType, typename T> class SharedMemoryPtr | |
127 : public LockType { | |
128 // Handle to disk file if we're backing this shared memory by a file | |
129 HANDLE file_; | |
130 // Local handle to file mapping. | |
131 HANDLE file_mapping_; | |
132 // pointer to a view. | |
133 T* data_; | |
134 // If the first time creation can do some initialization. | |
135 bool first_instance_; | |
136 public: | |
137 // The heart of the whole idea. Points to shared memrory | |
138 // instead of the beginning of the class. | |
139 SharedDataLockingProxy<LockType, T> operator->() { | |
140 return SharedDataLockingProxy<LockType, T>(this, data_); | |
141 } | |
142 // To check after creation. | |
143 // For example: | |
144 // SharedMemoryPtr<GLock, SomeClass> sm; | |
145 // if (sm) | |
146 // { do whatever you want} | |
147 // else | |
148 // {error reporting} | |
149 operator bool() const {return ((file_mapping_ != NULL) && (data_ != NULL));} | |
150 | |
151 // Initialize memory mapped file and sync mechanics. | |
152 // by calling InitializeSharedAccess | |
153 SharedMemoryPtr(const CString& name, | |
154 LPSECURITY_ATTRIBUTES sa, | |
155 LPSECURITY_ATTRIBUTES sa_mutex, | |
156 bool read_only) | |
157 : file_(INVALID_HANDLE_VALUE), | |
158 file_mapping_(NULL), | |
159 data_(NULL) { | |
160 HRESULT hr = InitializeSharedAccess(name, false, sa, sa_mutex, read_only); | |
161 if (FAILED(hr)) { | |
162 UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"), | |
163 name, read_only ? _T("R") : _T("RW"), hr)); | |
164 } | |
165 } | |
166 | |
167 // Use this constructor if you want to back the shared memory by a file. | |
168 // NOTE: if using a persistent shared memory, every object with this same | |
169 // name should be persistent. Otherwise, the objects marked as | |
170 // non-persistent will lead to InitializeSharedData called again if | |
171 // they are instantiated before the ones marked as persistent. | |
172 SharedMemoryPtr(bool persist, | |
173 LPSECURITY_ATTRIBUTES sa, | |
174 LPSECURITY_ATTRIBUTES sa_mutex, | |
175 bool read_only) | |
176 : file_(INVALID_HANDLE_VALUE), | |
177 file_mapping_(NULL), | |
178 data_(NULL) { | |
179 // Each shared data must implement GetFileName() to use this c-tor. The | |
180 // implementation should be: | |
181 // const CString GetFileName() const {return L"C:\\directory\file";} | |
182 // This is purposedly different from GetSharedName, so that the user is | |
183 // well aware that a file name is expected, not a mutex name. | |
184 HRESULT hr = InitializeSharedAccess(data_->GetFileName(), | |
185 persist, | |
186 sa, | |
187 sa_mutex, | |
188 read_only); | |
189 if (FAILED(hr)) { | |
190 UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"), | |
191 data_->GetFileName(), read_only ? _T("R") : _T("RW"), hr)); | |
192 } | |
193 } | |
194 | |
195 // Initialize memory mapped file and sync mechanics. | |
196 // by calling InitializeSharedAccess | |
197 SharedMemoryPtr() : | |
198 file_(INVALID_HANDLE_VALUE), file_mapping_(NULL), data_(NULL) { | |
199 // This should never happen but let's assert | |
200 // in case it does. | |
201 // Each shared data must implement GetSharedData() to use this c-tor. | |
202 // The implementation should be: | |
203 // const TCHAR * GetSharedName() const | |
204 // {return L"Some_unique_string_with_no_spaces";} | |
205 HRESULT hr = InitializeSharedAccess(data_->GetSharedName(), | |
206 false, | |
207 NULL, | |
208 NULL, | |
209 false); | |
210 if (FAILED(hr)) { | |
211 UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"), | |
212 data_->GetSharedName(), _T("RW"), hr)); | |
213 } | |
214 } | |
215 | |
216 // Clean up. | |
217 ~SharedMemoryPtr() { | |
218 Cleanup(); | |
219 } | |
220 | |
221 void Cleanup() { | |
222 __mutexScope(this); | |
223 if (data_) | |
224 UnmapViewOfFile(data_); | |
225 if (file_mapping_) | |
226 VERIFY(CloseHandle(file_mapping_), (L"")); | |
227 if (file_ != INVALID_HANDLE_VALUE) | |
228 VERIFY(CloseHandle(file_), (L"")); | |
229 } | |
230 | |
231 // Initialize memory mapped file and sync object. | |
232 bool InitializeSharedAccess(const CString& name, | |
233 bool persist, | |
234 LPSECURITY_ATTRIBUTES sa, | |
235 LPSECURITY_ATTRIBUTES sa_mutex, | |
236 bool read_only) { | |
237 return InitializeSharedAccessInternal(name, | |
238 persist, | |
239 sa, | |
240 sa_mutex, | |
241 read_only, | |
242 sizeof(T), | |
243 &T::InitializeSharedData); | |
244 } | |
245 | |
246 private: | |
247 // Initialize memory mapped file and sync object. | |
248 // | |
249 // This internal method allows template method folding by only using things | |
250 // that are consistent in all templates. Things that vary are passed in. | |
251 bool InitializeSharedAccessInternal(const CString& name, bool persist, | |
252 LPSECURITY_ATTRIBUTES sa, | |
253 LPSECURITY_ATTRIBUTES sa_mutex, | |
254 bool read_only, | |
255 size_t data_size, | |
256 void (T::*initialize_shared_data) | |
257 (const CString&)) { | |
258 // If this memory mapped object is backed by a file, then "name" is a fully | |
259 // qualified name with backslashes. Since we can't use backslashes in a | |
260 // mutex's name, let's make another name where we convert them to | |
261 // underscores. | |
262 CString mem_name(name); | |
263 if (persist) { | |
264 mem_name.Replace(_T('\\'), _T('_')); | |
265 } | |
266 | |
267 // Initialize the mutex | |
268 CString mutex_name(mem_name + _T("MUTEX")); | |
269 LPSECURITY_ATTRIBUTES mutex_attr = sa_mutex ? sa_mutex : sa; | |
270 if (!InitializeWithSecAttr(mutex_name, mutex_attr)) { | |
271 ASSERT(false, (L"Failed to initialize mutex. Err=%i", ::GetLastError())); | |
272 return false; | |
273 } | |
274 | |
275 // everything is synchronized till the end of the function or return. | |
276 __mutexScope(this); | |
277 | |
278 first_instance_ = false; | |
279 | |
280 if (persist) { | |
281 // Back this shared memory by a file | |
282 file_ = CreateFile(name, | |
283 GENERIC_READ | (read_only ? 0 : GENERIC_WRITE), | |
284 FILE_SHARE_READ | (read_only ? 0 : FILE_SHARE_WRITE), | |
285 sa, | |
286 OPEN_ALWAYS, | |
287 NULL, | |
288 NULL); | |
289 if (file_ == INVALID_HANDLE_VALUE) | |
290 return false; | |
291 | |
292 if (!read_only && GetLastError() != ERROR_ALREADY_EXISTS) | |
293 first_instance_ = true; | |
294 } else { | |
295 ASSERT(file_ == INVALID_HANDLE_VALUE, (L"")); | |
296 file_ = INVALID_HANDLE_VALUE; | |
297 } | |
298 | |
299 if (read_only) { | |
300 file_mapping_ = OpenFileMapping(FILE_MAP_READ, false, mem_name); | |
301 if (!file_mapping_) { | |
302 UTIL_LOG(LW, (L"[OpenFileMapping failed][error %i]", ::GetLastError())); | |
303 } | |
304 } else { | |
305 file_mapping_ = CreateFileMapping(file_, sa, | |
306 PAGE_READWRITE, 0, data_size, mem_name); | |
307 ASSERT(file_mapping_, (L"CreateFileMapping. Err=%i", ::GetLastError())); | |
308 } | |
309 | |
310 if (!file_mapping_) { | |
311 return false; | |
312 } else if (!read_only && | |
313 file_ == INVALID_HANDLE_VALUE && | |
314 GetLastError() != ERROR_ALREADY_EXISTS) { | |
315 first_instance_ = true; | |
316 } | |
317 | |
318 data_ = reinterpret_cast<T*>(MapViewOfFile(file_mapping_, | |
319 FILE_MAP_READ | | |
320 (read_only ? 0 : FILE_MAP_WRITE), | |
321 0, | |
322 0, | |
323 data_size)); | |
324 | |
325 if (!data_) { | |
326 ASSERT(false, (L"MapViewOfFile. Err=%i", ::GetLastError())); | |
327 VERIFY(CloseHandle(file_mapping_), (L"")); | |
328 file_mapping_ = NULL; | |
329 | |
330 if (file_ != INVALID_HANDLE_VALUE) { | |
331 VERIFY(CloseHandle(file_), (L"")); | |
332 file_ = INVALID_HANDLE_VALUE; | |
333 } | |
334 | |
335 return false; | |
336 } | |
337 | |
338 if (!first_instance_) { | |
339 return true; | |
340 } | |
341 | |
342 // If this is the first instance of shared object | |
343 // call initialization function. This is nice but | |
344 // at the same time we can not share built in data types. | |
345 // SharedMemoryPtr<double> - will not compile. But this is OK | |
346 // We don't want all the overhead to just share couple of bytes. | |
347 // Signature is void InitializeSharedData() | |
348 (data_->*initialize_shared_data)(name); | |
349 | |
350 return true; | |
351 } | |
352 | |
353 DISALLOW_EVIL_CONSTRUCTORS(SharedMemoryPtr); | |
354 }; | |
355 | |
356 // Sometimes we want Singletons that are shared between processes. | |
357 // SharedMemoryPtr can do that. But if used in C-written module there will be | |
358 // a need to make SharedMemoryPtr a global object. Making a Singleton from Share
dMemoryPtr | |
359 // is possible in this situation, but syntactically this is very difficult to re
ad. | |
360 // The following template solves the problem. It hides difficult to read details
inside. | |
361 // Usage is the same as SharedMemoryPtr (ONLY through -> operator). Completely t
hread-safe. | |
362 // Can be used in two ways: | |
363 // Class A { | |
364 // public: | |
365 // void foo(){} | |
366 // | |
367 //}; | |
368 // SharedMemorySingleton<A> a, b; | |
369 // a->foo(); | |
370 // b->foo(); //refers to the same data in any process. | |
371 // | |
372 // or | |
373 // | |
374 // class A : public SharedMemorySingleton<A> { | |
375 // public: | |
376 // void foo(){} | |
377 //}; | |
378 // A a, b; | |
379 // a->foo(); | |
380 // b->foo(); //refers to the same data in any process. | |
381 | |
382 template <typename LockType, typename T> class SharedMemorySingleton { | |
383 public: | |
384 SharedDataLockingProxy<LockType, T> operator->() { | |
385 return | |
386 Singleton<SharedMemoryPtr<LockType, T> >::Instance()->operator->(); | |
387 } | |
388 }; | |
389 | |
390 } // namespace omaha | |
391 | |
392 #endif // OMAHA_COMMON_SHARED_MEMORY_PTR_H__ | |
OLD | NEW |