| // Copyright 2004-2009 Google Inc. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // ======================================================================== |
| // |
| // Defines template class used to share data |
| // between processes. |
| // |
| |
| #ifndef OMAHA_COMMON_SHARED_MEMORY_PTR_H__ |
| #define OMAHA_COMMON_SHARED_MEMORY_PTR_H__ |
| |
| #include "common/debug.h" |
| #include "common/singleton.h" |
| #include "common/system_info.h" |
| #include "common/vista_utils.h" |
| |
| namespace omaha { |
| |
| // SharedMemoryPtr class is designed to allow seamless data sharing process boundaries. |
| // All the data passed as a template parameter will be shared between processes. |
| // Very important to remember that for now - all shared data should be on stack. |
| // For example if the class A had stl vector as a member, the members of the vector |
| // would be allocated not from shared memory and therefore will not be shared. |
| // That could be solved with allocators, but for now we don't need that. |
| // |
| // Here is a typical example of usage: |
| // Class A { |
| // int i_; |
| // double d_; |
| // ....... |
| // ........ |
| // |
| // public: |
| // set_double(double d){d_=d;} |
| // double get_double(){return d_}; |
| // |
| // }; |
| // |
| // ... Prosess one... |
| // SharedMemoryPtr<A> spA("ABC"); |
| // if (!spA) |
| // return false; |
| // |
| // spA->set_double(3.14); |
| // |
| // ... Process two ... |
| // |
| // |
| // SharedMemoryPtr<A> spA1("ABC"); |
| // if (!spA1) |
| // return false; |
| // |
| // process two will see the value set by process one. |
| // it will be 3.14 |
| // double d = spA1->get_double(); |
| // |
| |
| // You should implement a class member of SystemSharedData if the data you want |
| // to share is several hundred bytes. Always try this approach first before you implement |
| // new class that is derived from SharedMemoryPtr. The main difference is that SharedMemoryPtr |
| // will allocate a least page of shared memory. If your class is just a member of SystemSharedData |
| // memory mapped file will be shared between members. It is just more efficient. |
| // Look in system_shared_data.h , shared_data_member.h, and system_shared_data_members.h |
| // for more details. |
| |
| // Forward declaration. |
| template <typename LockType, typename T> class SharedMemoryPtr; |
| |
| // During several code reviews it has been noticed that the same error gets repeated over and over. |
| // People create SharedMemoryPtr<SomeData>. And than the access to member functions of SomeData |
| // is not synchronized by __mutexBlock or __mutexScope. So we need to somehow find a way to make |
| // automatic syncronization whenever people access shared data methods or members. |
| // Since by design the only way we can acess shared data is through operator -> of SharedMemoryPtr |
| // we need to somehow invoke synchronization at the time of access. |
| // We can implement this mainly because of the mechanics of operator-> dictated by C++ standard. |
| // When you apply operator-> to a type that's not a built-in pointer, the compiler does an interesting thing. |
| // After looking up and applying the user-defined operator-> to that type, it applies operator-> again to the result. |
| // The compiler keeps doing this recursively until it reaches a pointer to a built-in type, and only then proceeds with member access. |
| // It follows that a SharedMemoryPtr<T> operator-> does not have to return a pointer. |
| // It can return an object that in turn implements operator->, without changing the use syntax. |
| // So we can implement: pre- and postfunction calls. (See Stroustrup 2000) |
| // If you return an object of some type X |
| // by value from operator->, the sequence of execution is as follows: |
| // 1. Constructor of type X |
| // 2. X::operator-> called; returns a pointer to an object of type T of SharedMemoryPtr |
| // 3. Member access |
| // 4. Destructor of X |
| // In a nutshell, we have a way of implementing locked function calls. |
| |
| template <typename LockType, typename T> |
| class SharedDataLockingProxy { |
| public: |
| // Lock on construction. |
| SharedDataLockingProxy(SharedMemoryPtr<LockType, T> * mem_ptr, T* shared_data) |
| : mem_ptr_(mem_ptr), shared_data_(shared_data) { |
| mem_ptr_->Lock(); |
| } |
| // Unlock on destruction. |
| ~SharedDataLockingProxy() { |
| mem_ptr_->Unlock(); |
| } |
| // operator |
| T* operator->() const { |
| ASSERT(shared_data_ != NULL, (L"NULL object pointer being dereferenced")); |
| return shared_data_; |
| } |
| private: |
| SharedDataLockingProxy& operator=(const SharedDataLockingProxy&); |
| SharedMemoryPtr<LockType, T>* mem_ptr_; |
| T* shared_data_; |
| // To allow this implicit locking - copy constructor must be |
| // enabled. hence, no DISALLOW_EVIL_CONSTRUCTORS |
| }; |
| |
| template <typename LockType, typename T> class SharedMemoryPtr |
| : public LockType { |
| // Handle to disk file if we're backing this shared memory by a file |
| HANDLE file_; |
| // Local handle to file mapping. |
| HANDLE file_mapping_; |
| // pointer to a view. |
| T* data_; |
| // If the first time creation can do some initialization. |
| bool first_instance_; |
| public: |
| // The heart of the whole idea. Points to shared memrory |
| // instead of the beginning of the class. |
| SharedDataLockingProxy<LockType, T> operator->() { |
| return SharedDataLockingProxy<LockType, T>(this, data_); |
| } |
| // To check after creation. |
| // For example: |
| // SharedMemoryPtr<GLock, SomeClass> sm; |
| // if (sm) |
| // { do whatever you want} |
| // else |
| // {error reporting} |
| operator bool() const {return ((file_mapping_ != NULL) && (data_ != NULL));} |
| |
| // Initialize memory mapped file and sync mechanics. |
| // by calling InitializeSharedAccess |
| SharedMemoryPtr(const CString& name, |
| LPSECURITY_ATTRIBUTES sa, |
| LPSECURITY_ATTRIBUTES sa_mutex, |
| bool read_only) |
| : file_(INVALID_HANDLE_VALUE), |
| file_mapping_(NULL), |
| data_(NULL) { |
| HRESULT hr = InitializeSharedAccess(name, false, sa, sa_mutex, read_only); |
| if (FAILED(hr)) { |
| UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"), |
| name, read_only ? _T("R") : _T("RW"), hr)); |
| } |
| } |
| |
| // Use this constructor if you want to back the shared memory by a file. |
| // NOTE: if using a persistent shared memory, every object with this same |
| // name should be persistent. Otherwise, the objects marked as |
| // non-persistent will lead to InitializeSharedData called again if |
| // they are instantiated before the ones marked as persistent. |
| SharedMemoryPtr(bool persist, |
| LPSECURITY_ATTRIBUTES sa, |
| LPSECURITY_ATTRIBUTES sa_mutex, |
| bool read_only) |
| : file_(INVALID_HANDLE_VALUE), |
| file_mapping_(NULL), |
| data_(NULL) { |
| // Each shared data must implement GetFileName() to use this c-tor. The |
| // implementation should be: |
| // const CString GetFileName() const {return L"C:\\directory\file";} |
| // This is purposedly different from GetSharedName, so that the user is |
| // well aware that a file name is expected, not a mutex name. |
| HRESULT hr = InitializeSharedAccess(data_->GetFileName(), |
| persist, |
| sa, |
| sa_mutex, |
| read_only); |
| if (FAILED(hr)) { |
| UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"), |
| data_->GetFileName(), read_only ? _T("R") : _T("RW"), hr)); |
| } |
| } |
| |
| // Initialize memory mapped file and sync mechanics. |
| // by calling InitializeSharedAccess |
| SharedMemoryPtr() : |
| file_(INVALID_HANDLE_VALUE), file_mapping_(NULL), data_(NULL) { |
| // This should never happen but let's assert |
| // in case it does. |
| // Each shared data must implement GetSharedData() to use this c-tor. |
| // The implementation should be: |
| // const TCHAR * GetSharedName() const |
| // {return L"Some_unique_string_with_no_spaces";} |
| HRESULT hr = InitializeSharedAccess(data_->GetSharedName(), |
| false, |
| NULL, |
| NULL, |
| false); |
| if (FAILED(hr)) { |
| UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"), |
| data_->GetSharedName(), _T("RW"), hr)); |
| } |
| } |
| |
| // Clean up. |
| ~SharedMemoryPtr() { |
| Cleanup(); |
| } |
| |
| void Cleanup() { |
| __mutexScope(this); |
| if (data_) |
| UnmapViewOfFile(data_); |
| if (file_mapping_) |
| VERIFY(CloseHandle(file_mapping_), (L"")); |
| if (file_ != INVALID_HANDLE_VALUE) |
| VERIFY(CloseHandle(file_), (L"")); |
| } |
| |
| // Initialize memory mapped file and sync object. |
| bool InitializeSharedAccess(const CString& name, |
| bool persist, |
| LPSECURITY_ATTRIBUTES sa, |
| LPSECURITY_ATTRIBUTES sa_mutex, |
| bool read_only) { |
| return InitializeSharedAccessInternal(name, |
| persist, |
| sa, |
| sa_mutex, |
| read_only, |
| sizeof(T), |
| &T::InitializeSharedData); |
| } |
| |
| private: |
| // Initialize memory mapped file and sync object. |
| // |
| // This internal method allows template method folding by only using things |
| // that are consistent in all templates. Things that vary are passed in. |
| bool InitializeSharedAccessInternal(const CString& name, bool persist, |
| LPSECURITY_ATTRIBUTES sa, |
| LPSECURITY_ATTRIBUTES sa_mutex, |
| bool read_only, |
| size_t data_size, |
| void (T::*initialize_shared_data) |
| (const CString&)) { |
| // If this memory mapped object is backed by a file, then "name" is a fully |
| // qualified name with backslashes. Since we can't use backslashes in a |
| // mutex's name, let's make another name where we convert them to |
| // underscores. |
| CString mem_name(name); |
| if (persist) { |
| mem_name.Replace(_T('\\'), _T('_')); |
| } |
| |
| // Initialize the mutex |
| CString mutex_name(mem_name + _T("MUTEX")); |
| LPSECURITY_ATTRIBUTES mutex_attr = sa_mutex ? sa_mutex : sa; |
| if (!InitializeWithSecAttr(mutex_name, mutex_attr)) { |
| ASSERT(false, (L"Failed to initialize mutex. Err=%i", ::GetLastError())); |
| return false; |
| } |
| |
| // everything is synchronized till the end of the function or return. |
| __mutexScope(this); |
| |
| first_instance_ = false; |
| |
| if (persist) { |
| // Back this shared memory by a file |
| file_ = CreateFile(name, |
| GENERIC_READ | (read_only ? 0 : GENERIC_WRITE), |
| FILE_SHARE_READ | (read_only ? 0 : FILE_SHARE_WRITE), |
| sa, |
| OPEN_ALWAYS, |
| NULL, |
| NULL); |
| if (file_ == INVALID_HANDLE_VALUE) |
| return false; |
| |
| if (!read_only && GetLastError() != ERROR_ALREADY_EXISTS) |
| first_instance_ = true; |
| } else { |
| ASSERT(file_ == INVALID_HANDLE_VALUE, (L"")); |
| file_ = INVALID_HANDLE_VALUE; |
| } |
| |
| if (read_only) { |
| file_mapping_ = OpenFileMapping(FILE_MAP_READ, false, mem_name); |
| if (!file_mapping_) { |
| UTIL_LOG(LW, (L"[OpenFileMapping failed][error %i]", ::GetLastError())); |
| } |
| } else { |
| file_mapping_ = CreateFileMapping(file_, sa, |
| PAGE_READWRITE, 0, data_size, mem_name); |
| ASSERT(file_mapping_, (L"CreateFileMapping. Err=%i", ::GetLastError())); |
| } |
| |
| if (!file_mapping_) { |
| return false; |
| } else if (!read_only && |
| file_ == INVALID_HANDLE_VALUE && |
| GetLastError() != ERROR_ALREADY_EXISTS) { |
| first_instance_ = true; |
| } |
| |
| data_ = reinterpret_cast<T*>(MapViewOfFile(file_mapping_, |
| FILE_MAP_READ | |
| (read_only ? 0 : FILE_MAP_WRITE), |
| 0, |
| 0, |
| data_size)); |
| |
| if (!data_) { |
| ASSERT(false, (L"MapViewOfFile. Err=%i", ::GetLastError())); |
| VERIFY(CloseHandle(file_mapping_), (L"")); |
| file_mapping_ = NULL; |
| |
| if (file_ != INVALID_HANDLE_VALUE) { |
| VERIFY(CloseHandle(file_), (L"")); |
| file_ = INVALID_HANDLE_VALUE; |
| } |
| |
| return false; |
| } |
| |
| if (!first_instance_) { |
| return true; |
| } |
| |
| // If this is the first instance of shared object |
| // call initialization function. This is nice but |
| // at the same time we can not share built in data types. |
| // SharedMemoryPtr<double> - will not compile. But this is OK |
| // We don't want all the overhead to just share couple of bytes. |
| // Signature is void InitializeSharedData() |
| (data_->*initialize_shared_data)(name); |
| |
| return true; |
| } |
| |
| DISALLOW_EVIL_CONSTRUCTORS(SharedMemoryPtr); |
| }; |
| |
| // Sometimes we want Singletons that are shared between processes. |
| // SharedMemoryPtr can do that. But if used in C-written module there will be |
| // a need to make SharedMemoryPtr a global object. Making a Singleton from SharedMemoryPtr |
| // is possible in this situation, but syntactically this is very difficult to read. |
| // The following template solves the problem. It hides difficult to read details inside. |
| // Usage is the same as SharedMemoryPtr (ONLY through -> operator). Completely thread-safe. |
| // Can be used in two ways: |
| // Class A { |
| // public: |
| // void foo(){} |
| // |
| //}; |
| // SharedMemorySingleton<A> a, b; |
| // a->foo(); |
| // b->foo(); //refers to the same data in any process. |
| // |
| // or |
| // |
| // class A : public SharedMemorySingleton<A> { |
| // public: |
| // void foo(){} |
| //}; |
| // A a, b; |
| // a->foo(); |
| // b->foo(); //refers to the same data in any process. |
| |
| template <typename LockType, typename T> class SharedMemorySingleton { |
| public: |
| SharedDataLockingProxy<LockType, T> operator->() { |
| return |
| Singleton<SharedMemoryPtr<LockType, T> >::Instance()->operator->(); |
| } |
| }; |
| |
| } // namespace omaha |
| |
| #endif // OMAHA_COMMON_SHARED_MEMORY_PTR_H__ |