243 linhas
8.7 KiB
C++
243 linhas
8.7 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifndef incl_HPHP_MEMORY_SHARED_MEMORY_ALLOCATOR_H_
|
|
#define incl_HPHP_MEMORY_SHARED_MEMORY_ALLOCATOR_H_
|
|
|
|
#include <boost/interprocess/managed_shared_memory.hpp>
|
|
#include <boost/interprocess/sync/named_mutex.hpp>
|
|
#include <boost/interprocess/containers/string.hpp>
|
|
#include <boost/interprocess/containers/vector.hpp>
|
|
#include <boost/interprocess/containers/list.hpp>
|
|
#include <boost/interprocess/containers/map.hpp>
|
|
#include <boost/interprocess/containers/set.hpp>
|
|
#include <boost/interprocess/allocators/allocator.hpp>
|
|
#include <boost/interprocess/offset_ptr.hpp>
|
|
#include "base.h"
|
|
|
|
namespace HPHP {
|
|
|
|
/**
|
|
* HOW TO USE SHARED MEMORY FOR MY OBJECTS?
|
|
* ========================================
|
|
*
|
|
* 1. Make sure your class is POD (plain-old data object), simply meaning
|
|
* there cannot be virtual functions in the class. Derivation is okay, but
|
|
* just not virtual functions, because virtual function table has address
|
|
* pointers that are always local to creation process, and those are not
|
|
* pass-able between different processes. If you have it, the symptom would
|
|
* be, well it works for the creation process, but it will fail miserably
|
|
* when a 2nd process attaches to the same shared memory and call some
|
|
* functions (or other things) that use vtable.
|
|
*
|
|
* 2. Make sure ALL data members, no matter how deep it is, are shared memory
|
|
* safe. Primitive types are automatically shared memory safe, thanks to the
|
|
* allocator implemented in this file. For complex types, we just need to
|
|
* make sure all STL classes are converted to use their counterparts:
|
|
*
|
|
* std::string => SharedMemoryString
|
|
* std::vector => SharedMemoryVector
|
|
* std::list => SharedMemoryList
|
|
* std::set => SharedMemorySet
|
|
* std::map => SharedMemoryMap
|
|
*
|
|
* There is a macro DISABLE_SHARED_MEMORY for debugging purpose, turning off
|
|
* this conversion transparently.
|
|
*
|
|
* For any other complex types that are not covered here, they have to have
|
|
* a way to specify its memory allocator to use the allocator in this file.
|
|
* This normally means dirty work redirecting all malloc/free to call
|
|
* methods on the allocator and pass this allocator into the class. So it
|
|
* may not be trivial.
|
|
*
|
|
* 3. During application startup time, call this to initialize/attach to an
|
|
* existing piece of shared memory:
|
|
*
|
|
* SharedMemoryManager::Init(totalSizeInBytes);
|
|
*
|
|
* Shared memory can actually resizable, but it's not implemented in this
|
|
* file yet. Look up this web site for adding features in this file:
|
|
*
|
|
* http://www.boost.org/doc/libs/1_35_0/doc/html/interprocess.html
|
|
*
|
|
* 4. Now, we need a top level object to pull out anything from shared memory,
|
|
* right? This kind of top level objects always have a name to uniquely
|
|
* identify them:
|
|
*
|
|
* MyClass *top_level_object =
|
|
* SharedMemory<MyClass>::OpenOrCreate("a unique name for my object");
|
|
*
|
|
* Note SharedMemory<T> works just fine with primitive types:
|
|
*
|
|
* int *secret_integer = SharedMemory<int>::OpenOrCreate("secret");
|
|
*
|
|
* 5. If we are the sole owner of a shared memory object, we don't need locking
|
|
* at all. If not, we need to lock individual objects for accesses:
|
|
*
|
|
* int *shared = SharedMemory<int>::OpenOrCreate("shared");
|
|
* {
|
|
* SharedMemoryLock lock("any name for this lock");
|
|
* *shared = whatever;
|
|
* }
|
|
*
|
|
* Two things to notice, (1) SharedMemory<T>::OpenOrCreate() is atomic
|
|
* already, and there is no need to lock protect that. It also made sure
|
|
* same key/name never gets created twice by two concurrent processes.
|
|
* (2) lock's name has NOTHING to do with the object's name. So you can use
|
|
* the same lock for multiple objects. Or, more often than not, you can use
|
|
* multple locks for sub-component of the same object -- finer locking.
|
|
*
|
|
* It's a dumb simple mutex-based lock right now. It's possible to have
|
|
* read-write locks as well, just not implemented yet.
|
|
*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Helper class to hold a segment of shared-memory. We only need one instance
|
|
* of this class for one application for simplicity.
|
|
*/
|
|
class SharedMemoryManager {
|
|
public:
|
|
// an app needs to call this during startup time
|
|
static void Init(int size, bool clean);
|
|
static void Reset();
|
|
|
|
// called by allocator class
|
|
static boost::interprocess::managed_shared_memory *GetSegment() {
|
|
assert(Segment); // or Init() is missing
|
|
return Segment;
|
|
}
|
|
|
|
private:
|
|
static const char *Name;
|
|
static boost::interprocess::managed_shared_memory *Segment;
|
|
};
|
|
|
|
/**
|
|
* Helper class for STL classes to use our SharedMemoryManager for allocating
|
|
* elements.
|
|
*/
|
|
template<typename T>
|
|
class SharedMemoryAllocator : public boost::interprocess::allocator
|
|
<T, boost::interprocess::managed_shared_memory::segment_manager> {
|
|
public:
|
|
SharedMemoryAllocator() :
|
|
boost::interprocess::allocator
|
|
<T, boost::interprocess::managed_shared_memory::segment_manager>
|
|
(SharedMemoryManager::GetSegment()->get_segment_manager()) {
|
|
}
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Creating or destroying a top-level shared memory objects with names.
|
|
*/
|
|
template<typename T>
|
|
class SharedMemory {
|
|
public:
|
|
static T *OpenOrCreate(const char *name) {
|
|
assert(name && *name);
|
|
return SharedMemoryManager::GetSegment()->find_or_construct<T>(name)();
|
|
}
|
|
static bool Destroy(const char *name) {
|
|
assert(name && *name);
|
|
return SharedMemoryManager::GetSegment()->destroy<T>(name);
|
|
}
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef DISABLE_SHARED_MEMORY
|
|
|
|
/**
|
|
* Embeddable STL classes: string, vector, list, set, map.
|
|
*/
|
|
typedef boost::interprocess::string SharedMemoryString;
|
|
|
|
// This sucks, since C++ doesn't support partial template typedefs.
|
|
|
|
template<typename T>
|
|
class SharedMemoryVector :
|
|
public boost::interprocess::vector<T, SharedMemoryAllocator<T> > {
|
|
};
|
|
template<typename T>
|
|
class SharedMemoryList :
|
|
public boost::interprocess::list<T, SharedMemoryAllocator<T> > {
|
|
};
|
|
template<typename T>
|
|
class SharedMemorySet :
|
|
public boost::interprocess::set<T, std::less<T>,
|
|
SharedMemoryAllocator<T> > {
|
|
};
|
|
template<typename Key, typename T>
|
|
class SharedMemoryMap :
|
|
public boost::interprocess::map
|
|
<Key, T, std::less<Key>, SharedMemoryAllocator<std::pair<Key, T> > > {
|
|
};
|
|
|
|
template<typename Key, typename T, class C>
|
|
class SharedMemoryMapWithComp :
|
|
public boost::interprocess::map
|
|
<Key, T, C, SharedMemoryAllocator<std::pair<Key, T> > > {
|
|
};
|
|
|
|
#else
|
|
|
|
// falling back to regular std classes
|
|
typedef std::string SharedMemoryString;
|
|
template<typename T> class SharedMemoryVector : public std::vector<T> {};
|
|
template<typename T> class SharedMemoryList : public std::list<T> {};
|
|
template<typename T> class SharedMemorySet : public std::set<T> {};
|
|
template<typename K, typename T>
|
|
class SharedMemoryMap : public std::map<K, T> {};
|
|
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Lock/unlock a top level shared memory object for sole access.
|
|
*/
|
|
class SharedMemoryLock {
|
|
public:
|
|
static bool Destroy(const char *name) {
|
|
assert(name && *name);
|
|
return boost::interprocess::named_mutex::remove(name);
|
|
}
|
|
|
|
explicit SharedMemoryLock(const char *name) {
|
|
assert(name && *name);
|
|
m_mutex = new boost::interprocess::named_mutex
|
|
(boost::interprocess::open_or_create, name);
|
|
m_mutex->lock();
|
|
}
|
|
|
|
~SharedMemoryLock() {
|
|
m_mutex->unlock();
|
|
delete m_mutex;
|
|
}
|
|
|
|
private:
|
|
boost::interprocess::named_mutex *m_mutex;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
#endif // incl_HPHP_MEMORY_SHARED_MEMORY_ALLOCATOR_H_
|