Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

RCRegion.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifdef PLATFORM_APERIOS
00003 #  include <OPENR/RCRegion.h>
00004 #else
00005 #  ifndef INCLUDED_RCRegion_h_
00006 #  define INCLUDED_RCRegion_h_
00007 
00008 #include "Shared/ReferenceCounter.h"
00009 #include "ProcessID.h"
00010 #include "Shared/plist.h"
00011 #include <sys/types.h>
00012 #include <map>
00013 #include <exception>
00014 
00015 /* Do you want to use the SysV shared memory call interface
00016  * or the POSIX shared memory interface?  They have different
00017  * strengths/weaknesses on different platforms... :-/    */
00018 
00019 /*! @def POSIX_SHM
00020  *  @brief If TEKKOTSU_SHM_STYLE is set to POSIX_SHM, POSIX style shared memory will be used (shm_open, shm_unlink, mmap, munmap, ...);
00021  *  This is the default shared memory interface, both portable and stable.  By default, regular open() and unlink()
00022  *  will be used to implement the regions using file-backed shared memory.  If you define USE_UNBACKED_SHM,
00023  *  RCRegion will use shm_open/shm_unlink instead.  Unbacked shared memory is nice because there's no
00024  *  interaction with your filesystem, but isn't quite as portable.  (Cygwin didn't seem to like it too much...) */
00025 
00026 /*! @def SYSV_SHM
00027  *  @brief If TEKKOTSU_SHM_STYLE is set to SYSV_SHM, SysV style shared memory will be used (shmget, shmctl, shmat, shmdt, ...)
00028  *  SysV style seems to stick around following a program crash, and can't be unlinked pre-emptively while still in use.
00029  *  Since it appears many systems also limit the number of shared memory regions (via a sysctl configuration), leaking
00030  *  regions following repeated crashes during development gets annoying. */
00031 
00032 /*! @def NO_SHM
00033  *  @brief If TEKKOTSU_SHM_STYLE is set to NO_SHM, all shared memory operations become straight new/delete's; this restricts the program to using threads in a single process */
00034 
00035 /*! @def TEKKOTSU_SHM_STYLE
00036  *  @brief Can be set to one of POSIX_SHM, SYSV_SHM, or NO_SHM */
00037 
00038 #ifndef POSIX_SHM
00039 #  define POSIX_SHM 1
00040 #endif
00041 #ifndef SYSV_SHM
00042 #  define SYSV_SHM 2
00043 #endif
00044 #ifndef NO_SHM
00045 #  define NO_SHM 3
00046 #endif
00047 #ifndef TEKKOTSU_SHM_STYLE
00048 #  define TEKKOTSU_SHM_STYLE POSIX_SHM
00049 #endif
00050 
00051 #if TEKKOTSU_SHM_STYLE!=SYSV_SHM && TEKKOTSU_SHM_STYLE!=POSIX_SHM && TEKKOTSU_SHM_STYLE!=NO_SHM
00052 #  error Unknown TEKKOTSU_SHM_STYLE setting
00053 #endif
00054 
00055 namespace ThreadNS {
00056   class Lock;
00057 }
00058 
00059 //! provides compatability with the OPEN-R type of the same name
00060 class RCRegion {
00061 public:
00062 
00063   // The type of region Identifier depends on the style of shared memory being used
00064 
00065 #if TEKKOTSU_SHM_STYLE==SYSV_SHM
00066   //! contains all information needed to attach this region from a different process
00067   struct Identifier {
00068     Identifier() : key(), shmid(), size(0) {}
00069     key_t key; //!< key_t is defined by system headers, contains system region info
00070     int shmid; //!< an integer key value which identifies the region
00071     size_t size; //!< the size of the region
00072   };
00073 
00074 #elif TEKKOTSU_SHM_STYLE==POSIX_SHM || TEKKOTSU_SHM_STYLE==NO_SHM
00075   //! maximum guaranteed length for users' region names (might have a little leeway depending on process ID prefix or tmp path prefix)
00076   static const unsigned int MAX_NAME_LEN=64;
00077 
00078   //! contains all information needed to attach this region from a different process
00079   struct Identifier {
00080     Identifier() : size(0) {} //!< constructor
00081     char key[MAX_NAME_LEN]; //!< a string name for the key
00082     size_t size; //!< size of the region
00083   };
00084 
00085 #  if TEKKOTSU_SHM_STYLE==POSIX_SHM
00086 #    ifndef USE_UNBACKED_SHM
00087   static plist::Primitive<std::string> shmRoot; //!< determines location of the file backing within file system
00088 #    endif
00089   static plist::Primitive<bool> useUniqueMemoryRegions; //!< if true, prefixes region names with #rootPID
00090   static pid_t rootPID; //!< this is the pid of the original process, used for unique names of memory regions; pid_t is from sys/types.h
00091 #  endif
00092 #endif
00093 
00094 
00095   // The constructors, offering either an Aperios-compatable version,
00096   // or a linux-specific version offering explicit control over the
00097   // key value, aids better debugging
00098 
00099 #if TEKKOTSU_SHM_STYLE==SYSV_SHM
00100   //! constructor (OPEN-R compatability)
00101   explicit RCRegion(size_t sz)
00102     : id(), base(NULL), references(NULL)
00103   { init(sz,nextKey,true); }
00104   //! constructor, name isn't used for sysv-style shared memory (not OPEN-R compatable)
00105   /*! could hash the name to generate key...? */
00106   RCRegion(const std::string&, size_t sz)
00107     : id(), base(NULL), references(NULL)
00108   { init(sz,nextKey,true); }
00109 
00110 #elif TEKKOTSU_SHM_STYLE==POSIX_SHM || TEKKOTSU_SHM_STYLE==NO_SHM
00111   //! constructor (OPEN-R compatability, name is autogenerated)
00112   explicit RCRegion(size_t sz)
00113     : id(), base(NULL), references(NULL)
00114   {
00115     char name[RCRegion::MAX_NAME_LEN];
00116     snprintf(name,RCRegion::MAX_NAME_LEN,"Rgn.%d.%u",ProcessID::getID(),static_cast<unsigned int>(++nextKey));
00117     name[RCRegion::MAX_NAME_LEN-1]='\0';
00118     init(sz,name,true);
00119   }
00120   //! constructor, specify your own name for better debugging accountability (not OPEN-R compatable)
00121   RCRegion(const std::string& name, size_t sz)
00122     : id(), base(NULL), references(NULL)
00123   { init(sz,name,true); }
00124 #endif
00125 
00126   //! requests that a specified RCRegion be loaded into the current process's memory space
00127   static RCRegion * attach(const Identifier& rid);
00128   
00129   char * Base() const { return base; } //!< the pointer to the shared memory region
00130   size_t Size() const { return id.size; } //!< the size of the shared memory region
00131   static void setNextKey(key_t k) { nextKey=k; } //!< sets the next key to be used for automatic assignment to new regions
00132   static key_t getNextKey() { return nextKey+1; } //!< return the next region serial number -- doesn't actually increment it though, repeated calls will return the same value until the value is actually used
00133   const Identifier& ID() const { return id; } //!< returns the identifier of this region
00134   
00135   int NumberOfReference() const { return references[ProcessID::NumProcesses]; } //!< number of total references to this region, total of all processes
00136   int NumberOfLocalReference() const { return references[ProcessID::getID()]; } //!< number of references to the region from the current process (in the ProcessID threadgroup sense, not necessarily system-process)
00137   void AddReference(); //!< adds a reference from the current process
00138   void RemoveReference(); //!< removes a reference from the current process
00139   void AddSharedReference(); //!< adds a reference which is held by another shared memory region
00140   void RemoveSharedReference(); //!< removes a reference which is held by another shared memory region
00141   
00142   static void aboutToFork(ProcessID::ProcessID_t newID); //!< does housekeeping to mark the region as attached and the same number of references in the new process as well
00143   static void faultShutdown(); //!< try to unload all regions in a clean manner
00144 
00145 #if TEKKOTSU_SHM_STYLE==SYSV_SHM
00146   //! a map from the shared memory key type to the actual region structure
00147   typedef std::map<key_t,RCRegion*> attachedRegions_t;
00148 #elif TEKKOTSU_SHM_STYLE==POSIX_SHM || TEKKOTSU_SHM_STYLE==NO_SHM
00149   //! a map from the shared memory key type to the actual region structure
00150   typedef std::map<std::string,RCRegion*> attachedRegions_t;
00151 #endif
00152   static unsigned int NumberOfAttach() { return attachedRegions.size(); } //!< returns the number of regions which are currently attached in the process
00153   
00154   //! Returns an iterator to the beginning of the attached regions mapping -- it->first is the key, it->second is the RCRegion*
00155   /*! If you need thread-safety (i.e. another thread may attach/detach while you are iterating), pass true, and be sure to use attachedAdvance() to increment the iterator!
00156    *  This doesn't prevent other threads from attaching/detaching regions, it only prevents detaching the one you're on.
00157    *  When you're done with a thread-safe iterator, either attachedAdvance() it off the end, or manually call RemoveReference() on the iterator's final region */
00158   static attachedRegions_t::const_iterator attachedBegin(bool threadSafe);
00159   //! Returns an iterator to the end of the attached regions -- it->first is the key, it->second is the RCRegion*
00160   /*! If you need thread-safety (i.e. another thread may attach/detach while you are iterating), be sure to use attachedAdvance() to decrement the iterator!
00161    *  This doesn't prevent other threads from attaching/detaching regions, it only prevents detaching the one you're on. */
00162   static attachedRegions_t::const_iterator attachedEnd();
00163   //! Increments the attached region iterator in a thread-safe way -- only use this if you previously passed 'true' to begin(), or are decrementing from end()
00164   /*! If you are using an iterator obtained without thread-safety, just increment it normally -- don't switch to this or it will screw up reference counting.
00165    *  If you insist on switching back and forth between thread-safe advance (this function) and normal iterator advancing, you will need to add a reference to the current iterator's region before calling this.
00166    *  When you're done, either advance off the end, or manually call RemoveReference() on the iterator's final region */
00167   static void attachedAdvance(attachedRegions_t::const_iterator& it, int x=1);
00168   
00169   //! Different methods of handling regions with conflicting keys
00170   enum ConflictResolutionStrategy {
00171     RENAME,  //!< try another key until we find one that works (better for SYSV, maybe not so smart for POSIX)
00172     REPLACE, //!< delete the other region and try again (better for POSIX, maybe not so smart for SYSV)
00173     EXIT //!< go home and cry about it
00174   };
00175   
00176   static void setConflictResolution(ConflictResolutionStrategy crs) { conflictStrategy=crs; } //!< sets #conflictStrategy
00177   static ConflictResolutionStrategy getConflictResolution() { return conflictStrategy; } //!< returns #conflictStrategy
00178   
00179   static void setMultiprocess(bool mp) { multiprocess=mp; } //!< sets #multiprocess
00180   static bool getMultiprocess() { return multiprocess; } //!< returns #multiprocess
00181 
00182   
00183 protected:
00184   //! this protected constructor is used for attaching regions previously created by another process (see attach())
00185   RCRegion(const Identifier& rid)
00186     : id(), base(NULL), references(NULL)
00187   { init(rid.size,rid.key,false); }
00188 
00189   ~RCRegion(); //!< prevents stack allocation -- needs to be heap allocated and reference counted
00190 
00191   //! the alignment multiple of the extra space at the end of the region
00192   static const unsigned int align=sizeof(unsigned int);
00193   //! the amount of space to leave at the end of the region for housekeeping (reference counts)
00194   static const unsigned int extra=sizeof(unsigned int)*(ProcessID::NumProcesses+1);
00195   //! returns the size of the region to be allocated, given the size requested by the client
00196   static unsigned int calcRealSize(unsigned int size);
00197 
00198   //! intializes and returns #staticLock
00199   static ThreadNS::Lock& getStaticLock();
00200 
00201 #if TEKKOTSU_SHM_STYLE==SYSV_SHM
00202   //! initializes the region's information, either creating a new shared memory region or attempting to connect to a pre-existing one
00203   void init(size_t sz, key_t sug_key, bool create);
00204 #elif TEKKOTSU_SHM_STYLE==POSIX_SHM
00205   //! returns the qualified version of this region's key (see getQualifiedName(const std::string& key) )
00206   std::string getQualifiedName() const { return getQualifiedName(id.key); }
00207   //! wraps the region's key with a root path prefix and optionally a PID suffix (see #useUniqueMemoryRegions and #shmRoot)
00208   static std::string getQualifiedName(const std::string& key);
00209   //! opens a region either in "pure" shared memory, or in file-backed shared memory, based on whether USE_UNBACKED_SHM is defined
00210   int openRegion(int mode) const;
00211   //! unlinks a region either in "pure" shared memory, or in file-backed shared memory, based on whether USE_UNBACKED_SHM is defined
00212   bool unlinkRegion() const;
00213   //! initializes the region's information, either creating a new shared memory region or attempting to connect to a pre-existing one
00214   void init(size_t sz, const std::string& name, bool create);
00215 #elif TEKKOTSU_SHM_STYLE==NO_SHM
00216   //! initializes the region's information, either pointing to an existing region or allocating a new one
00217   void init(size_t sz, const std::string& name, bool create);
00218 #endif
00219 
00220   //! controls what to do about creating a region with a conflicting key (i.e. another region already exists with the same key)
00221   static ConflictResolutionStrategy conflictStrategy;
00222   //! set to true if we are shutting down because of an error, and trying to unload shared regions to avoid leaking beyond program scope
00223   static bool isFaultShutdown;
00224   //! set to false if the different "processes" are just threads (and thus the last process reference shouldn't actually trigger unlinking a region
00225   static bool multiprocess;
00226                     
00227   static ThreadNS::Lock* staticLock; //!< a lock over all static RCRegion members for the current process, must be obtained before changing reference counts or attaching/detaching regions
00228 
00229   static key_t nextKey; //!< serial number of next key -- starts at 1024 for TEKKOTSU_SHM_STYLE==SYSV_SHM, 0 for POSIX_SHM
00230   static attachedRegions_t attachedRegions; //!< a mapping of key values to RCRegion pointers of the attached region
00231   
00232   Identifier id; //!< key values for the region, namely the system key type (either an integer or string depending on TEKKOTSU_SHM_STYLE) and the size of the region
00233   char * base; //!< pointer to the region's user data
00234   unsigned int * references; //!< pointer to the per-process reference counts (stored within the shared region!)
00235 
00236 private:
00237   RCRegion(const RCRegion& r); //!< don't call
00238   RCRegion& operator=(const RCRegion& r); //!< don't call
00239 };
00240 
00241 /*! @file
00242  * @brief Describes RCRegion, which provides compatability with the OPEN-R type of the same name
00243  * @author ejt (Creator)
00244  *
00245  * $Author: ejt $
00246  * $Name: tekkotsu-4_0 $
00247  * $Revision: 1.12 $
00248  * $State: Exp $
00249  * $Date: 2007/11/13 16:17:46 $
00250  */
00251 
00252 #  endif
00253 #endif

Tekkotsu v4.0
Generated Thu Nov 22 00:54:55 2007 by Doxygen 1.5.4