| 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 |