/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-2013 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 #include #include #include #include #include #include #include #include #include "hphp/util/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::OpenOrCreate("a unique name for my object"); * * Note SharedMemory works just fine with primitive types: * * int *secret_integer = SharedMemory::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::OpenOrCreate("shared"); * { * SharedMemoryLock lock("any name for this lock"); * *shared = whatever; * } * * Two things to notice, (1) SharedMemory::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 class SharedMemoryAllocator : public boost::interprocess::allocator { public: SharedMemoryAllocator() : boost::interprocess::allocator (SharedMemoryManager::GetSegment()->get_segment_manager()) { } }; /////////////////////////////////////////////////////////////////////////////// /** * Creating or destroying a top-level shared memory objects with names. */ template class SharedMemory { public: static T *OpenOrCreate(const char *name) { assert(name && *name); return SharedMemoryManager::GetSegment()->find_or_construct(name)(); } static bool Destroy(const char *name) { assert(name && *name); return SharedMemoryManager::GetSegment()->destroy(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 class SharedMemoryVector : public boost::interprocess::vector > { }; template class SharedMemoryList : public boost::interprocess::list > { }; template class SharedMemorySet : public boost::interprocess::set, SharedMemoryAllocator > { }; template class SharedMemoryMap : public boost::interprocess::map , SharedMemoryAllocator > > { }; template class SharedMemoryMapWithComp : public boost::interprocess::map > > { }; #else // falling back to regular std classes typedef std::string SharedMemoryString; template class SharedMemoryVector : public std::vector {}; template class SharedMemoryList : public std::list {}; template class SharedMemorySet : public std::set {}; template class SharedMemoryMap : public std::map {}; #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_