source: XIOS/dev/dev_olga/src/extern/blitz/include/blitz/memblock.h @ 1022

Last change on this file since 1022 was 1022, checked in by mhnguyen, 7 years ago
File size: 14.9 KB
Line 
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
47BZ_NAMESPACE(blitz)
48
49enum preexistingMemoryPolicy { 
50  duplicateData, 
51  deleteDataWhenDone, 
52  neverDeleteData
53};
54
55// This function makes it possible for users to make sure threadsafety
56// is enabled.
57inline bool isThreadsafe() {
58#ifdef BZ_THREADSAFE
59  return true;
60#else
61  return false;
62#endif
63};
64
65
66// Forward declaration of MemoryBlockReference
67template<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.
73template<typename P_type>
74class MemoryBlock {
75
76    friend class MemoryBlockReference<P_type>;
77
78public:
79    typedef P_type T_type;
80
81protected:
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;
92        allocate(length_);
93
94        BZASSERT(dataBlockAddress_ != 0);
95
96        references_ = 1;
97        BZ_MUTEX_INIT(mutex)
98    }
99
100  /** Constructor for a preallocated block that should be deleted when
101      we are done? */
102    MemoryBlock(sizeType length, T_type* data)
103    {
104      length_ = length;
105      data_ = data;
106      dataBlockAddress_ = 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. */
152    void          addReference()
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
189    int           removeReference()
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
213protected:
214    inline void allocate(sizeType length);
215    void deallocate();
216
217private:   // Disabled member functions
218  MemoryBlock(const MemoryBlock<T_type>&) { };
219
220  void operator=(const MemoryBlock<T_type>&) {};
221
222  /** The default constructor is needed for serialization. */
223  MemoryBlock() {};
224#ifdef BZ_HAVE_BOOST_SERIALIZATION
225    friend class boost::serialization::access;
226
227
228  /** Implementation of boost::serialization. It doesn't make sense to
229      save the mutex, the reference count, or the allocation flags,
230      since upon restoring, we must necessarily reallocate. */
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
277private:   // 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  /** Keeps track of whether the block was preallocated or not. This
283      affects potential alignment so must be taken into account when
284      we delete. */
285  bool allocatedByUs_;
286
287 union {
288    T_type * restrict     data_;
289   typename simdTypes<T_type>::vecType * restrict data_tv_;
290   char * restrict        data_char_;
291  };
292 union {
293   T_type *              dataBlockAddress_;
294   typename simdTypes<T_type>::vecType * restrict dBA_tv_;
295   char *                dBA_char_;
296 };
297  sizeType              length_;
298
299    BZ_REFCOUNT_DECLARE(references_)
300    BZ_MUTEX_DECLARE(mutex)
301};
302
303
304
305
306template<typename P_type>
307class MemoryBlockReference {
308
309public:
310    typedef P_type T_type;
311
312protected:
313    T_type * restrict data_;
314
315private:
316    MemoryBlock<T_type>* block_;
317
318#ifdef BZ_HAVE_BOOST_SERIALIZATION
319    friend class boost::serialization::access;
320
321  /** Serialization operator. This is a bit hacky, because we need to
322      restore the data pointer as part of the skeleton, not
323      content. For this reason, we serialize the offset as a
324      collection_size_item even though it's a signed type. This makes
325      the serialization code treat it as part of the skeleton and not
326      content, which is what we want.
327  */
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
343  /** Unserialization operator. See comment for the save method for
344      the reinterpret_cast hack.
345  */
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
365public:
366
367    MemoryBlockReference()
368    {
369        block_ = 0;
370        // no block, so nothing to add reference to
371        data_ = 0;
372    }
373
374    MemoryBlockReference(MemoryBlockReference<T_type>& ref, sizeType offset=0)
375    {
376        block_ = ref.block_;
377        addReference();
378        data_ = ref.data_ + offset;
379    }
380
381    MemoryBlockReference(sizeType length, T_type* data, 
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
411    explicit MemoryBlockReference(sizeType items)
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
425   ~MemoryBlockReference()
426    {
427        blockRemoveReference();
428    }
429
430  /** Returns true if the offset from data_ is vector aligned. */
431  bool isVectorAligned(size_t offset) const
432  { return simdTypes<T_type>::isVectorAligned(data_ + offset); }
433
434  /** Returns the allocated length of the memory block. */
435  sizeType blockLength() const { return block_->length(); };
436 
437protected:
438#ifdef BZ_TESTSUITE
439public:
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
462    void changeToNullBlock()
463    {
464        blockRemoveReference();
465        block_ = 0;
466        // no block, so nothing to add reference to
467        data_ = 0;
468    }
469
470    void changeBlock(MemoryBlockReference<T_type>& ref, sizeType offset=0)
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
491private:
492    void blockRemoveReference()
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 
537    void operator=(const MemoryBlockReference<T_type>&)
538    { }
539};
540
541
542BZ_NAMESPACE_END
543
544#include <blitz/memblock.cc>
545
546#endif // BZ_MEMBLOCK_H
Note: See TracBrowser for help on using the repository browser.