00001 /* ******************************************************************************* 00002 * Copyright (c) 2007-2014, Intel Corporation 00003 * 00004 * Redistribution and use in source and binary forms, with or without 00005 * modification, are permitted provided that the following conditions are met: 00006 * 00007 * * Redistributions of source code must retain the above copyright notice, 00008 * this list of conditions and the following disclaimer. 00009 * * Redistributions in binary form must reproduce the above copyright 00010 * notice, this list of conditions and the following disclaimer in the 00011 * documentation and/or other materials provided with the distribution. 00012 * * Neither the name of Intel Corporation nor the names of its contributors 00013 * may be used to endorse or promote products derived from this software 00014 * without specific prior written permission. 00015 * 00016 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00017 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00018 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 00019 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 00020 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00021 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 00022 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00023 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 00024 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00025 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00026 ********************************************************************************/ 00027 00028 /* 00029 Marshalling interface for distributed CnC. 00030 */ 00031 00032 #ifndef _CNC_SERIALIZER_H_ 00033 #define _CNC_SERIALIZER_H_ 00034 00035 #include <cnc/internal/cnc_api.h> 00036 #include <cnc/internal/scalable_object.h> 00037 #include <complex> 00038 #include <vector> 00039 00040 #include <cnc/internal/cnc_stddef.h> // size_type 00041 00042 #include <memory> // std::allocator 00043 00044 namespace CnC 00045 { 00046 class CNC_API serializer; 00047 class no_alloc{}; 00048 00049 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00050 00051 /** 00052 \defgroup serialization Serialization 00053 @{ 00054 00055 There are three possible ways of setting up the serialization 00056 of your struct resp. class: 00057 00058 \defgroup ser_bit Serialization of simple structs/classes without pointers or virtual functions. 00059 @{ 00060 If your struct does not contain pointers (nor virtual 00061 functions), instances of it may be serialized by simply 00062 copying them bytewise. In this case, it suffices to 00063 add your struct to the category bitwise_serializable which is done 00064 by providing the following global function: 00065 \code 00066 inline bitwise_serializable serializer_category( const Your_Class * ) { 00067 return bitwise_serializable(); 00068 } 00069 \endcode 00070 As a short-cut, you can just use #CNC_BITWISE_SERIALIZABLE( T ) to declare your class as bitwise serializable: 00071 \code 00072 CNC_BITWISE_SERIALIZABLE( my_class ); 00073 \endcode 00074 @} 00075 \defgroup ser_exp Non bitwise serializable objects 00076 @{ 00077 You'll have to provide a serializer function for your class. 00078 One way of doing this is to derive your class from 00079 serializable and to provide a method "serialize": 00080 \code 00081 class Your_class : public serializable { 00082 ... 00083 public: 00084 void serialize( CnC::serializer & ); 00085 ... 00086 }; 00087 \endcode 00088 In the implementation of this serialize method you should 00089 use the operator& of the serializer class. Here is an example: 00090 \code 00091 class Your_class : public serializable { 00092 private: 00093 double x; 00094 float y; 00095 int len; 00096 char* arr; // array of lenth len 00097 ... 00098 public: 00099 void serialize( CnC::serializer & buf ) { 00100 buf & x 00101 & y 00102 & len; 00103 buf & CnC::chunk< char >( arr, len_arr ); // chunk declared below 00104 ... 00105 } 00106 } 00107 \endcode 00108 This method will called for both packing and unpacking 00109 (what will actually be done, depends on the internal mode 00110 of the serializer.) 00111 \n 00112 Alternatively, you can provide a global function \n 00113 \code 00114 void serialize( serializer& buf, Your_class& obj ); 00115 \endcode 00116 which should use the operator& of the serializer class 00117 (same as for method 2. above). This is useful in cases 00118 where you want to leave your class unchanged. 00119 @} 00120 \defgroup ser_ptr Marshalling pointer types (e.g. items which are pointers) 00121 @{ 00122 A common optimization used by CnC programs targeting the 00123 shared-memory runtime is to store pointers to large objects 00124 inside of item collections instead of copies of the objects. 00125 This is done to minimize the overhead of copying into and out 00126 of the item collection. Programs written in this style can be 00127 sued with distCnC, but special care needs to be taken. 00128 \n 00129 Distributed CnC requires that data stored in item collections be 00130 serializable in order to communicate data between different 00131 processes. Consequently, the programmer must modify such 00132 pointer-using CnC programs in two ways in order to use the 00133 distributed-memory runtime. First, the programmer must provide a 00134 specialization of CnC::serialize for the pointer type pointing to T: 00135 \code 00136 void CnC::serialize( CnC::serializer & ser, T *& ptr ) { 00137 ser & CnC::chunk< Your_class[, allocator] >( ptr, 1 ); 00138 } 00139 \endcode 00140 Please refer to CnC::chunk to learn about appropriate memory 00141 management. 00142 \n 00143 If the pointer represents a single object and not a C-style array, 00144 then the programmer may the use convenience macro: 00145 \code CNC_POINTER_SERIALIZABLE( T ); \endcode 00146 This macro implements the above method for the case when the 00147 pointer refers to only a single object (length == 1). Note that 00148 if T is a type that contains template parameters, the programmer 00149 should create a typedef of the specific type being serialized and 00150 use this type as the argument for the macro. 00151 \n 00152 Next, programmers must ensure that type T itself implements a 00153 default constructor and is serializable by itself. The latter 00154 as achieved as described above. 00155 It is the same method that programmers must provide if storing 00156 copies of objects instead of pointers to objects. 00157 \n 00158 Reading from and writing to item collections is not different in 00159 this style of writing CnC programs. However, there are a few 00160 things to note: 00161 \n 00162 When an object is communicated from one process to another, an 00163 item must be allocated on the receiving end. By default, CnC::chunk 00164 uses the default allocator for this and so requires the default constructor 00165 of T. After object creation, any fields are updated by the provided 00166 serialize methods. Thios default behavior simplifies the object 00167 allocation/deallocation: most of it is done automatically behind the scenes. 00168 The programmer should just pass an unitialized pointer to 00169 get(). The pointer will then point to the allocated object/array after 00170 the get completes. 00171 \n 00172 In advanced and potentially dangerous setups, the programmer might wish 00173 to store the object into memory that has been previously allocated. 00174 This can be done by an explicit copy and explicit object lifetime control. 00175 Please note that such a scenario can easily lead to uses of CnC which 00176 do not comply with CnC's methodology, e.g. no side effects in steps and 00177 dynamic single assigments. 00178 00179 \defgroup seralloc Serialization of std::shared_ptr 00180 @{ 00181 std::shared_ptr are normally supported out-of-the-box (given the underlying type 00182 is serializable (similar to \ref ser_ptr). 00183 00184 Only shared_ptrs to more complex pointer-types require special treatment 00185 to guarantee correct garbage collection. If 00186 - using std::shared:ptr 00187 - AND the pointer-class holds data which is dynamically allocated 00188 - AND this dynamic memory is NOT allocated in the default constructor but during marshalling with CnC::chunk 00189 00190 then (and only then) you might want to use construct_array and destruct_array. 00191 It is probably most convenient to 00192 - use the standard CnC::chunk mechanism during serialization 00193 - allocate dynamic memory in the constructors with construct_array 00194 - and deallocate it in the destructor using destruct_array 00195 00196 This mechanism is used in the cholesky example which comes with this distribution. 00197 00198 Alternatively you can use matching 00199 allocation/deallocation explicitly in your con/destructors and during serialization. 00200 @} 00201 @} 00202 **/ 00203 00204 class serializable { 00205 public: 00206 // must provide member function 00207 // void serialize( CnC::serializer& buf ) { 00208 // /* use serializer::operator& for accessing buf */ 00209 // } 00210 }; 00211 00212 template< class T > inline 00213 void serialize( serializer & buf, T & obj ) { 00214 obj.serialize( buf ); 00215 } 00216 00217 00218 /// Specifies serialization category: explicit serialization via a "serialize" function 00219 class explicitly_serializable {}; 00220 00221 /// General case: a type belongs to the category explicitly_serializable, 00222 /// unless there is a specialization of the function template 00223 /// "serializer_category" (see below). 00224 template< class T > inline 00225 explicitly_serializable serializer_category( const T * ) 00226 { 00227 return explicitly_serializable(); 00228 } 00229 00230 /// simple structs/classes are bitwise serializable. 00231 00232 /// Specifies serialization category: byte-wise copy 00233 class bitwise_serializable {}; 00234 00235 /// \def CNC_BITWISE_SERIALIZABLE( T ) 00236 /// Convenience macro for defining that a type should be treated 00237 /// as bitwise serializable: 00238 # define CNC_BITWISE_SERIALIZABLE( T ) \ 00239 inline CnC::bitwise_serializable serializer_category( const T * ) { \ 00240 return CnC::bitwise_serializable(); \ 00241 } 00242 00243 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00244 00245 template< class A, class B > inline 00246 void serialize( CnC::serializer & buf, std::pair< A, B > & obj ) { 00247 buf & obj.first & obj.second; 00248 } 00249 00250 template< class T > 00251 inline CnC::bitwise_serializable serializer_category( const std::complex< T > * ) { 00252 return CnC::bitwise_serializable(); 00253 } 00254 00255 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00256 00257 /** 00258 \class chunk 00259 \brief Serialization of arrays with and without automatic memory handling. 00260 00261 Specified (to serializer::operator&) via 00262 \code 00263 CnC::chunk< type[, allocator] >( your_ptr, len ) 00264 \endcode 00265 where your_ptr is a pointer variable to your array of <type>s. 00266 00267 If the allocator is the special type CnC::no_alloc, the runtime assumes that 00268 the programer appropriately allocates (if ser.is_unpacking()) 00269 and deallocates (if ser:is_cleaning_up()) the array. This implies that 00270 the using serialize method/function needs to make sure it passes valid and correct 00271 pointers to chunk. 00272 00273 If not CnC::no_alloc , the Allocator class must meet "allocator" requirements of ISO C++ Standard, Section 20.1.5 00274 00275 When unpacking, your_ptr must be NULL, and it is allocated automatically using 00276 the given allocator and when cleaning up, your_ptr is deallocated accordingly. 00277 00278 If using an allocator (in particular hre default std::allocator) object/array 00279 allocation and deallocation is greatly simplified for the programmer. 00280 There is no need to allocate and/or deallocate objects/arrays manually. 00281 **/ 00282 template< class T, class Allocator = std::allocator< T > > 00283 struct chunk 00284 { 00285 chunk( T *& arr, size_type len ) : m_arrVar( arr ), m_len( len ) {} 00286 template< template< class I > class Range, class J > 00287 chunk( T *& arr, const Range< J > & range ) 00288 : m_arrVar( arr ), m_len( range.size() ) 00289 { 00290 CNC_ASSERT( range.begin() == 0 ); 00291 } 00292 00293 T *& m_arrVar; 00294 size_type m_len; 00295 }; 00296 00297 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00298 00299 /// Convenience macro for declaring serializable pointers. 00300 /// The object being pointed to should be serializable. 00301 00302 # define CNC_POINTER_SERIALIZABLE( T ) \ 00303 namespace CnC { \ 00304 void serialize( serializer & ser, T *& t ) { \ 00305 ser & chunk< T >(t, 1); \ 00306 } \ 00307 } 00308 00309 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00310 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00311 00312 /// \defgroup serspec Automatic serialization of built-in types. 00313 /// They can be copied byte-wise. 00314 /// 00315 /// In order to add your own class to the category bitwise_serializable, 00316 /// add a specialization of "serializer_category" returning bitwise_serializable for your class. 00317 /// (e.g. by using CnC::CNC_BITWISE_SERIALIZABLE). 00318 /// @{ 00319 CNC_BITWISE_SERIALIZABLE( bool ); ///< bool is bitwise serializable 00320 CNC_BITWISE_SERIALIZABLE( char ); ///< char is bitwise serializable 00321 CNC_BITWISE_SERIALIZABLE( signed char ); ///< signed char is bitwise serializable 00322 CNC_BITWISE_SERIALIZABLE( unsigned char ); ///< unsigend char is bitwise serializable 00323 CNC_BITWISE_SERIALIZABLE( short ); ///< short is bitwise serializable 00324 CNC_BITWISE_SERIALIZABLE( unsigned short ); ///< unsigend short is bitwise serializable 00325 CNC_BITWISE_SERIALIZABLE( int ); ///< int is bitwise serializable 00326 CNC_BITWISE_SERIALIZABLE( unsigned int ); ///< unsigned int is bitwise serializable 00327 CNC_BITWISE_SERIALIZABLE( long ); ///< long is bitwise serializable 00328 CNC_BITWISE_SERIALIZABLE( unsigned long ); ///< unsigned long is bitwise serializable 00329 CNC_BITWISE_SERIALIZABLE( long long ); ///< long long is bitwise serializable 00330 CNC_BITWISE_SERIALIZABLE( unsigned long long ); ///< unsigned long long is bitwise serializable 00331 CNC_BITWISE_SERIALIZABLE( float ); ///< float is bitwise serializable 00332 CNC_BITWISE_SERIALIZABLE( double ); ///< double is bitwise serializable 00333 CNC_BITWISE_SERIALIZABLE( long double ); ///< long double is bitwise serializable 00334 /// @} 00335 00336 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00337 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00338 00339 namespace Internal { 00340 class Buffer; 00341 class BufferAccess; 00342 } 00343 00344 /// \brief Handles serilialization of data-objects. 00345 /// 00346 /// objects of this class are passed to serialization methods/functions. 00347 /// Try to use operator & only; using anything else is potentially dangerous. 00348 class CNC_API serializer : public Internal::scalable_object 00349 { 00350 public: 00351 enum Mode { 00352 MODE_PACKED_SIZE, 00353 MODE_PACK, 00354 MODE_UNPACK, 00355 MODE_CLEANUP 00356 }; 00357 00358 inline serializer( bool addCRC = false, bool addSize = false ); 00359 inline ~serializer(); 00360 00361 /// (Re)allocates a buffer of the given size. 00362 inline void resize( size_type len ); 00363 00364 /// Swap internal representations of the given two serializers. 00365 friend void swap( serializer & s1, serializer & s2 ); 00366 00367 inline void set_mode_packed_size(); 00368 inline void set_mode_pack(); 00369 inline void set_mode_unpack(); 00370 inline void set_mode_cleanup(); 00371 inline void set_mode_pack( bool addCRC, bool addSize ); 00372 inline void set_mode_unpack( bool addCRC, bool addSize ); 00373 bool is_packing() const { return m_mode == MODE_PACK; } 00374 bool is_unpacking() const { return m_mode == MODE_UNPACK; } 00375 bool is_cleaning_up() const { return m_mode == MODE_CLEANUP; } 00376 size_type get_size() const { return m_packedSize; } 00377 /// @return size of header 00378 inline size_type get_header_size() const; 00379 /// @return the number of bytes already packed into the buffer 00380 inline size_type get_body_size() const; 00381 /// @return total number of bytes in buffer (header + data) 00382 inline size_type get_total_size() const; 00383 /// @return pointer to message header 00384 inline void * get_header() const; 00385 /// @return pointer to the message body (without header) 00386 inline void * get_body() const; 00387 /// @return body size from header, -1 if something goes wrong 00388 inline size_type unpack_header() const; 00389 00390 /// Top-level packing interface, to be used in the "serialize" 00391 /// function of your classes. 00392 /// Applies the serializer to one object resp. an array of objects 00393 /// (e.g. packing them into the serializer or unpacking them from 00394 /// the serializer). 00395 /// Dispatches w.r.t. the packing category of the object's type 00396 /// (i.e. byte-wise copying or object serialization). 00397 template< class T > serializer & operator&( T & var ); 00398 template< class T > serializer & operator&( const T & var ); 00399 //template< class T > serializer & operator&( Internal::array_no_alloc_type< T > a ); //< used via array_no_alloc( yourArr, len ) 00400 template< class T, class Allocator > 00401 serializer & operator&( chunk< T, Allocator > a ); //< used via auto_array( yourArrVar, len ) 00402 00403 class reserved; 00404 /// reserve current position in buffer to be filled later with a call to "complete" 00405 /// rserve/complete supported for bitwise-serializable types only 00406 template< class T > reserved reserve( const T & _obj ); 00407 00408 /// fill in data at position that has been reserved before 00409 /// rserve/complete supported for bitwise-serializable types only 00410 template< class T > void complete( const reserved & r, const T & _obj ); 00411 00412 // Low-level packing interface: 00413 template< class T > inline size_type packed_size( const T * arr, size_type len ) const { 00414 return packed_size( arr, len, serializer_category( arr ) ); 00415 } 00416 template< class T > inline void pack( const T * arr, size_type len ) { 00417 pack( arr, len, serializer_category( arr ) ); 00418 } 00419 template< class T > inline void unpack( T * arr, size_type len ) { 00420 unpack( arr, len, serializer_category( arr ) ); 00421 } 00422 template< class T > inline void cleanup( T * arr, size_type len ) { 00423 cleanup( arr, len, serializer_category( arr ) ); 00424 } 00425 template< class T > inline size_type packed_size( const T & var ) const { return packed_size( &var, 1 ); } 00426 template< class T > inline void pack( const T & var ) { return pack( &var, 1 ); } 00427 template< class T > inline void unpack( T & var ) { return unpack( &var, 1 ); } 00428 template< class T > inline void cleanup( T & var ) { return cleanup( &var, 1 ); } 00429 00430 inline serializer( const serializer& ); 00431 00432 /// Allocates an array of type T and size num in pointer variable arrVar 00433 template< class T, class Allocator = std::allocator< T > > 00434 struct construct_array 00435 { 00436 construct_array( T *& arrVar, size_type num ); 00437 }; 00438 /// destructs the array of type T and isze num at arrVar and resets arrVar to NULL. 00439 template< class T, class Allocator = std::allocator< T > > 00440 struct destruct_array 00441 { 00442 destruct_array( T *& arrVar, size_type num ); 00443 }; 00444 /// @} 00445 template< class T > 00446 struct construct_array< T, no_alloc > 00447 { 00448 construct_array( T *&, size_type ){} 00449 }; 00450 template< class T > 00451 struct destruct_array< T, no_alloc > 00452 { 00453 destruct_array( T *&, size_type ){} 00454 }; 00455 00456 private: 00457 template< class T > inline size_type packed_size( const T * arr, size_type len, bitwise_serializable ) const; 00458 template< class T > inline void pack( const T * arr, size_type len, bitwise_serializable ); 00459 template< class T > inline void unpack( T * arr, size_type len, bitwise_serializable ); 00460 template< class T > inline void cleanup( T * arr, size_type len, bitwise_serializable ); 00461 template< class T > inline size_type packed_size( const T * arr, size_type len, explicitly_serializable ) const; 00462 template< class T > inline void pack( const T * arr, size_type len, explicitly_serializable ); 00463 template< class T > inline void unpack( T * arr, size_type len, explicitly_serializable ); 00464 template< class T > inline void cleanup( T * arr, size_type len, explicitly_serializable ); 00465 00466 00467 /// Don't allow copying 00468 void operator=( const serializer& ); 00469 00470 /// misc: 00471 size_type remaining_capacity() const; 00472 00473 Internal::Buffer * m_buf; 00474 size_type m_packedSize; /// < for MODE_PACKED_SIZE only 00475 Mode m_mode; 00476 00477 friend class Internal::BufferAccess; 00478 }; 00479 00480 /// @} 00481 00482 } // namespace CnC 00483 00484 #include <cnc/internal/dist/Serializer.impl.h> 00485 00486 #endif // _CNC_SERIALIZER_H_