blitz  Version 1.0.2
memblock.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 /***************************************************************************
3  * blitz/memblock.h MemoryBlock<T> and MemoryBlockReference<T>
4  *
5  * $Id$
6  *
7  * Copyright (C) 1997-2011 Todd Veldhuizen <tveldhui@acm.org>
8  *
9  * This file is a part of Blitz.
10  *
11  * Blitz is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation, either version 3
14  * of the License, or (at your option) any later version.
15  *
16  * Blitz is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with Blitz. If not, see <http://www.gnu.org/licenses/>.
23  *
24  * Suggestions: blitz-devel@lists.sourceforge.net
25  * Bugs: blitz-support@lists.sourceforge.net
26  *
27  * For more information, please see the Blitz++ Home Page:
28  * https://sourceforge.net/projects/blitz/
29  *
30  ***************************************************************************/
31 
32 #ifndef BZ_MEMBLOCK_H
33 #define BZ_MEMBLOCK_H
34 
35 #include <blitz/blitz.h>
36 #include <blitz/simdtypes.h>
37 #ifdef BZ_HAVE_BOOST_SERIALIZATION
38 #include <boost/serialization/serialization.hpp>
39 #include <boost/serialization/base_object.hpp>
40 #include <boost/serialization/split_member.hpp>
41 #include <boost/serialization/array.hpp>
42 #include <boost/serialization/collection_size_type.hpp>
43 #include <boost/serialization/nvp.hpp>
44 #endif
45 #include <stddef.h> // diffType
46 
47 namespace blitz {
48 
53 };
54 
55 // This function makes it possible for users to make sure threadsafety
56 // is enabled.
57 inline bool isThreadsafe() {
58 #ifdef BZ_THREADSAFE
59  return true;
60 #else
61  return false;
62 #endif
63 };
64 
65 
66 // Forward declaration of MemoryBlockReference
67 template<typename T_type> class MemoryBlockReference;
68 
69 // Class MemoryBlock provides a reference-counted block of memory. This block
70 // may be referred to by multiple vector, matrix and array objects. The memory
71 // is automatically deallocated when the last referring object is destructed.
72 // MemoryBlock may be subclassed to provide special allocators.
73 template<typename P_type>
74 class MemoryBlock {
75 
76  friend class MemoryBlockReference<P_type>;
77 
78 public:
79  typedef P_type T_type;
80 
81 protected:
82  explicit MemoryBlock(sizeType items)
83  {
84  // pad the length to vecWidth, if not already done
85  const int w = simdTypes<T_type>::vecWidth;
86  const int mod = items%w;
87  if (mod>0)
88  items += simdTypes<T_type>::vecWidth-mod;
89  BZASSERT(items%w==0);
90 
91  length_ = items;
93 
94  BZASSERT(dataBlockAddress_ != 0);
95 
96  references_ = 1;
97  BZ_MUTEX_INIT(mutex)
98  }
99 
103  {
104  length_ = length;
105  data_ = data;
107  references_ = 1;
108  BZ_MUTEX_INIT(mutex);
109  allocatedByUs_ = false;
110  }
111 
112  virtual ~MemoryBlock()
113  {
114  if (dataBlockAddress_)
115  {
116 #ifdef BZ_DEBUG_LOG_ALLOCATIONS
117  cout << "MemoryBlock: freed " << setw(8) << length_
118  << " at " << ((void *)dataBlockAddress_) << endl;
119 #endif
120 
121  deallocate();
122  }
123 
124  BZ_MUTEX_DESTROY(mutex)
125  }
126 
127  // set mutex locking policy and return true if successful
128  bool doLock(bool lockingPolicy)
129  {
130 #if defined(BZ_THREADSAFE) && !defined(BZ_THREADSAFE_USE_ATOMIC)
131  if (mutexLocking_ == lockingPolicy) { // already set
132  return true;
133  }
134  else if (references_ <= 1) { // no multiple references, safe to change
135  mutexLocking_ = lockingPolicy;
136  return true;
137  }
138  return false; // unsafe to change
139 #elif defined(BZ_THREADSAFE_USE_ATOMIC)
140  // with locking we consider the request successful
141  return true;
142 #else
143  // without threadsafety we return false if user wants to enable locking
144  return !lockingPolicy;
145 #endif
146  }
147 
148  /* Note that the the MemoryBlock will be created with reference
149  count 1, so there is no need to call addReference immediately
150  upon creating it. (The creator obviously does have to call
151  removeReference, though.) This avoids the initial mutex lock. */
153  {
154 #ifdef BZ_DEBUG_LOG_REFERENCES
155  BZ_MUTEX_LOCK(mutex)
156  const int refcount = ++references_;
157  BZ_MUTEX_UNLOCK(mutex)
158 
159  cout << "MemoryBlock: reffed " << setw(8) << length_
160  << " at " << ((void *)dataBlockAddress_) << " (r="
161  << refcount << ")" << endl;
162 #else
163  BZ_MUTEX_LOCK(mutex)
164  ++references_;
165  BZ_MUTEX_UNLOCK(mutex)
166 #endif
167  }
168 
169  T_type* restrict data()
170  {
171  return data_;
172  }
173 
174  const T_type* restrict data() const
175  {
176  return data_;
177  }
178 
179  T_type*& dataBlockAddress()
180  {
181  return dataBlockAddress_;
182  }
183 
184  sizeType length() const
185  {
186  return length_;
187  }
188 
190  {
191 
192  BZ_MUTEX_LOCK(mutex)
193  const int refcount = --references_;
194  BZ_MUTEX_UNLOCK(mutex)
195 
196 #ifdef BZ_DEBUG_LOG_REFERENCES
197  cout << "MemoryBlock: dereffed " << setw(8) << length_
198  << " at " << ((void *)dataBlockAddress_) << " (r=" << refcount
199  << ")" << endl;
200 #endif
201  return refcount;
202  }
203 
204  int references() const
205  {
206  BZ_MUTEX_LOCK(mutex)
207  const int refcount = references_;
208  BZ_MUTEX_UNLOCK(mutex)
209 
210  return refcount;
211  }
212 
213 protected:
214  inline void allocate(sizeType length);
215  void deallocate();
216 
217 private: // Disabled member functions
219 
221 
224 #ifdef BZ_HAVE_BOOST_SERIALIZATION
225  friend class boost::serialization::access;
226 
227 
231  template<class T_arch>
232  void save(T_arch& ar, const unsigned int version) const {
233 #if defined(BZ_THREADSAFE) && !defined(BZ_THREADSAFE_USE_ATOMIC)
234  ar << mutexLocking_;
235 #endif
236  const boost::serialization::collection_size_type count(length_);
237  ar << BOOST_SERIALIZATION_NVP(count);
238 #ifdef BZ_DEBUG_LOG_ALLOCATIONS
239  cout << "MemoryBlock: serializing " << length_ << " data for MemoryBlock at "
240  << ((void*)this) << endl;
241 #endif
242  if(length_>0)
243  ar << boost::serialization::make_array(data_, length_);
244  };
245 
246  template<class T_arch>
247  void load(T_arch& ar, const unsigned int version) {
248 #if defined(BZ_THREADSAFE) && !defined(BZ_THREADSAFE_USE_ATOMIC)
249  ar >> mutexLocking_;
250 #endif
251  boost::serialization::collection_size_type count(length_);
252  ar >> BOOST_SERIALIZATION_NVP(count);
253  length_=count;
254  allocate(length_);
255 
256 #ifdef BZ_DEBUG_LOG_ALLOCATIONS
257  cout << "MemoryBlock: unserializing " << length_ << " data for MemoryBlock at "
258  << ((void*)this) << endl;
259 #endif
260 
261  if(length_>0)
262  ar >> boost::serialization::make_array(data_, length_);
263 
264  // initialize the members that are not restored. Note that we
265  // must initialize references_ to 0 here because the
266  // unserialization always adds a reference since the creation of
267  // the MemoryBlock object happens by the boost::serialization
268  // magic.
269  references_ = 0;
270  BZ_MUTEX_INIT(mutex);
271  };
272 
273  BOOST_SERIALIZATION_SPLIT_MEMBER()
274 #endif
275 
276 
277 private: // Data members
278 #if defined(BZ_THREADSAFE) && !defined(BZ_THREADSAFE_USE_ATOMIC)
279  // with atomic reference counts, there is no locking
280  bool mutexLocking_;
281 #endif
282 
285  bool allocatedByUs_;
286 
287  union {
288  T_type * restrict data_;
291  };
292  union {
295  char * dBA_char_;
296  };
298 
299  BZ_REFCOUNT_DECLARE(references_)
300  BZ_MUTEX_DECLARE(mutex)
301 };
302 
303 
304 
305 
306 template<typename P_type>
307 class MemoryBlockReference {
308 
309 public:
310  typedef P_type T_type;
311 
312 protected:
313  T_type * restrict data_;
314 
315 private:
317 
318 #ifdef BZ_HAVE_BOOST_SERIALIZATION
319  friend class boost::serialization::access;
320 
328  template<class T_arch>
329  void save(T_arch& ar, const unsigned int version) const {
330 #ifdef BZ_DEBUG_LOG_ALLOCATIONS
331  cout << "MemoryBlockReference: serializing object at " << ((void*)this) << ", MemoryBlock at "
332  << ((void*)block_) <<endl;
333 #endif
334  ar << block_;
335  ptrdiff_t ptroffset=0;
336  if(block_)
337  ptroffset = data_ - block_->data();
338  boost::serialization::collection_size_type
339  offset(*reinterpret_cast<size_t*>(&ptroffset));
340  ar << BOOST_SERIALIZATION_NVP(offset);
341  };
342 
346  template<class T_arch>
347  void load(T_arch& ar, const unsigned int version) {
348 #ifdef BZ_DEBUG_LOG_ALLOCATIONS
349  cout << "MemoryBlockReference: unserializing at " << ((void*)this) << endl;
350 #endif
351  ar >> block_;
352  addReference();
353  boost::serialization::collection_size_type offset;
354  ar >> BOOST_SERIALIZATION_NVP(offset);
355  ptrdiff_t ptroffset = *reinterpret_cast<ptrdiff_t*>(&offset);
356  if(block_)
357  data_ = block_->data() + ptroffset;
358  else
359  data_ = 0;
360  };
361 
362  BOOST_SERIALIZATION_SPLIT_MEMBER()
363 #endif
364 
365 public:
366 
368  {
369  block_ = 0;
370  // no block, so nothing to add reference to
371  data_ = 0;
372  }
373 
375  {
376  block_ = ref.block_;
377  addReference();
378  data_ = ref.data_ + offset;
379  }
380 
382  preexistingMemoryPolicy deletionPolicy)
383  {
384  // Create a memory block using already allocated memory.
385 
386  // Note: if the deletionPolicy is duplicateData, this must
387  // be handled by the leaf class. In MemoryBlockReference,
388  // this is treated as neverDeleteData; the leaf class (e.g. Array)
389  // must duplicate the data.
390 
391  if ((deletionPolicy == neverDeleteData)
392  || (deletionPolicy == duplicateData)) {
393  // in this case, we do not need a MemoryBlock to ref-count the data
394  block_ = 0;
395  }
396  else if (deletionPolicy == deleteDataWhenDone) {
397  block_ = new MemoryBlock<T_type>(length, data);
398 
399 #ifdef BZ_DEBUG_LOG_ALLOCATIONS
400  cout << "MemoryBlockReference: created MemoryBlock at "
401  << ((void*)block_) << endl;
402 #endif
403  }
404  // creating a MemoryBlock automatically sets it to one
405  // reference, so we do no longer need to add a reference in
406  // the constructor.
407 
408  data_ = data;
409  }
410 
412  {
413  block_ = new MemoryBlock<T_type>(items);
414  // creating a MemoryBlock automatically sets it to one
415  // reference, so we do no longer need to add a reference in
416  // the constructor.
417  data_ = block_->data();
418 
419 #ifdef BZ_DEBUG_LOG_ALLOCATIONS
420  cout << "MemoryBlockReference: created MemoryBlock at "
421  << ((void*)block_) << endl;
422 #endif
423  }
424 
426  {
427  blockRemoveReference();
428  }
429 
431  bool isVectorAligned(size_t offset) const
432  { return simdTypes<T_type>::isVectorAligned(data_ + offset); }
433 
435  sizeType blockLength() const { return block_->length(); };
436 
437 protected:
438 #ifdef BZ_TESTSUITE
439 public:
440 #endif
441  int numReferences() const
442  {
443  if (block_)
444  return block_->references();
445 #ifdef BZ_DEBUG_LOG_REFERENCES
446  cout << "Invalid reference count for data at "<< data_ << endl;
447 #endif
448  return -1;
449  }
450 
451  bool lockReferenceCount(bool lockingPolicy) const
452  {
453  if (block_)
454  return block_->doLock(lockingPolicy);
455  // if we have no block, consider request successful
456 #ifdef BZ_DEBUG_LOG_REFERENCES
457  cout << "No reference count locking for data at "<< data_ << endl;
458 #endif
459  return true;
460  }
461 
463  {
464  blockRemoveReference();
465  block_ = 0;
466  // no block, so nothing to add reference to
467  data_ = 0;
468  }
469 
471  {
472  blockRemoveReference();
473  block_ = ref.block_;
474  addReference();
475  data_ = ref.data_ + offset;
476  }
477 
478  void newBlock(sizeType items)
479  {
480  blockRemoveReference();
481  block_ = new MemoryBlock<T_type>(items);
482  // creating a memory block automatically sets it to one reference
483  data_ = block_->data();
484 
485 #ifdef BZ_DEBUG_LOG_ALLOCATIONS
486  cout << "MemoryBlockReference: created MemoryBlock at "
487  << ((void*)block_) << endl;
488 #endif
489  }
490 
491 private:
493  {
494  const int refcount = removeReference();
495  if (refcount == 0)
496  {
497 #ifdef BZ_DEBUG_LOG_ALLOCATIONS
498  cout << "MemoryBlockReference: no more refs, delete MemoryBlock object at "
499  << ((void*)block_) << endl;
500 #endif
501 
502  delete block_;
503  }
504  }
505 
506  void addReference() const
507  {
508  if (block_) {
509 #ifdef BZ_DEBUG_LOG_REFERENCES
510  cout << "MemoryBlockReference: reffing block at " << ((void*)block_)
511  << endl;
512 #endif
513  block_->addReference();
514  }
515  else {
516 #ifdef BZ_DEBUG_LOG_REFERENCES
517  cout << "MemoryBlockReference:: Skipping reference count for data at "<< data_ << endl;
518 #endif
519  }
520  };
521 
522  int removeReference() const
523  {
524  if (block_) {
525 #ifdef BZ_DEBUG_LOG_REFERENCES
526  cout << "MemoryBlockReference: dereffing block at " << ((void*)block_)
527  << endl;
528 #endif
529  return block_->removeReference();
530  }
531 #ifdef BZ_DEBUG_LOG_REFERENCES
532  cout << "Skipping reference count for data at "<< data_ << endl;
533 #endif
534  return -1;
535  };
536 
538  { }
539 };
540 
541 
542 }
543 
544 #include <blitz/memblock.cc>
545 
546 #endif // BZ_MEMBLOCK_H
void operator=(const MemoryBlock< T_type > &)
Definition: memblock.h:220
#define BZ_MUTEX_INIT(name)
Definition: blitz.h:190
MemoryBlock(sizeType items)
Definition: memblock.h:82
virtual ~MemoryBlock()
Definition: memblock.h:112
void changeToNullBlock()
Definition: memblock.h:462
T_type * dataBlockAddress_
Definition: memblock.h:293
Helper class that defines the width of the simd instructions for a given type.
Definition: simdtypes.h:31
char * dBA_char_
Definition: memblock.h:295
MemoryBlockReference(sizeType length, T_type *data, preexistingMemoryPolicy deletionPolicy)
Definition: memblock.h:381
bool doLock(bool lockingPolicy)
Definition: memblock.h:128
void operator=(const MemoryBlockReference< T_type > &)
Definition: memblock.h:537
P_type T_type
Definition: memblock.h:310
MemoryBlockReference()
Definition: memblock.h:367
simdTypes< T_type >::vecType *restrict dBA_tv_
Definition: memblock.h:294
sizeType blockLength() const
Returns the allocated length of the memory block.
Definition: memblock.h:435
~MemoryBlockReference()
Definition: memblock.h:425
Definition: memblock.h:74
static bool isVectorAligned(const T *restrict pointer)
Test if a pointer to T is simd aligned.
Definition: simdtypes.h:46
void addReference() const
Definition: memblock.h:506
MemoryBlock()
The default constructor is needed for serialization.
Definition: memblock.h:223
Definition: memblock.h:50
void allocate(sizeType length)
const T_type *restrict data() const
Definition: memblock.h:174
#define BZ_MUTEX_UNLOCK(name)
Definition: blitz.h:192
void changeBlock(MemoryBlockReference< T_type > &ref, sizeType offset=0)
Definition: memblock.h:470
void newBlock(sizeType items)
Definition: memblock.h:478
int removeReference()
Definition: memblock.h:189
bool allocatedByUs_
Keeps track of whether the block was preallocated or not.
Definition: memblock.h:223
T_type *& dataBlockAddress()
Definition: memblock.h:179
T_type *restrict data_
Definition: memblock.h:313
sizeType length() const
Definition: memblock.h:184
Definition: array-impl.h:66
P_type T_type
Definition: memblock.h:79
MemoryBlock< T_type > * block_
Definition: memblock.h:316
#define BZ_REFCOUNT_DECLARE(name)
Definition: blitz.h:156
bool isVectorAligned(size_t offset) const
Returns true if the offset from data_ is vector aligned.
Definition: memblock.h:431
size_t sizeType
Definition: blitz.h:110
Definition: memblock.h:67
T_type *restrict data()
Definition: memblock.h:169
void addReference()
Definition: memblock.h:152
void blockRemoveReference()
Definition: memblock.h:492
#define BZ_MUTEX_DESTROY(name)
Definition: blitz.h:193
MemoryBlock(const MemoryBlock< T_type > &)
Definition: memblock.h:218
Definition: memblock.h:51
MemoryBlockReference(sizeType items)
Definition: memblock.h:411
#define BZ_MUTEX_DECLARE(name)
Definition: blitz.h:189
bool lockReferenceCount(bool lockingPolicy) const
Definition: memblock.h:451
int references() const
Definition: memblock.h:204
simdTypes< T_type >::vecType *restrict data_tv_
Definition: memblock.h:289
int removeReference() const
Definition: memblock.h:522
preexistingMemoryPolicy
Definition: memblock.h:49
Definition: memblock.h:52
sizeType length_
Definition: memblock.h:297
MemoryBlock(sizeType length, T_type *data)
Constructor for a preallocated block that should be deleted when we are done?
Definition: memblock.h:102
char *restrict data_char_
Definition: memblock.h:290
int numReferences() const
Definition: memblock.h:441
#define restrict
Definition: compiler.h:95
bool isThreadsafe()
Definition: memblock.h:57
T_type *restrict data_
Definition: memblock.h:288
MemoryBlockReference(MemoryBlockReference< T_type > &ref, sizeType offset=0)
Definition: memblock.h:374
#define BZ_MUTEX_LOCK(name)
Definition: blitz.h:191