src/Message/Message.h

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 /***************************************************************************
00003  *
00004  * The IPPL Framework
00005  * 
00006  *
00007  * Visit http://people.web.psi.ch/adelmann/ for more details
00008  *
00009  ***************************************************************************/
00010 
00011 #ifndef MESSAGE_H
00012 #define MESSAGE_H
00013 
00014 /***************************************************************************
00015  * Message - contains a list of items that comprise a set of data to be
00016  * sent, or received from, another node in a parallel architecture.  A person
00017  * creates a Message object, loads it with data to be sent, and gives it
00018  * to a Communicate object.
00019  *
00020  * The message consists of N 'MsgItem' objects, which each contain some
00021  * data; data is added sequentially to a new message using the 'put'
00022  * routine, and extracted using the 'get' routine.  The messages are
00023  * retrieved in a FIFO fashion - you put in messages, and get them out
00024  * in the order in which they were put in.  You can access MsgItem's via
00025  * random access with the [] operator, but it is important
00026  * to actually remove the data using 'get' as this will properly deal with
00027  * copying data and freeing up memory (if necessary).
00028  *
00029  * Note on usage: A Message is primarily intended to be created once,
00030  * given to a Communicate instance to transmit to another node, and then
00031  * to be unpacked or otherwise read by the receiver.  A received message
00032  * may be forwarded to another node, but it cannot be combined with another
00033  * or used to initialize a copy of the Message.  This is due to the problems
00034  * of resolving who needs to free up the storage used for the Message elements.
00035  ***************************************************************************/
00036 
00037 // include files
00038 #include "Utility/Inform.h"
00039 #include "Utility/Pstring.h"
00040 #include "AppTypes/dcomplex.h"
00041 #include <stddef.h>
00042 
00043 #ifdef IPPL_STDSTL
00044 #include <vector>
00045 using std::vector;
00046 #else
00047 #include <vector.h>
00048 #endif // IPPL_STDSTL
00049 
00050 #ifdef IPPL_USE_STANDARD_HEADERS
00051 #include <iostream>
00052 using namespace std;
00053 #else
00054 #include <iostream.h>
00055 #endif
00056 
00057 #include <string.h>
00058 #include <stdlib.h>
00059 
00060 // forward declarations
00061 class Communicate;
00062 class Message;
00063 ostream& operator<<(ostream& o, const Message& m);
00064 
00065 // Macros
00067 // simple template traits class to tell if a type is a built-in type
00068 // or not.  The classes have an enum which is 1 if the type is build-in,
00069 // 0 otherwise.  The default case is 0, but specialized to 1 for other types.
00070 template <class T>
00071 struct MessageTypeIntrinsic {
00072   enum { builtin = 0 };
00073   enum { pointer = 0 };
00074 };
00075 
00076 #define DEFINE_BUILTIN_TRAIT_CLASS(T)                           \
00077 template <>                                                     \
00078 struct MessageTypeIntrinsic<T> {                                \
00079   enum { builtin = 1 };                                         \
00080   enum { pointer = 0 };                                         \
00081 };                                                              \
00082 template <>                                                     \
00083 struct MessageTypeIntrinsic<T *> {                              \
00084   enum { builtin = 1 };                                         \
00085   enum { pointer = 1 };                                         \
00086 };                                                              \
00087 template <int N>                                                \
00088 struct MessageTypeIntrinsic<T[N]> {                             \
00089   enum { builtin = 1 };                                         \
00090   enum { pointer = 1 };                                         \
00091 };
00092 
00093 #define DEFINE_ALL_BUILTIN_TRAIT_CLASS(T)       \
00094 DEFINE_BUILTIN_TRAIT_CLASS(T)                   \
00095 DEFINE_BUILTIN_TRAIT_CLASS(const T)
00096 
00097 DEFINE_ALL_BUILTIN_TRAIT_CLASS(bool)
00098 DEFINE_ALL_BUILTIN_TRAIT_CLASS(char)
00099 DEFINE_ALL_BUILTIN_TRAIT_CLASS(unsigned char)
00100 DEFINE_ALL_BUILTIN_TRAIT_CLASS(short)
00101 DEFINE_ALL_BUILTIN_TRAIT_CLASS(unsigned short)
00102 DEFINE_ALL_BUILTIN_TRAIT_CLASS(int)
00103 DEFINE_ALL_BUILTIN_TRAIT_CLASS(unsigned int)
00104 DEFINE_ALL_BUILTIN_TRAIT_CLASS(long)
00105 DEFINE_ALL_BUILTIN_TRAIT_CLASS(unsigned long)
00106 #if defined(IPPL_LONGLONG)
00107 DEFINE_ALL_BUILTIN_TRAIT_CLASS(long long)
00108 #endif
00109 DEFINE_ALL_BUILTIN_TRAIT_CLASS(float)
00110 DEFINE_ALL_BUILTIN_TRAIT_CLASS(double)
00111 DEFINE_ALL_BUILTIN_TRAIT_CLASS(dcomplex)
00112 #if ( defined(IPPL_HAS_TEMPLATED_COMPLEX) && \
00113       !defined(IPPL_USE_SINGLE_PRECISION) )
00114 DEFINE_ALL_BUILTIN_TRAIT_CLASS(fComplex)
00115 #endif
00116 
00117 // ada change fcomplex to fComples cmee name clash
00118 
00119 
00120 
00122 // a class to put single items into a Message, which can be specialized
00123 // to the case of built-in types and other types.
00124 
00125 template <class T, bool builtin, bool pointer>
00126 struct PutSingleItem { };
00127 
00128 // specialization to a non-built-in type, which is never assumed to be a ptr
00129 template <class T>
00130 struct PutSingleItem<T, false, false> {
00131   // put a value into a message
00132   static Message& put(Message&, const T&);
00133   // get a value out of a message
00134   static Message& get(Message&, T&);
00135   // version of put using a pair of iterators
00136   static Message& put(Message&, T, T);
00137   // version of put using a list of indices and an iterator
00138   static Message& put(Message&, const vector<size_t>&, T);
00139   // get_iter uses an output iterator
00140   static Message& get_iter(Message&, T);
00141 };
00142 
00143 // specialization to a built-in type that is not a pointer
00144 template <class T>
00145 struct PutSingleItem<T, true, false> {
00146   // put a value into a message
00147   static Message& put(Message&, const T&);
00148   // get a value out of a message
00149   static Message& get(Message&, T&);
00150 };
00151 
00152 // specialization to a pointer to a built-in type. In this class, we
00153 // know that 'T' is a pointer type.
00154 template <class T>
00155 struct PutSingleItem<T, true, true> {
00156   // put using a pair of pointers
00157   static Message& put(Message&, T, T);
00158   // get using a pointer
00159   static Message& get(Message&, T);
00160   // put using a list of indices and a pointer
00161   static Message& put(Message&, const vector<size_t>&, T);
00162   // get using an output iterator
00163   static Message& get_iter(Message&, T);
00164 };
00165 
00166 
00167 class Message {
00168 
00169 public:
00170   // a class which stores a single message element.  This will either store
00171   // a reference to the data (if copy=false), make an internal copy (if
00172   // copy=true, and it is large enough), or put the data into an internal
00173   // fast buffer (the MsgItemBuf struct).
00174   class MsgItem {
00175   private:
00176     // a very simple struct for storing MsgItem data
00177     struct MsgItemBuf {
00178 #if defined(IPPL_LONGLONG)
00179       unsigned long long d1, d2, d3, d4;
00180 #else
00181       unsigned long d1, d2, d3, d4;
00182 #endif
00183       MsgItemBuf() { }
00184       MsgItemBuf(const MsgItemBuf& m) : d1(m.d1),d2(m.d2),d3(m.d3),d4(m.d4) {}
00185       ~MsgItemBuf() { }
00186     };
00187 
00188   public:
00189     // default constructor
00190     MsgItem() : item(0), elements(0), bytesize(0), needDelete(false) { }
00191 
00192     // regular constructor, with data to store
00193     MsgItem(void *d, unsigned int elems, unsigned int totbytes,
00194             bool needcopy, bool needdel) : item(&defbuf), elements(elems),
00195       bytesize(totbytes), needDelete(needdel) {
00196         if (totbytes > 0 && d != 0) {
00197           if (needcopy) {
00198             if (totbytes <= sizeof(MsgItemBuf)) {
00199               // copy data into internal buffer
00200               needDelete = false;
00201             } else {
00202               // malloc and copy over data
00203               item = malloc(totbytes);
00204               needDelete = true;
00205             }
00206             memcpy(item, d, totbytes);
00207           } else {
00208             // we just store a reference, we do not copy
00209             item = d;
00210           }
00211         } else {
00212           // no data in message
00213           item = 0;
00214           elements = 0;
00215           needDelete = false;
00216         }
00217     }
00218 
00219     // copy constructor
00220     MsgItem(const MsgItem &m) : item(&defbuf), defbuf(m.defbuf),
00221         elements(m.elements), bytesize(m.bytesize), needDelete(m.needDelete) {
00222           // either we just copy the 'item' pointer, or we copy the default buf
00223           if (m.item != &(m.defbuf))
00224             item = m.item;
00225     }
00226 
00227     // destructor
00228     ~MsgItem() { }
00229 
00230     // return our total byte size, number of elements, and elem size
00231     unsigned int numBytes() const { return bytesize; }
00232     unsigned int numElems() const { return elements; }
00233     unsigned int elemSize() const { return (elements>0?bytesize/elements:0); }
00234 
00235     // will we need to delete our data?
00236     bool willNeedDelete() const { return (needDelete && item != 0); }
00237 
00238     // cancel the need to delete the data
00239     void cancelDelete() { needDelete = false; }
00240     
00241     // return our item pointer
00242     void *data() { return item; }
00243 
00244     // delete our data item
00245     void deleteData() {
00246       if (willNeedDelete())
00247         free(item);
00248     }
00249 
00250   private:
00251     // pointer to the item; must be newed/deleted, or pointing to defbuf
00252     void *item;
00253 
00254     // number of individual elements, and total storage size in bytes
00255     unsigned int elements, bytesize;
00256 
00257     // default storage space; used for speed
00258     MsgItemBuf defbuf;
00259 
00260     // do we need to delete this item storage?
00261     bool needDelete;
00262   };
00263 
00264 public:
00265   //
00266   // constructors and destructors
00267   //
00268 
00269   // 'default' constructor: just make an empty message, optionally requesting
00270   // how many items we should preallocate space for
00271   Message(unsigned int numelems = 8)
00272     : numRemoved(0), comm(0), commdata(0), DoCopy(true), DoDelete(true) {
00273     MsgItemList.reserve(numelems);
00274   }
00275 
00276   // destructor: delete all items in this message, as if 'get' had been
00277   // called for all the items
00278   ~Message();
00279 
00280   //
00281   // global Message operations
00282   //
00283 
00284   // return number of items left in this message
00285   size_t size() const { return (MsgItemList.size() - numRemoved); }
00286   size_t removed() const { return numRemoved; }
00287   bool empty() const { return (size() == 0); }
00288 
00289   // returns a reference to the Nth MsgItem.  Note that 'n' refers
00290   // to the index from te first unremoved item, and that 'get' and 'remove'
00291   // will remove the top item.
00292   MsgItem& item(size_t n) { return MsgItemList[n + numRemoved]; }
00293   const MsgItem& item(size_t n) const {
00294     return MsgItemList[n+numRemoved];
00295   }
00296 
00297   // indicate that the next message item should be copied (t) or just
00298   // remembered via a pointer (f)
00299   Message& setCopy(const bool c) {
00300     DoCopy = c;
00301     return *this;
00302   }
00303   bool willCopy() const { return DoCopy; }
00304 
00305   // indicate that the next message item memory should be deleted by this
00306   // object when the Message is deleted (t)
00307   Message& setDelete(const bool c) {
00308     DoDelete = c;
00309     return *this;
00310   }
00311   bool willDelete() const { return DoDelete; }
00312 
00313   // clear the message; deletes all its items
00314   Message& clear();
00315 
00316   // return and remove the next item from this message ... if the item
00317   // does not exist, NULL is returned.  This is similar to get, except that 
00318   // just a void* pointer to the data is returned (instead of having the
00319   // data copied into given storage), and this data is DEFINITELY a malloced
00320   // block of data that the user must deallocate using 'free' (NOT delete).
00321   // Like 'get', after this is called, the top MsgItem is removed.
00322   // If you wish to just access the Nth item's item  pointer, use
00323   // item(N).item  .
00324   void *remove();
00325 
00326   // tell this Message to call the special function 'cleanupMessage' with
00327   // the provided pointer, in case the message is using buffer space
00328   // allocated by the Communicate object.  If no Communicate object has
00329   // been provided, nothing is done
00330   void useCommunicate(Communicate *c, void *d) {
00331     comm = c;
00332     commdata = d;
00333   }
00334 
00335   //
00336   // routines to get and put data out of/into the message.
00337   //
00338 
00339   // NOTES for 'put' routines:
00340   // For intrinsic scalar values, there is one argument: the scalar item.
00341   // For arrays, there are two arguments: the begining and (one-past-end)
00342   // pointers (i.e. like iterators, but restricted to pointers).
00343   // For any other arbitrary type, calling put will in turn call the method
00344   // 'putMessage' in the provided object.  'putMessage' can then call put
00345   // to put in the proper intrinsic-type objects.
00346   //
00347   // When putting in data, you can have the data copied over, or just have
00348   // a pointer stored.  If a pointer is stored (copy=F), you must also
00349   // specify whether this Message object should be responsible for deleting
00350   // the data (delstor=T), or should just leave the storage alone after the
00351   // data has been retrieved with get.  These flags are set by calling
00352   // 'setCopy' and 'setDelete' with the desired setting.  After an item
00353   // has been 'put', the are set back to the default values 'copy=true' and
00354   // 'delete=true'.  You can use them in this way:
00355   //      Message msg;
00356   //      msg.setCopy(false).setDelete(false).put(data);
00357   // copy and delstor are used to increase performance.  They
00358   // should be used in the following circumstances:
00359   //    a. Data to be sent/rec is in a location that will not change before the
00360   // msg is used, but should not be affected after the Message is deleted.
00361   // For this case, use copy=F, delstor=F.
00362   //    b. Data for a message has already had space allocated for it, and so
00363   // does not need to be copied.  Also, since the space has been allocated
00364   // already, it must be freed when the msg is sent.  For this case, use
00365   // copy=F, delstor=T.
00366   //    c. For data that is in a volatile location, it must be copied to new
00367   // storage, so use copy=T, and don't specify any value for delstor (it will
00368   // be ignored if copy=T).
00369 
00370   // general templated version of put
00371   // for an arbitrary type, call the function 'putMessage' with this
00372   // message so that it can put in data any way it wants.  That function
00373   // should return a reference to this message.
00374   // 'putMessage' should be defined as:
00375   //                   Message& putMessage(Message &);
00376   template <class T>
00377   Message& put(const T& val) {
00378     return PutSingleItem<T,
00379                          MessageTypeIntrinsic<T>::builtin,
00380                          MessageTypeIntrinsic<T>::pointer>::put(*this, val);
00381   }
00382 
00383   // specialized versions of put for character strings
00384   /*
00385   Message &put(char *d) {       // null-terminated string
00386     return putmsg((void *)d, sizeof(char), strlen(d) + 1);
00387   }
00388   */
00389   Message &put(const char *d) { // null-terminated string
00390     return putmsg((void *)d, sizeof(char), strlen(d) + 1);
00391   }
00392   // specialized version for string class
00393   Message& put(const string& s) {
00394     int len = s.length() + 1;
00395     put(len);
00396     put(s.c_str());
00397     return *this;
00398   }
00399 
00400   // general templated version of put for two iterators
00401   // Template put using a pair of iterators.  This version is called by
00402   // the public 'put' with two iterators after getting the proper type
00403   // of the data pointed to by the iterators
00404   template <class ForwardIterator>
00405   Message& put(ForwardIterator beg, ForwardIterator end) {
00406     return PutSingleItem<ForwardIterator,
00407                          MessageTypeIntrinsic<ForwardIterator>::builtin,
00408                          MessageTypeIntrinsic<ForwardIterator>::pointer>::put(
00409                            *this, beg, end);
00410   }
00411 
00412   // for using an indirection list
00413   // Template put using an indirection list.  This version is called by
00414   // the public 'put' with an indirection list and a RandomAccessIterator
00415   // after getting the proper type of the data pointed to by the iterator
00416   template <class RandomAccessIterator>
00417   Message& put(const vector<size_t>& indices,
00418                RandomAccessIterator beg) {
00419     return PutSingleItem<RandomAccessIterator,
00420       MessageTypeIntrinsic<RandomAccessIterator>::builtin,
00421       MessageTypeIntrinsic<RandomAccessIterator>::pointer>::put(*this, 
00422                                                                 indices, beg);
00423   }
00424 
00425   // general put routine; called by other cases
00426   // arguments are the item, its element size (in bytes),
00427   // and how many items total to copy (if 0, this is a scalar).
00428   Message &putmsg(void *, int, int = 0);
00429 
00430 
00431   // general templated version of get, for a ref or a pointer
00432   // for an arbitrary type, call the function 'getMessage' with this
00433   // message so that it can get data any way it wants.  That function
00434   // should return a reference to this message.
00435   // 'getMessage' should be defined as:
00436   //                   Message& getMessage(Message &);
00437   // NOTE: the argument is a const ref, but will be cast to non-const,
00438   // to eliminate warning messages about anachronisms.
00440   // general templated version of get.
00441   template <class T>
00442   Message& get(const T& cval) {
00443     T& val = const_cast<T&>(cval);
00444     return PutSingleItem<T,
00445                          MessageTypeIntrinsic<T>::builtin,
00446                          MessageTypeIntrinsic<T>::pointer>::get(*this, val);
00447   }
00448 
00449   // specialized version for string class
00450   Message& get(const string& s) {
00451     string& ncs = const_cast<string&>(s);
00452     int len;
00453     get(len);
00454     char* cstring = new char[len];
00455     get(cstring);
00456     ncs = cstring;
00457     delete [] cstring;
00458     return *this;
00459   }
00460 
00461   // this version of get just removes the data without copying it
00462   Message &get() { return getmsg(0); }
00463 
00464   // an iterator-based version of get, which uses a general
00465   // iterator approach to copy the data out of the storage into
00466   // the space pointed to by the given iterator
00468   // Template get using a pair of iterators.  This version is called by
00469   // the public 'get' with two iterators after getting the proper type
00470   // of the data pointed to by the iterators
00471   template <class OutputIterator>
00472   Message& get_iter(OutputIterator o) {
00473     return PutSingleItem<OutputIterator,
00474       MessageTypeIntrinsic<OutputIterator>::builtin,
00475       MessageTypeIntrinsic<OutputIterator>::pointer>::get_iter(*this, o);
00476   }
00477 
00478   // general get routine; called by other cases
00479   // arguments are the location where to write the data, and the type
00480   // of the data.
00481   Message &getmsg(void *);
00482 
00483 private:
00484   // MsgItem's that are in this message
00485   vector<MsgItem> MsgItemList;
00486 
00487   // number of elements that have been removed (via get or remove)
00488   size_t numRemoved;
00489 
00490   // should we copy the next added item?  if not, just store pointer
00491   bool DoCopy;
00492 
00493   // should we be responsible for deleting the next item?
00494   bool DoDelete;
00495 
00496   // a Communicate object which should be informed when this object is
00497   // deleted, and a comm-supplied object that it might need
00498   Communicate *comm;
00499   void *commdata;
00500 
00501   // delete the data in the top MsgItem, and remove that item from the top
00502   // (by incrementing numRemoved)
00503   void deleteMsgItem();
00504 };
00505 
00506 
00507 // General template for the put routine:
00508 template<class T>
00509 inline void putMessage(Message &m, const T &t) {
00510   m.put(t);
00511 }
00512 
00513 // for using a pair of iterators
00514 template<class ForwardIterator>
00515 inline void putMessage(Message &m, ForwardIterator beg, ForwardIterator end) {
00516   m.put(beg, end);
00517 }
00518 
00519 // for using an indirection list
00520 template <class RandomAccessIterator>
00521 inline void putMessage(Message &m, const vector<size_t> &v,
00522                        RandomAccessIterator r) {
00523   m.put(v, r);
00524 }
00525 
00526 
00527 // General template for the get routine:
00528 template<class T>
00529 inline void getMessage(Message &m, T &t) {
00530   m.get(t);
00531 }
00532 
00533 // this templated version of getMessage is for arbitrary pointers
00534 template<class T>
00535 inline void getMessage(Message &m, T *t) {
00536   m.get_iter(t);
00537 }
00538 
00539 // this templated version of getMessage is for arbitrary pointers
00540 template<class T>
00541 inline void getMessage(Message &m, T *t, T *) {
00542   m.get_iter(t);
00543 }
00544 
00545 // an iterator-based version of get, which uses a general
00546 // iterator approach to copy the data out of the storage into
00547 // the space pointed to by the given iterator
00548 template<class OutputIterator>
00549 void getMessage_iter(Message &m, OutputIterator o) {
00550   m.get_iter(o);
00551 }
00552 
00553 
00554 #if ( defined(IPPL_MPIXX) || defined(IPPL_PM) )
00555 #define main mpi_main
00556 extern "C" {
00557   int mpi_main(int, char**);
00558 }
00559 #endif // IPPL_MPIXX || IPPL_PM
00560 
00561 #include "Message/Message.cpp"
00562 
00563 #endif // MESSAGE_H
00564 
00565 /***************************************************************************
00566  * $RCSfile: Message.h,v $   $Author: adelmann $
00567  * $Revision: 1.1.1.1 $   $Date: 2003/01/23 07:40:28 $
00568  * IPPL_VERSION_ID: $Id: Message.h,v 1.1.1.1 2003/01/23 07:40:28 adelmann Exp $ 
00569  ***************************************************************************/

Generated on Mon Jan 16 13:23:52 2006 for IPPL by  doxygen 1.4.6