| Index: base/shared_memory_ptr.h
|
| diff --git a/base/shared_memory_ptr.h b/base/shared_memory_ptr.h
|
| deleted file mode 100644
|
| index d813cc691996733e2b89daf780949acf955a3f43..0000000000000000000000000000000000000000
|
| --- a/base/shared_memory_ptr.h
|
| +++ /dev/null
|
| @@ -1,392 +0,0 @@
|
| -// 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 "base/debug.h"
|
| -#include "base/singleton.h"
|
| -#include "base/system_info.h"
|
| -#include "base/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__
|
|
|