12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001 |
- /*
- * @copyright
- * Copyright (C) 2011-2013, Intel Corporation
- * All rights reserved.
- *
- * @copyright
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * @copyright
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- */
- /*
- * holder.h
- *
- * Purpose: hyperobject to provide different views of an object to each
- * parallel strand.
- */
- #ifndef HOLDER_H_INCLUDED
- #define HOLDER_H_INCLUDED
- #include <cilk/reducer.h>
- #include <memory>
- #include <utility>
- #ifdef __cplusplus
- /* C++ Interface
- *
- * Classes: holder<Type>
- *
- * Description:
- * ============
- * This component provides a hyperobject that isolates a parallel uses of a
- * common variable where it is not necessary to preserve changes from
- * different parallel strands. In effect, a holder acts a bit like
- * thread-local storage, but has qualities that work better with the
- * fork-join structure of Cilk. In particular, a holder has the following
- * qualities:
- *
- * - The view of a holder before the first spawn within a function is the same
- * as the view after each sync (as in the case of a reducer).
- * - The view of a holder within the first spawned child of a function (or the
- * first child spawned after a sync) is the same as the view on entry to the
- * function.
- * - The view of a holder before entering a _Cilk_for loop is the same as the
- * view during the first iteration of the loop and the view at the end of
- * the loop.
- * - The view of a holder in the continuation of a spawn or in an arbitrary
- * iteration of a _Cilk_for loop is *non-deterministic*. It is generally
- * recommended that the holder be explicitly put into a known state in these
- * situations.
- *
- * A holder can be used as an alternative to parameter-passing. They are most
- * useful for replacing non-local variables without massive refactoring. A
- * holder takes advantage of the fact that, most of the time, a holder view
- * does not change after a spawn or from one iteration of a parallel for loop
- * to the next (i.e., stealing is the exception, not the rule). When the
- * holder view is a large object that is expensive to construct, this
- * optimization can save significant time versus creating a separate local
- * object for each view. In addition, a holder using the "keep last" policy
- * will have the same value after a sync as the serialization of the same
- * program. The last quality will often allow the program to avoid
- * recomputing a value.
- *
- * Usage Example:
- * ==============
- * Function 'compute()' is a complex function that computes a value using a
- * memoized algorithm, storing intermediate results in a hash table. Compute
- * calls several other functions, each of which calls several other functions,
- * all of which share a global hash table. In all, there are over a dozen
- * functions with a total of about 60 references to the hash table.
- *..
- * hash_table<int, X> memos;
- *
- * void h(const X& x); // Uses memos
- *
- * double compute(const X& x)
- * {
- * memos.clear();
- * // ...
- * memos[i] = x;
- * ...
- * g(i); // Uses memos
- * // ...
- * std::for_each(c.begin(), c.end(), h); // Call h for each element of c
- * }
- *
- * int main()
- * {
- * const std::size_t ARRAY_SIZE = 1000000;
- * extern X myArray[ARRAY_SIZE];
- *
- * for (std::size_t i = 0; i < ARRAY_SIZE; ++i)
- * {
- * compute(myArray[i]);
- * }
- * }
- *..
- * We would like to replace the 'for' loop in 'main' with a 'cilk_for'.
- * Although the hash table is cleared on entry to each call to 'compute()',
- * and although the values stored in the hash table are no longer used after
- * 'compute()' returns, the use of the hash table as a global variable
- * prevents 'compute()' from being called safely in parallel. One way to do
- * this would be to make 'memos' a private variable within the cilk_for loop
- * and pass it down to the actual computation, so that each loop iteration has
- * its own private copy:
- *..
- * cilk_for (std::size_t i = 0; i < ARRAY_SIZE; ++i)
- * {
- * hash_table<int, X> memos;
- * compute(myArray[i], memos);
- * }
- *..
- * The problem with this approach is that it requires changing the signature
- * of 'compute', 'h', 'g', and every one of the dozen or so functions that
- * reference 'memos' as well as any function that calls those functions. This
- * may break the abstraction of 'compute' and other functions, exposing an
- * implementation detail that was not part of the interface. In addition, the
- * function 'h' is called through a templated algorithm, 'for_each', which
- * requires a fixed interface. Finally, there is constructor and destructor
- * overhead for 'hash_table' each time through the loop.
- *
- * The alternative approach is to replace 'memos' with a holder. The holder
- * would be available to all of the functions involved, but would not cause a
- * race between parallel loop iterations. In order to make this work, each
- * use of the 'memos' variable must be (mechanically) replaced by a use of the
- * holder:
- *..
- * cilk::holder<hash_table<int, X> > memos_h;
- *
- * void h(const X& x); // Uses memos_h
- *
- * double compute(const X& x)
- * {
- * memos_h().clear(); // operator() used to "dereference" the holder
- * // ...
- * memos_h()[i] = x; // operator() used to "dereference" the holder
- * ...
- * g(i); // Uses memos_h
- * // ...
- * std::for_each(c.begin(), c.end(), h); // Call h for each element of c
- * }
- *..
- * Note that each reference to the holder must be modified with an empty pair
- * of parenthesis. This syntax is needed because there is no facility in C++
- * for a "smart reference" that would allow 'memos_h' to be a perfect
- * replacement for 'memos'. One way that a user can avoid this syntax change
- * is to wrap the holder in a class that has the same inteface as
- * 'hash_table' but redirects all calls to the holder:
- *..
- * template <typename K, typename V>
- * class hash_table_holder
- * {
- * private:
- * cilk::holder<hash_table<K, V> > m_holder;
- * public:
- * void clear() { m_holder().clear(); }
- * V& operator[](const K& x) { return m_holder()[x]; }
- * std::size_t size() const { return m_holder().size(); }
- * // etc. ...
- * };
- *..
- * Using the above wrapper, the original code can be left unchanged except for
- * replacing 'hash_table' with 'hash_table_holder' and replacing 'for' with
- * 'cilk_for':
- *..
- * hash_table_holder<int, X> memos;
- *
- * void h(const X& x); // Uses memos
- *
- * double compute(const X& x)
- * {
- * memos.clear(); // Calls hash_table_holder::clear().
- * // ...
- * }
- *..
- * The above changes have no benefit over the use of thread-local storage.
- * What if one of the functions has a 'cilk_spawn', however?
- *..
- * void h(const X& x)
- * {
- * Y y = x.nested();
- * double d, w;
- * if (y)
- * {
- * w = cilk_spawn compute_width(y); // May use 'memos'
- * d = compute_depth(y); // Does not use 'memos'
- * cilk_sync;
- * compute(y); // recursive call. Uses 'memos'.
- * }
- * }
- *..
- * In the above example, the view of the holder within 'compute_width' is the
- * same as the view on entry to 'h'. More importantly, the view of the holder
- * within the recursive call to 'compute' is the same as the view on entry to
- * 'h', even if a different worker is executing the recursive call. Thus, the
- * holder view within a Cilk program has useful qualities not found in
- * thread-local storage.
- */
- namespace cilk {
-
- /**
- * After a sync, the value stored in a holder matches the most recent
- * value stored into the holder by one of the starnds entering the sync.
- * The holder policy used to instantiate the holder determines which of
- * the entering strands determines the final value of the holder. A policy
- * of 'holder_keep_indeterminate' (the default) is the most efficient, and
- * results in an indeterminate value depending on the runtime schedule
- * (see below for more specifics). An indeterminate value after a sync is
- * often acceptable, especially if the value of the holder is not reused
- * after the sync. All of the remaining policies retain the value of the
- * last strand that would be executed in the serialization of the program.
- * They differ in the mechanism used to move the value from one view to
- * another. A policy of 'holder_keep_last_copy' moves values by
- * copy-assignment. A policy of 'holder_keep_last_swap' moves values by
- * calling 'swap'. A policy of 'holder_keep_last_move' is available only
- * for compilers that support C++0x rvalue references and moves values by
- * move-assignment. A policy of 'holder_keep_last' attempts to choose the
- * most efficient mechanism: member-function 'swap' if the view type
- * supports it, otherwise move-assignment if supported, otherwise
- * copy-assignment. (The swap member function for a class that provides
- * one is almost always as fast or faster than move-assignment or
- * copy-assignment.)
- *
- * The behavior of 'holder_keep_indeterminate', while indeterminate, is
- * not random and can be used for advanced programming or debugging. With
- * a policy of 'holder_keep_intermediate', values are never copied or
- * moved between views. The value of the view after a sync is the same as
- * the value set in the last spawned child before a steal occurs or the
- * last value set in the continuation if no steal occurs. Using this
- * knowledge, a programmer can use a holder to detect the earliest steal
- * in a piece of code. An indeterminate holder is also useful for keeping
- * cached data similar to the way some applications might use thread-local
- * storage.
- */
- enum holder_policy {
- holder_keep_indeterminate,
- holder_keep_last,
- holder_keep_last_copy,
- holder_keep_last_swap,
- #ifdef __CILKRTS_RVALUE_REFERENCES
- holder_keep_last_move
- #endif
- };
- namespace internal {
- // Private special-case holder policy using the swap member-function
- const holder_policy holder_keep_last_member_swap =
- (holder_policy) (holder_keep_last_swap | 0x10);
- /* The constant, 'has_member_swap<T>::value', will be 'true' if 'T'
- * has a non-static member function with prototype 'void swap(T&)'.
- * The mechanism used to detect 'swap' is the most portable among
- * present-day compilers, but is not the most robust. Specifically,
- * the prototype for 'swap' must exactly match 'void swap(T&)'.
- * Near-matches like a 'swap' function that returns 'int' instead of
- * 'void' will not be detected. Detection will also fail if 'T'
- * inherits 'swap' from a base class.
- */
- template <typename T>
- class has_member_swap
- {
- // This technique for detecting member functions was described by
- // Rani Sharoni in comp.lang.c++.moderated:
- // http://groups.google.com/group/comp.lang.c++.moderated/msg/2b06b2432fddfb60
- // sizeof(notchar) is guaranteed larger than 1
- struct notchar { char x[2]; };
- // Instantiationg Q<U, &U::swap> will fail unless U contains a
- // non-static member with prototype 'void swap(U&)'.
- template <class U, void (U::*)(U&)> struct Q { };
- // First 'test' is preferred overload if U::swap exists with the
- // correct prototype. Second 'test' is preferred overload
- // otherwise.
- template <typename U> static char test(Q<U,&U::swap>*);
- template <typename U> static notchar test(...);
- public:
- /// 'value' will be true if T has a non-static member function
- /// with prototype 'void swap(T&)'.
- static const bool value = (1 == sizeof(test<T>(0)));
- };
- template <typename T> const bool has_member_swap<T>::value;
- /**
- * @brief Utility class for exception safety.
- *
- * The constuctor for this class takes a pointer and an allocator and
- * holds on to them. The destructor deallocates the pointed-to
- * object, without calling its destructor, typically to recover memory
- * in case an exception is thrown. The release member clears the
- * pointer so that the deallocation is prevented, i.e., when the
- * exception danger has passed. The behavior of this class is similar
- * to auto_ptr and unique_ptr.
- */
- template <typename Type, typename Allocator = std::allocator<Type> >
- class auto_deallocator
- {
- Allocator m_alloc;
- Type* m_ptr;
- // Non-copiable
- auto_deallocator(const auto_deallocator&);
- auto_deallocator& operator=(const auto_deallocator&);
- public:
- /// Constructor
- explicit auto_deallocator(Type* p, const Allocator& a = Allocator())
- : m_alloc(a), m_ptr(p) { }
- /// Destructor - free allocated resources
- ~auto_deallocator() { if (m_ptr) m_alloc.deallocate(m_ptr, 1); }
- /// Remove reference to resource
- void release() { m_ptr = 0; }
- };
- /**
- * Pure-abstract base class to initialize holder views
- */
- template <typename Type, typename Allocator>
- class init_base
- {
- public:
- virtual ~init_base() { }
- virtual init_base* clone_self(Allocator& a) const = 0;
- virtual void delete_self(Allocator& a) = 0;
- virtual void construct_view(Type* p, Allocator& a) const = 0;
- };
- /**
- * Class to default-initialize a holder view
- */
- template <typename Type, typename Allocator>
- class default_init : public init_base<Type, Allocator>
- {
- typedef init_base<Type, Allocator> base;
- /// Private constructor (called from static make() function).
- default_init() { }
- // Non-copiable
- default_init(const default_init&);
- default_init& operator=(const default_init&);
- public:
- // Static factory function
- static default_init* make(Allocator& a);
- // Virtual function overrides
- virtual ~default_init();
- virtual base* clone_self(Allocator& a) const;
- virtual void delete_self(Allocator& a);
- virtual void construct_view(Type* p, Allocator& a) const;
- };
- template <typename Type, typename Allocator>
- default_init<Type, Allocator>*
- default_init<Type, Allocator>::make(Allocator&)
- {
- // Return a pointer to a singleton. All instances of this class
- // are identical, so we need only one.
- static default_init self;
- return &self;
- }
- template <typename Type, typename Allocator>
- default_init<Type, Allocator>::~default_init()
- {
- }
- template <typename Type, typename Allocator>
- init_base<Type, Allocator>*
- default_init<Type, Allocator>::clone_self(Allocator& a) const
- {
- return make(a);
- }
- template <typename Type, typename Allocator>
- void default_init<Type, Allocator>::delete_self(Allocator&)
- {
- // Since make() returned a shared singleton, there is nothing to
- // delete here.
- }
- template <typename Type, typename Allocator>
- void
- default_init<Type, Allocator>::construct_view(Type* p,
- Allocator&) const
- {
- ::new((void*) p) Type();
- // TBD: In a C++0x library, this should be rewritten
- // std::allocator_traits<Allocator>::construct(a, p);
- }
- /**
- * Class to copy-construct a view from a stored exemplar.
- */
- template <typename Type, typename Allocator>
- class exemplar_init : public init_base<Type, Allocator>
- {
- typedef init_base<Type, Allocator> base;
- Type* m_exemplar;
- // Private constructors (called from make() functions).
- exemplar_init(const Type& val, Allocator& a);
- #ifdef __CILKRTS_RVALUE_REFERENCES
- exemplar_init(Type&& val, Allocator& a);
- #endif
- // Non-copyiable
- exemplar_init(const exemplar_init&);
- exemplar_init& operator=(const exemplar_init&);
- public:
- // Static factory functions
- static exemplar_init* make(const Type& val,
- Allocator& a = Allocator());
- #ifdef __CILKRTS_RVALUE_REFERENCES
- static exemplar_init* make(Type&& val,
- Allocator& a = Allocator());
- #endif
- // Virtual function overrides
- virtual ~exemplar_init();
- virtual base* clone_self(Allocator& a) const;
- virtual void delete_self(Allocator& a);
- virtual void construct_view(Type* p, Allocator& a) const;
- };
- template <typename Type, typename Allocator>
- exemplar_init<Type, Allocator>::exemplar_init(const Type& val,
- Allocator& a)
- {
- m_exemplar = a.allocate(1);
- auto_deallocator<Type, Allocator> guard(m_exemplar, a);
- a.construct(m_exemplar, val);
- guard.release();
- }
- #ifdef __CILKRTS_RVALUE_REFERENCES
- template <typename Type, typename Allocator>
- exemplar_init<Type, Allocator>::exemplar_init(Type&& val,
- Allocator& a)
- {
- m_exemplar = a.allocate(1);
- auto_deallocator<Type, Allocator> guard(m_exemplar, a);
- a.construct(m_exemplar, std::forward<Type>(val));
- guard.release();
- }
- #endif
- template <typename Type, typename Allocator>
- exemplar_init<Type, Allocator>*
- exemplar_init<Type, Allocator>::make(const Type& val,
- Allocator& a)
- {
- typedef typename Allocator::template rebind<exemplar_init>::other
- self_alloc_t;
- self_alloc_t alloc(a);
- exemplar_init *self = alloc.allocate(1);
- auto_deallocator<exemplar_init, self_alloc_t> guard(self, alloc);
- // Don't use allocator to construct self. Allocator should be
- // used only on elements of type 'Type'.
- ::new((void*) self) exemplar_init(val, a);
- guard.release();
- return self;
- }
- #ifdef __CILKRTS_RVALUE_REFERENCES
- template <typename Type, typename Allocator>
- exemplar_init<Type, Allocator>*
- exemplar_init<Type, Allocator>::make(Type&& val,
- Allocator& a)
- {
- typedef typename Allocator::template rebind<exemplar_init>::other
- self_alloc_t;
- self_alloc_t alloc(a);
- exemplar_init *self = alloc.allocate(1);
- auto_deallocator<exemplar_init, self_alloc_t> guard(self, alloc);
- // Don't use allocator to construct self. Allocator should be
- // used only on elements of type 'Type'.
- ::new((void*) self) exemplar_init(std::forward<Type>(val), a);
- guard.release();
- return self;
- }
- #endif
- template <typename Type, typename Allocator>
- exemplar_init<Type, Allocator>::~exemplar_init()
- {
- // Called only by delete_self, which deleted the exemplar using an
- // allocator.
- __CILKRTS_ASSERT(0 == m_exemplar);
- }
- template <typename Type, typename Allocator>
- init_base<Type, Allocator>*
- exemplar_init<Type, Allocator>::clone_self(Allocator& a) const
- {
- return make(*m_exemplar, a);
- }
- template <typename Type, typename Allocator>
- void exemplar_init<Type, Allocator>::delete_self(Allocator& a)
- {
- typename Allocator::template rebind<exemplar_init>::other alloc(a);
- a.destroy(m_exemplar);
- a.deallocate(m_exemplar, 1);
- m_exemplar = 0;
- this->~exemplar_init();
- alloc.deallocate(this, 1);
- }
- template <typename Type, typename Allocator>
- void
- exemplar_init<Type, Allocator>::construct_view(Type* p,
- Allocator& a) const
- {
- a.construct(p, *m_exemplar);
- // TBD: In a C++0x library, this should be rewritten
- // std::allocator_traits<Allocator>::construct(a, p, *m_exemplar);
- }
- /**
- * Class to construct a view using a stored functor. The functor,
- * 'f', must be be invokable using the expression 'Type x = f()'.
- */
- template <typename Func, typename Allocator>
- class functor_init :
- public init_base<typename Allocator::value_type, Allocator>
- {
- typedef typename Allocator::value_type value_type;
- typedef init_base<value_type, Allocator> base;
- typedef typename Allocator::template rebind<Func>::other f_alloc;
- Func *m_functor;
- /// Private constructors (called from make() functions
- functor_init(const Func& f, Allocator& a);
- #ifdef __CILKRTS_RVALUE_REFERENCES
- functor_init(Func&& f, Allocator& a);
- #endif
- // Non-copiable
- functor_init(const functor_init&);
- functor_init& operator=(const functor_init&);
- public:
- // Static factory functions
- static functor_init* make(const Func& val,
- Allocator& a = Allocator());
- #ifdef __CILKRTS_RVALUE_REFERENCES
- static functor_init* make(Func&& val,
- Allocator& a = Allocator());
- #endif
- // Virtual function overrides
- virtual ~functor_init();
- virtual base* clone_self(Allocator& a) const;
- virtual void delete_self(Allocator& a);
- virtual void
- construct_view(value_type* p, Allocator& a) const;
- };
- /// Specialization to strip off reference from 'Func&'.
- template <typename Func, typename Allocator>
- struct functor_init<Func&, Allocator>
- : functor_init<Func, Allocator> { };
- /// Specialization to strip off reference and cvq from 'const Func&'.
- template <typename Func, typename Allocator>
- struct functor_init<const Func&, Allocator>
- : functor_init<Func, Allocator> { };
- template <typename Func, typename Allocator>
- functor_init<Func, Allocator>::functor_init(const Func& f,
- Allocator& a)
- {
- f_alloc alloc(a);
- m_functor = alloc.allocate(1);
- auto_deallocator<Func, f_alloc> guard(m_functor, alloc);
- alloc.construct(m_functor, f);
- guard.release();
- }
- #ifdef __CILKRTS_RVALUE_REFERENCES
- template <typename Func, typename Allocator>
- functor_init<Func, Allocator>::functor_init(Func&& f,
- Allocator& a)
- {
- f_alloc alloc(a);
- m_functor = alloc.allocate(1);
- auto_deallocator<Func, f_alloc> guard(m_functor, alloc);
- alloc.construct(m_functor, std::forward<Func>(f));
- guard.release();
- }
- #endif
- template <typename Func, typename Allocator>
- functor_init<Func, Allocator>*
- functor_init<Func, Allocator>::make(const Func& f, Allocator& a)
- {
- typedef typename Allocator::template rebind<functor_init>::other
- self_alloc_t;
- self_alloc_t alloc(a);
- functor_init *self = alloc.allocate(1);
- auto_deallocator<functor_init, self_alloc_t> guard(self, alloc);
- // Don't use allocator to construct self. Allocator should be
- // used only on elements of type 'Func'.
- ::new((void*) self) functor_init(f, a);
- guard.release();
- return self;
- }
- #ifdef __CILKRTS_RVALUE_REFERENCES
- template <typename Func, typename Allocator>
- functor_init<Func, Allocator>*
- functor_init<Func, Allocator>::make(Func&& f, Allocator& a)
- {
- typedef typename Allocator::template rebind<functor_init>::other
- self_alloc_t;
- self_alloc_t alloc(a);
- functor_init *self = alloc.allocate(1);
- auto_deallocator<functor_init, self_alloc_t> guard(self, alloc);
- // Don't use allocator to construct self. Allocator should be
- // used only on elements of type 'Func'.
- ::new((void*) self) functor_init(std::forward<Func>(f), a);
- guard.release();
- return self;
- }
- #endif
- template <typename Func, typename Allocator>
- functor_init<Func, Allocator>::~functor_init()
- {
- // Called only by delete_self, which deleted the functor using an
- // allocator.
- __CILKRTS_ASSERT(0 == m_functor);
- }
- template <typename Func, typename Allocator>
- init_base<typename Allocator::value_type, Allocator>*
- functor_init<Func, Allocator>::clone_self(Allocator& a) const
- {
- return make(*m_functor, a);
- }
- template <typename Func, typename Allocator>
- inline
- void functor_init<Func, Allocator>::delete_self(Allocator& a)
- {
- typename Allocator::template rebind<functor_init>::other alloc(a);
- f_alloc fa(a);
- fa.destroy(m_functor);
- fa.deallocate(m_functor, 1);
- m_functor = 0;
- this->~functor_init();
- alloc.deallocate(this, 1);
- }
- template <typename Func, typename Allocator>
- void functor_init<Func, Allocator>::construct_view(value_type* p,
- Allocator& a) const
- {
- a.construct(p, (*m_functor)());
- // In C++0x, the above should be written
- // std::allocator_traits<Allocator>::construct(a, p, m_functor());
- }
- /**
- * Functor called to reduce a holder
- */
- template <typename Type, holder_policy Policy>
- struct holder_reduce_functor;
- /**
- * Specialization to keep the left (first) value.
- */
- template <typename Type>
- struct holder_reduce_functor<Type, holder_keep_indeterminate>
- {
- void operator()(Type* left, Type* right) const { }
- };
- /**
- * Specialization to copy-assign from the right (last) value.
- */
- template <typename Type>
- struct holder_reduce_functor<Type, holder_keep_last_copy>
- {
- void operator()(Type* left, Type* right) const {
- *left = *right;
- }
- };
- /*
- * Specialization to keep the right (last) value via swap.
- */
- template <typename Type>
- struct holder_reduce_functor<Type, holder_keep_last_swap>
- {
- void operator()(Type* left, Type* right) const {
- using std::swap;
- swap(*left, *right);
- }
- };
- #ifdef __CILKRTS_RVALUE_REFERENCES
- /*
- * Specialization to move-assign from the right (last) value.
- */
- template <typename Type>
- struct holder_reduce_functor<Type, holder_keep_last_move>
- {
- void operator()(Type* left, Type* right) const {
- *left = std::move(*right);
- }
- };
- #endif
- /*
- * Specialization to keep the right (last) value via the swap member
- * function.
- */
- template <typename Type>
- struct holder_reduce_functor<Type, holder_keep_last_member_swap>
- {
- void operator()(Type* left, Type* right) const {
- left->swap(*right);
- }
- };
- /*
- * Specialization to keep the right (last) value by the most efficient
- * means detectable.
- */
- template <typename Type>
- struct holder_reduce_functor<Type, holder_keep_last> :
- holder_reduce_functor<Type,
- (holder_policy)
- (has_member_swap<Type>::value ?
- holder_keep_last_member_swap :
- #ifdef __CILKRTS_RVALUE_REFERENCES
- holder_keep_last_move
- #else
- holder_keep_last_copy
- #endif
- )>
- {
- };
- } // end namespace internal
- /**
- * Monoid for holders.
- * Allocator type is required to be thread-safe.
- */
- template <typename Type,
- holder_policy Policy = holder_keep_indeterminate,
- typename Allocator = std::allocator<Type> >
- class holder_monoid : public monoid_base<Type>
- {
- // Allocator is mutable because the copy of the monoid inside the
- // reducer is const (to avoid races on the shared state). However,
- // the allocator is required to be thread-safe, so it is ok (and
- // necessary) to modify.
- mutable Allocator m_allocator;
- internal::init_base<Type, Allocator> *m_initializer;
- public:
- /// This constructor uses default-initialization for both the leftmost
- /// view and each identity view.
- holder_monoid(const Allocator& a = Allocator())
- : m_allocator(a)
- , m_initializer(
- internal::default_init<Type, Allocator>::make(m_allocator))
- { }
- /// These constructors use 'val' as an exemplar to copy-construct both
- /// the leftmost view and each identity view.
- holder_monoid(const Type& val, const Allocator& a = Allocator())
- : m_allocator(a)
- , m_initializer(internal::exemplar_init<Type, Allocator>::make(
- val, m_allocator)) { }
- /// This constructor uses 'f' as a functor to construct both
- /// the leftmost view and each identity view.
- template <typename Func>
- holder_monoid(const Func& f, const Allocator& a = Allocator())
- : m_allocator(a)
- , m_initializer(
- internal::functor_init<Func, Allocator>::make(f,m_allocator))
- { }
- /// Copy constructor
- holder_monoid(const holder_monoid& rhs)
- : m_allocator(rhs.m_allocator)
- , m_initializer(rhs.m_initializer->clone_self(m_allocator)) { }
- /// "Extended" copy constructor with allocator
- holder_monoid(const holder_monoid& rhs, const Allocator& a)
- : m_allocator(a)
- , m_initializer(rhs.m_initializer->clone_self(m_allocator)) { }
- #ifdef __CILKRTS_RVALUE_REFERENCES
- /// Move constructor
- holder_monoid(holder_monoid&& rhs)
- : m_allocator(rhs.m_allocator)
- , m_initializer(rhs.m_initializer) {
- rhs.m_initializer =
- internal::default_init<Type, Allocator>::make(m_allocator);
- }
- /// "Extended" move constructor with allocator
- holder_monoid(holder_monoid&& rhs, const Allocator& a)
- : m_allocator(a)
- , m_initializer(0) {
- if (a != rhs.m_allocator)
- m_initializer = rhs.m_initializer->clone_self(a);
- else {
- m_initializer = rhs.m_initializer;
- rhs.m_initializer =
- internal::default_init<Type, Allocator>::make(m_allocator);
- }
- }
- #endif
- /// Destructor
- ~holder_monoid() { m_initializer->delete_self(m_allocator); }
- holder_monoid& operator=(const holder_monoid& rhs) {
- if (this == &rhs) return *this;
- m_initializer->delete_self(m_allocator);
- m_initializer = rhs.m_initializer->clone_self(m_allocator);
- }
- #ifdef __CILKRTS_RVALUE_REFERENCES
- holder_monoid& operator=(holder_monoid&& rhs) {
- if (m_allocator != rhs.m_allocator)
- // Delegate to copy-assignment on unequal allocators
- return operator=(static_cast<const holder_monoid&>(rhs));
- std::swap(m_initializer, rhs.m_initializer);
- return *this;
- }
- #endif
- /// Constructs IDENTITY value into the uninitilized '*p'
- void identity(Type* p) const
- { m_initializer->construct_view(p, m_allocator); }
- /// Calls the destructor on the object pointed-to by 'p'
- void destroy(Type* p) const
- { m_allocator.destroy(p); }
- /// Return a pointer to size bytes of raw memory
- void* allocate(std::size_t s) const {
- __CILKRTS_ASSERT(sizeof(Type) == s);
- return m_allocator.allocate(1);
- }
- /// Deallocate the raw memory at p
- void deallocate(void* p) const {
- m_allocator.deallocate(static_cast<Type*>(p), sizeof(Type));
- }
- void reduce(Type* left, Type* right) const {
- internal::holder_reduce_functor<Type, Policy>()(left, right);
- }
- void swap(holder_monoid& other) {
- __CILKRTS_ASSERT(m_allocator == other.m_allocator);
- std::swap(m_initializer, other.m_initializer);
- }
- Allocator get_allocator() const {
- return m_allocator;
- }
- };
- // Namespace-scope swap
- template <typename Type, holder_policy Policy, typename Allocator>
- inline void swap(holder_monoid<Type, Policy, Allocator>& a,
- holder_monoid<Type, Policy, Allocator>& b)
- {
- a.swap(b);
- }
- /**
- * Hyperobject to provide different views of an object to each
- * parallel strand.
- */
- template <typename Type,
- holder_policy Policy = holder_keep_indeterminate,
- typename Allocator = std::allocator<Type> >
- class holder : public reducer<holder_monoid<Type, Policy, Allocator> >
- {
- typedef holder_monoid<Type, Policy, Allocator> monoid_type;
- typedef reducer<monoid_type> imp;
- // Return a value of Type constructed using the functor Func.
- template <typename Func>
- Type make_value(const Func& f) const {
- struct obj {
- union {
- char buf[sizeof(Type)];
- void* align1;
- double align2;
- };
- obj(const Func& f) { f(static_cast<Type*>(buf)); }
- ~obj() { static_cast<Type*>(buf)->~Type(); }
- operator Type&() { return *static_cast<Type*>(buf); }
- };
- return obj(f);
- }
- public:
- /// Default constructor uses default-initialization for both the
- /// leftmost view and each identity view.
- holder(const Allocator& alloc = Allocator())
- : imp(monoid_type(alloc)) { }
- /// Construct from an exemplar that is used to initialize both the
- /// leftmost view and each identity view.
- holder(const Type& v, const Allocator& alloc = Allocator())
- // Alas, cannot use an rvalue reference for 'v' because it is used
- // twice in the same expression for initializing imp.
- : imp(monoid_type(v, alloc), v) { }
- /// Construct from a functor that is used to initialize both the
- /// leftmost view and each identity view. The functor, 'f', must be be
- /// invokable using the expression 'Type x = f()'.
- template <typename Func>
- holder(const Func& f, const Allocator& alloc = Allocator())
- // Alas, cannot use an rvalue for 'f' because it is used twice in
- // the same expression for initializing imp.
- : imp(monoid_type(f, alloc), make_value(f)) { }
- };
- } // end namespace cilk
- #else /* C */
- # error Holders are currently available only for C++
- #endif /* __cplusplus */
- #endif /* HOLDER_H_INCLUDED */
|