CnC
serializer.h
1 /* *******************************************************************************
2  * Copyright (c) 2007-2014, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * * Redistributions of source code must retain the above copyright notice,
8  * this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of Intel Corporation nor the names of its contributors
13  * may be used to endorse or promote products derived from this software
14  * without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  ********************************************************************************/
27 
28 /*
29  Marshalling interface for distributed CnC.
30  */
31 
32 #ifndef _CNC_SERIALIZER_H_
33 #define _CNC_SERIALIZER_H_
34 
35 #include <cnc/internal/cnc_api.h>
36 #include <cnc/internal/scalable_object.h>
37 #include <complex>
38 #include <vector>
39 
40 #include <cnc/internal/cnc_stddef.h> // size_type
41 
42 #include <memory> // std::allocator
43 
44 namespace CnC
45 {
46  class CNC_API serializer;
47  class no_alloc{};
48 
49  //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50 
51 /**
52  \defgroup serialization Serialization
53  @{
54 
55  There are three possible ways of setting up the serialization
56  of your struct resp. class:
57 
58  \defgroup ser_bit Serialization of simple structs/classes without pointers or virtual functions.
59  @{
60  If your struct does not contain pointers (nor virtual
61  functions), instances of it may be serialized by simply
62  copying them bytewise. In this case, it suffices to
63  add your struct to the category bitwise_serializable which is done
64  by providing the following global function:
65  \code
66  inline bitwise_serializable serializer_category( const Your_Class * ) {
67  return bitwise_serializable();
68  }
69  \endcode
70  As a short-cut, you can just use #CNC_BITWISE_SERIALIZABLE( T ) to declare your class as bitwise serializable:
71  \code
72  CNC_BITWISE_SERIALIZABLE( my_class );
73  \endcode
74  @}
75  \defgroup ser_exp Non bitwise serializable objects
76  @{
77  You'll have to provide a serializer function for your class.
78  One way of doing this is to derive your class from
79  serializable and to provide a method "serialize":
80  \code
81  class Your_class : public serializable {
82  ...
83  public:
84  void serialize( CnC::serializer & );
85  ...
86  };
87  \endcode
88  In the implementation of this serialize method you should
89  use the operator& of the serializer class. Here is an example:
90  \code
91  class Your_class : public serializable {
92  private:
93  double x;
94  float y;
95  int len;
96  char* arr; // array of lenth len
97  ...
98  public:
99  void serialize( CnC::serializer & buf ) {
100  buf & x
101  & y
102  & len;
103  buf & CnC::chunk< char >( arr, len_arr ); // chunk declared below
104  ...
105  }
106  }
107  \endcode
108  This method will called for both packing and unpacking
109  (what will actually be done, depends on the internal mode
110  of the serializer.)
111  \n
112  Alternatively, you can provide a global function \n
113  \code
114  void serialize( serializer& buf, Your_class& obj );
115  \endcode
116  which should use the operator& of the serializer class
117  (same as for method 2. above). This is useful in cases
118  where you want to leave your class unchanged.
119  @}
120  \defgroup ser_ptr Marshalling pointer types (e.g. items which are pointers)
121  @{
122  A common optimization used by CnC programs targeting the
123  shared-memory runtime is to store pointers to large objects
124  inside of item collections instead of copies of the objects.
125  This is done to minimize the overhead of copying into and out
126  of the item collection. Programs written in this style can be
127  sued with distCnC, but special care needs to be taken.
128  \n
129  Distributed CnC requires that data stored in item collections be
130  serializable in order to communicate data between different
131  processes. Consequently, the programmer must modify such
132  pointer-using CnC programs in two ways in order to use the
133  distributed-memory runtime. First, the programmer must provide a
134  specialization of CnC::serialize for the pointer type pointing to T:
135  \code
136  void CnC::serialize( CnC::serializer & ser, T *& ptr ) {
137  ser & CnC::chunk< Your_class[, allocator] >( ptr, 1 );
138  }
139  \endcode
140  Please refer to CnC::chunk to learn about appropriate memory
141  management.
142  \n
143  If the pointer represents a single object and not a C-style array,
144  then the programmer may the use convenience macro:
145  \code CNC_POINTER_SERIALIZABLE( T ); \endcode
146  This macro implements the above method for the case when the
147  pointer refers to only a single object (length == 1). Note that
148  if T is a type that contains template parameters, the programmer
149  should create a typedef of the specific type being serialized and
150  use this type as the argument for the macro.
151  \n
152  Next, programmers must ensure that type T itself implements a
153  default constructor and is serializable by itself. The latter
154  as achieved as described above.
155  It is the same method that programmers must provide if storing
156  copies of objects instead of pointers to objects.
157  \n
158  Reading from and writing to item collections is not different in
159  this style of writing CnC programs. However, there are a few
160  things to note:
161  \n
162  When an object is communicated from one process to another, an
163  item must be allocated on the receiving end. By default, CnC::chunk
164  uses the default allocator for this and so requires the default constructor
165  of T. After object creation, any fields are updated by the provided
166  serialize methods. Thios default behavior simplifies the object
167  allocation/deallocation: most of it is done automatically behind the scenes.
168  The programmer should just pass an unitialized pointer to
169  get(). The pointer will then point to the allocated object/array after
170  the get completes.
171  \n
172  In advanced and potentially dangerous setups, the programmer might wish
173  to store the object into memory that has been previously allocated.
174  This can be done by an explicit copy and explicit object lifetime control.
175  Please note that such a scenario can easily lead to uses of CnC which
176  do not comply with CnC's methodology, e.g. no side effects in steps and
177  dynamic single assigments.
178 
179  \defgroup seralloc Serialization of std::shared_ptr
180  @{
181  std::shared_ptr are normally supported out-of-the-box (given the underlying type
182  is serializable (similar to \ref ser_ptr).
183 
184  Only shared_ptrs to more complex pointer-types require special treatment
185  to guarantee correct garbage collection. If
186  - using std::shared:ptr
187  - AND the pointer-class holds data which is dynamically allocated
188  - AND this dynamic memory is NOT allocated in the default constructor but during marshalling with CnC::chunk
189 
190  then (and only then) you might want to use construct_array and destruct_array.
191  It is probably most convenient to
192  - use the standard CnC::chunk mechanism during serialization
193  - allocate dynamic memory in the constructors with construct_array
194  - and deallocate it in the destructor using destruct_array
195 
196  This mechanism is used in the cholesky example which comes with this distribution.
197 
198  Alternatively you can use matching
199  allocation/deallocation explicitly in your con/destructors and during serialization.
200  @}
201  @}
202 **/
203 
204  class serializable {
205  public:
206  // must provide member function
207  // void serialize( CnC::serializer& buf ) {
208  // /* use serializer::operator& for accessing buf */
209  // }
210  };
211 
212  template< class T > inline
213  void serialize( serializer & buf, T & obj ) {
214  obj.serialize( buf );
215  }
216 
217 
218  /// Specifies serialization category: explicit serialization via a "serialize" function
220 
221  /// General case: a type belongs to the category explicitly_serializable,
222  /// unless there is a specialization of the function template
223  /// "serializer_category" (see below).
224  template< class T > inline
226  {
227  return explicitly_serializable();
228  }
229 
230  /// simple structs/classes are bitwise serializable.
231 
232  /// Specifies serialization category: byte-wise copy
234 
235  /// \def CNC_BITWISE_SERIALIZABLE( T )
236  /// Convenience macro for defining that a type should be treated
237  /// as bitwise serializable:
238 # define CNC_BITWISE_SERIALIZABLE( T ) \
239  static inline CnC::bitwise_serializable serializer_category( const T * ) { \
240  return CnC::bitwise_serializable(); \
241  }
242 
243  //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244 
245  template< class A, class B > inline
246  void serialize( CnC::serializer & buf, std::pair< A, B > & obj ) {
247  buf & obj.first & obj.second;
248  }
249 
250  template< class T >
251  inline CnC::bitwise_serializable serializer_category( const std::complex< T > * ) {
252  return CnC::bitwise_serializable();
253  }
254 
255  //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
256 
257  /**
258  \class chunk
259  \brief Serialization of arrays with and without automatic memory handling.
260 
261  Specified (to serializer::operator&) via
262  \code
263  CnC::chunk< type[, allocator] >( your_ptr, len )
264  \endcode
265  where your_ptr is a pointer variable to your array of <type>s.
266 
267  If the allocator is the special type CnC::no_alloc, the runtime assumes that
268  the programer appropriately allocates (if ser.is_unpacking())
269  and deallocates (if ser:is_cleaning_up()) the array. This implies that
270  the using serialize method/function needs to make sure it passes valid and correct
271  pointers to chunk.
272 
273  If not CnC::no_alloc , the Allocator class must meet "allocator" requirements of ISO C++ Standard, Section 20.1.5
274 
275  When unpacking, your_ptr must be NULL, and it is allocated automatically using
276  the given allocator and when cleaning up, your_ptr is deallocated accordingly.
277 
278  If using an allocator (in particular hre default std::allocator) object/array
279  allocation and deallocation is greatly simplified for the programmer.
280  There is no need to allocate and/or deallocate objects/arrays manually.
281  **/
282  template< class T, class Allocator = std::allocator< T > >
283  struct chunk
284  {
285  chunk( T *& arr, size_type len ) : m_arrVar( arr ), m_len( len ) {}
286  template< template< class I > class Range, class J >
287  chunk( T *& arr, const Range< J > & range )
288  : m_arrVar( arr ), m_len( range.size() )
289  {
290  CNC_ASSERT( range.begin() == 0 );
291  }
292 
293  T *& m_arrVar;
294  size_type m_len;
295  };
296 
297  //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
298 
299  /// Convenience macro for declaring serializable pointers.
300  /// The object being pointed to should be serializable.
301 
302 # define CNC_POINTER_SERIALIZABLE( T ) \
303  namespace CnC { \
304  static inline void serialize( serializer & ser, T *& t ) { \
305  ser & chunk< T >(t, 1); \
306  } \
307  }
308 
309  //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
310  //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
311 
312  /// \defgroup serspec Automatic serialization of built-in types.
313  /// They can be copied byte-wise.
314  ///
315  /// In order to add your own class to the category bitwise_serializable,
316  /// add a specialization of "serializer_category" returning bitwise_serializable for your class.
317  /// (e.g. by using CnC::CNC_BITWISE_SERIALIZABLE).
318  /// @{
319  CNC_BITWISE_SERIALIZABLE( bool ); ///< bool is bitwise serializable
320  CNC_BITWISE_SERIALIZABLE( char ); ///< char is bitwise serializable
321  CNC_BITWISE_SERIALIZABLE( signed char ); ///< signed char is bitwise serializable
322  CNC_BITWISE_SERIALIZABLE( unsigned char ); ///< unsigend char is bitwise serializable
323  CNC_BITWISE_SERIALIZABLE( short ); ///< short is bitwise serializable
324  CNC_BITWISE_SERIALIZABLE( unsigned short ); ///< unsigend short is bitwise serializable
325  CNC_BITWISE_SERIALIZABLE( int ); ///< int is bitwise serializable
326  CNC_BITWISE_SERIALIZABLE( unsigned int ); ///< unsigned int is bitwise serializable
327  CNC_BITWISE_SERIALIZABLE( long ); ///< long is bitwise serializable
328  CNC_BITWISE_SERIALIZABLE( unsigned long ); ///< unsigned long is bitwise serializable
329  CNC_BITWISE_SERIALIZABLE( long long ); ///< long long is bitwise serializable
330  CNC_BITWISE_SERIALIZABLE( unsigned long long ); ///< unsigned long long is bitwise serializable
331  CNC_BITWISE_SERIALIZABLE( float ); ///< float is bitwise serializable
332  CNC_BITWISE_SERIALIZABLE( double ); ///< double is bitwise serializable
333  CNC_BITWISE_SERIALIZABLE( long double ); ///< long double is bitwise serializable
334  /// @}
335 
336  //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
337  //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338 
339  namespace Internal {
340  class Buffer;
341  class BufferAccess;
342  }
343 
344  /// \brief Handles serilialization of data-objects.
345  ///
346  /// objects of this class are passed to serialization methods/functions.
347  /// Try to use operator & only; using anything else is potentially dangerous.
348  class CNC_API serializer : public Internal::scalable_object
349  {
350  public:
351  enum Mode {
352  MODE_PACKED_SIZE,
353  MODE_PACK,
354  MODE_UNPACK,
355  MODE_CLEANUP
356  };
357 
358  inline serializer( bool addCRC = false, bool addSize = false );
359  inline ~serializer();
360 
361  /// (Re)allocates a buffer of the given size.
362  inline void resize( size_type len );
363 
364  /// Swap internal representations of the given two serializers.
365  friend void swap( serializer & s1, serializer & s2 );
366 
367  inline void set_mode_packed_size();
368  inline void set_mode_pack();
369  inline void set_mode_unpack();
370  inline void set_mode_cleanup();
371  inline void set_mode_pack( bool addCRC, bool addSize );
372  inline void set_mode_unpack( bool addCRC, bool addSize );
373  bool is_packing() const { return m_mode == MODE_PACK; }
374  bool is_unpacking() const { return m_mode == MODE_UNPACK; }
375  bool is_cleaning_up() const { return m_mode == MODE_CLEANUP; }
376  size_type get_size() const { return m_packedSize; }
377  /// @return size of header
378  inline size_type get_header_size() const;
379  /// @return the number of bytes already packed into the buffer
380  inline size_type get_body_size() const;
381  /// @return total number of bytes in buffer (header + data)
382  inline size_type get_total_size() const;
383  /// @return pointer to message header
384  inline void * get_header() const;
385  /// @return pointer to the message body (without header)
386  inline void * get_body() const;
387  /// @return body size from header, -1 if something goes wrong
388  inline size_type unpack_header() const;
389 
390  /// Top-level packing interface, to be used in the "serialize"
391  /// function of your classes.
392  /// Applies the serializer to one object resp. an array of objects
393  /// (e.g. packing them into the serializer or unpacking them from
394  /// the serializer).
395  /// Dispatches w.r.t. the packing category of the object's type
396  /// (i.e. byte-wise copying or object serialization).
397  template< class T > serializer & operator&( T & var );
398  template< class T > serializer & operator&( const T & var );
399  //template< class T > serializer & operator&( Internal::array_no_alloc_type< T > a ); //< used via array_no_alloc( yourArr, len )
400  template< class T, class Allocator >
401  serializer & operator&( chunk< T, Allocator > a ); //< used via auto_array( yourArrVar, len )
402 
403  class reserved;
404  /// reserve current position in buffer to be filled later with a call to "complete"
405  /// rserve/complete supported for bitwise-serializable types only
406  template< class T > reserved reserve( const T & _obj );
407 
408  /// fill in data at position that has been reserved before
409  /// rserve/complete supported for bitwise-serializable types only
410  template< class T > void complete( const reserved & r, const T & _obj );
411 
412  // Low-level packing interface:
413  template< class T > inline size_type packed_size( const T * arr, size_type len ) const {
414  return packed_size( arr, len, serializer_category( arr ) );
415  }
416  template< class T > inline void pack( const T * arr, size_type len ) {
417  pack( arr, len, serializer_category( arr ) );
418  }
419  template< class T > inline void unpack( T * arr, size_type len ) {
420  unpack( arr, len, serializer_category( arr ) );
421  }
422  template< class T > inline void cleanup( T * arr, size_type len ) {
423  cleanup( arr, len, serializer_category( arr ) );
424  }
425  template< class T > inline size_type packed_size( const T & var ) const { return packed_size( &var, 1 ); }
426  template< class T > inline void pack( const T & var ) { return pack( &var, 1 ); }
427  template< class T > inline void unpack( T & var ) { return unpack( &var, 1 ); }
428  template< class T > inline void cleanup( T & var ) { return cleanup( &var, 1 ); }
429 
430  inline serializer( const serializer& );
431 
432  /// Allocates an array of type T and size num in pointer variable arrVar
433  template< class T, class Allocator = std::allocator< T > >
435  {
436  construct_array( T *& arrVar, size_type num );
437  };
438  /// destructs the array of type T and isze num at arrVar and resets arrVar to NULL.
439  template< class T, class Allocator = std::allocator< T > >
441  {
442  destruct_array( T *& arrVar, size_type num );
443  };
444  /// @}
445  template< class T >
446  struct construct_array< T, no_alloc >
447  {
448  construct_array( T *&, size_type ){}
449  };
450  template< class T >
451  struct destruct_array< T, no_alloc >
452  {
453  destruct_array( T *&, size_type ){}
454  };
455 
456  private:
457  template< class T > inline size_type packed_size( const T * arr, size_type len, bitwise_serializable ) const;
458  template< class T > inline void pack( const T * arr, size_type len, bitwise_serializable );
459  template< class T > inline void unpack( T * arr, size_type len, bitwise_serializable );
460  template< class T > inline void cleanup( T * arr, size_type len, bitwise_serializable );
461  template< class T > inline size_type packed_size( const T * arr, size_type len, explicitly_serializable ) const;
462  template< class T > inline void pack( const T * arr, size_type len, explicitly_serializable );
463  template< class T > inline void unpack( T * arr, size_type len, explicitly_serializable );
464  template< class T > inline void cleanup( T * arr, size_type len, explicitly_serializable );
465 
466 
467  /// Don't allow copying
468  void operator=( const serializer& );
469 
470  /// misc:
471  size_type remaining_capacity() const;
472 
473  Internal::Buffer * m_buf;
474  size_type m_packedSize; /// < for MODE_PACKED_SIZE only
475  Mode m_mode;
476 
477  friend class Internal::BufferAccess;
478  };
479 
480  /// @}
481 
482 } // namespace CnC
483 
484 #include <cnc/internal/dist/Serializer.impl.h>
485 
486 #endif // _CNC_SERIALIZER_H_
explicitly_serializable serializer_category(const T *)
Definition: serializer.h:225
Serialization of arrays with and without automatic memory handling.
Definition: serializer.h:283
CnC API.
Definition: cnc.h:49
CNC_BITWISE_SERIALIZABLE(bool)
bool is bitwise serializable
simple structs/classes are bitwise serializable.
Definition: serializer.h:233
Specifies serialization category: explicit serialization via a "serialize" function.
Definition: serializer.h:219
Handles serilialization of data-objects.
Definition: serializer.h:348
Allocates an array of type T and size num in pointer variable arrVar.
Definition: serializer.h:434
destructs the array of type T and isze num at arrVar and resets arrVar to NULL.
Definition: serializer.h:440