1 /*
2   This file is part of MADNESS.
3 
4   Copyright (C) 2007,2010 Oak Ridge National Laboratory
5 
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10 
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14   GNU General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 
20   For more information please contact:
21 
22   Robert J. Harrison
23   Oak Ridge National Laboratory
24   One Bethel Valley Road
25   P.O. Box 2008, MS-6367
26 
27   email: harrisonrj@ornl.gov
28   tel:   865-241-3937
29   fax:   865-572-0680
30 */
31 
32 #ifndef MADNESS_WORLD_WORLDDC_H__INCLUDED
33 #define MADNESS_WORLD_WORLDDC_H__INCLUDED
34 
35 /*!
36   \file worlddc.h
37   \brief Implements WorldContainer
38   \addtogroup worlddc
39   @{
40 
41 */
42 
43 #include <madness/world/parallel_archive.h>
44 #include <madness/world/worldhashmap.h>
45 #include <madness/world/mpi_archive.h>
46 #include <madness/world/world_object.h>
47 #include <set>
48 
49 namespace madness {
50 
51     template <typename keyT, typename valueT, typename hashfunT>
52     class WorldContainer;
53 
54     template <typename keyT, typename valueT, typename hashfunT>
55     class WorldContainerImpl;
56 
57     template <typename keyT, typename valueT, typename hashfunT>
58     void swap(WorldContainer<keyT, valueT, hashfunT>&, WorldContainer<keyT, valueT, hashfunT>&);
59 
60     template <typename keyT>
61     class WorldDCPmapInterface;
62 
63     template <typename keyT>
64     class WorldDCRedistributeInterface {
65     public:
66         virtual std::size_t size() const = 0;
67         virtual void redistribute_phase1(const std::shared_ptr< WorldDCPmapInterface<keyT> >& newmap) = 0;
68         virtual void redistribute_phase2() = 0;
69         virtual void redistribute_phase3() = 0;
~WorldDCRedistributeInterface()70 	virtual ~WorldDCRedistributeInterface() {};
71     };
72 
73 
74     /// Interface to be provided by any process map
75 
76     /// \ingroup worlddc
77     template <typename keyT>
78     class WorldDCPmapInterface {
79     public:
80         typedef WorldDCRedistributeInterface<keyT>* ptrT;
81     private:
82         std::set<ptrT> ptrs;
83     public:
84         /// Maps key to processor
85 
86         /// @param[in] key Key for container
87         /// @return Processor that logically owns the key
88         virtual ProcessID owner(const keyT& key) const = 0;
89 
~WorldDCPmapInterface()90         virtual ~WorldDCPmapInterface() {}
91 
print()92         virtual void print() const {}
93 
94         /// Registers object for receipt of redistribute callbacks
95 
96         /// @param[in] ptr Pointer to class derived from WorldDCRedistributedInterface
register_callback(ptrT ptr)97         void register_callback(ptrT ptr) {
98             ptrs.insert(ptr);
99         }
100 
101         /// Deregisters object for receipt of redistribute callbacks
102 
103         /// @param[in] ptr Pointer to class derived from WorldDCRedistributedInterface
deregister_callback(ptrT ptr)104         void deregister_callback(ptrT ptr) {
105             ptrs.erase(ptr);
106         }
107 
108         /// Invoking this switches all registered objects from this process map to the new one
109 
110         /// After invoking this routine all objects will be registered with the
111         /// new map and no objects will be registered in the current map.
112         /// @param[in] world The associated world
113         /// @param[in] newpmap The new process map
redistribute(World & world,const std::shared_ptr<WorldDCPmapInterface<keyT>> & newpmap)114         void redistribute(World& world, const std::shared_ptr< WorldDCPmapInterface<keyT> >& newpmap) {
115             print_data_sizes(world, "before redistributing");
116             world.gop.fence();
117             for (typename std::set<ptrT>::iterator iter = ptrs.begin();
118                  iter != ptrs.end();
119                  ++iter) {
120                 (*iter)->redistribute_phase1(newpmap);
121             }
122             world.gop.fence();
123             for (typename std::set<ptrT>::iterator iter = ptrs.begin();
124                  iter != ptrs.end();
125                  ++iter) {
126                 (*iter)->redistribute_phase2();
127                 newpmap->register_callback(*iter);
128             }
129             world.gop.fence();
130             for (typename std::set<ptrT>::iterator iter = ptrs.begin();
131                  iter != ptrs.end();
132                  ++iter) {
133 	         (*iter)->redistribute_phase3();
134             }
135             world.gop.fence();
136             ptrs.clear();
137             newpmap->print_data_sizes(world, "after redistributing");
138         }
139 
140         /// Counts global number of entries in all containers associated with this process map
141 
142         /// Collective operation with global fence
global_size(World & world)143         std::size_t global_size(World& world) const {
144             world.gop.fence();
145             std::size_t sum = local_size();
146             world.gop.sum(sum);
147             world.gop.fence();
148             return sum;
149         }
150 
151         /// Counts local number of entries in all containers associated with this process map
local_size()152         std::size_t local_size() const {
153             std::size_t sum = 0;
154             for (typename std::set<ptrT>::iterator iter = ptrs.begin(); iter != ptrs.end(); ++iter) {
155                 sum += (*iter)->size();
156             }
157             return sum;
158         }
159 
160         /// Prints size info to std::cout
161 
162         /// Collective operation with global fence
163         void print_data_sizes(World& world, const std::string msg="") const {
164             world.gop.fence();
165             std::size_t total = global_size(world);
166             std::vector<std::size_t> sizes(world.size());
167             sizes[world.rank()] = local_size();
168             world.gop.sum(&sizes[0],world.size());
169             if (world.rank() == 0) {
170                 madness::print("data distribution info", msg);
171                 madness::print("   total: ", total);
172                 std::cout << "   procs: ";
173                 for (int i=0; i<world.size(); i++) std::cout << sizes[i] << " ";
174                 std::cout << std::endl;
175             }
176             world.gop.fence();
177         }
178     };
179 
180     /// Default process map is "random" using madness::hash(key)
181 
182     /// \ingroup worlddc
183     template <typename keyT, typename hashfunT = Hash<keyT> >
184     class WorldDCDefaultPmap : public WorldDCPmapInterface<keyT> {
185     private:
186         const int nproc;
187         hashfunT hashfun;
188     public:
189         WorldDCDefaultPmap(World& world, const hashfunT& hf = hashfunT()) :
190             nproc(world.mpi.nproc()),
191             hashfun(hf)
192         { }
193 
owner(const keyT & key)194         ProcessID owner(const keyT& key) const {
195             if (nproc == 1) return 0;
196             return hashfun(key)%nproc;
197         }
198     };
199 
200 
201     /// Iterator for distributed container wraps the local iterator
202 
203     /// \ingroup worlddc
204     template <class internal_iteratorT>
205     class WorldContainerIterator {
206     public:
207       typedef typename std::iterator_traits<internal_iteratorT>::iterator_category iterator_category;
208       typedef typename std::iterator_traits<internal_iteratorT>::value_type value_type;
209       typedef typename std::iterator_traits<internal_iteratorT>::difference_type difference_type;
210       typedef typename std::iterator_traits<internal_iteratorT>::pointer pointer;
211       typedef typename std::iterator_traits<internal_iteratorT>::reference reference;
212 
213     private:
214         internal_iteratorT  it;       ///< Iterator from local container
215         // TODO: Convert this to a scoped pointer.
216         mutable value_type* value;    ///< holds the remote values
217 
218     public:
219         /// Default constructor makes a local uninitialized value
WorldContainerIterator()220         explicit WorldContainerIterator()
221                 : it(), value(nullptr) {}
222 
223         /// Initializes from a local iterator
WorldContainerIterator(const internal_iteratorT & it)224         explicit WorldContainerIterator(const internal_iteratorT& it)
225                 : it(it), value(nullptr) {}
226 
227         /// Initializes to cache a remote value
WorldContainerIterator(const value_type & v)228         explicit WorldContainerIterator(const value_type& v)
229                 : it(), value(nullptr)
230         {
231             value = new value_type(v);
232         }
233 
WorldContainerIterator(const WorldContainerIterator & other)234         WorldContainerIterator(const WorldContainerIterator& other)
235                 : it(), value(nullptr)
236         {
237             copy(other);
238         }
239 
240         template <class iteratorT>
WorldContainerIterator(const WorldContainerIterator<iteratorT> & other)241         WorldContainerIterator(const WorldContainerIterator<iteratorT>& other)
242                 : it(), value(nullptr)
243         {
244             copy(other);
245         }
246 
~WorldContainerIterator()247         ~WorldContainerIterator() {
248             delete value;
249         }
250 
251         /// Assignment
252         WorldContainerIterator& operator=(const WorldContainerIterator& other) {
253             copy(other);
254             return *this;
255         }
256 
257         /// Determines if two iterators are identical
258         bool operator==(const WorldContainerIterator& other) const {
259             return (((!is_cached()) && (!other.is_cached())) && it == other.it) ||
260                 ((is_cached() && other.is_cached()) && value->first == other.value->first);
261         }
262 
263 
264         /// Determines if two iterators are different
265         bool operator!=(const WorldContainerIterator& other) const {
266             return !(*this == other);
267         }
268 
269 
270         /// Pre-increment of an iterator (i.e., ++it) --- \em local iterators only
271 
272         /// Trying to increment a remote iterator will throw
273         WorldContainerIterator& operator++() {
274             MADNESS_ASSERT( !is_cached() );
275             ++it;
276             return *this;
277         }
278 
279         WorldContainerIterator operator++(int) {
280             MADNESS_ASSERT( !is_cached() );
281             WorldContainerIterator<internal_iteratorT> result(*this);
282             ++it;
283             return result;
284         }
285 
286         /// Iterators dereference to std::pair<const keyT,valueT>
287         pointer operator->() const {
288             return (is_cached() ? value : it.operator->() );
289         }
290 
291         /// Iterators dereference to std::pair<const keyT,valueT>
292         reference operator*() const {
293             return (is_cached() ? *value : *it );
294         }
295 
296         /// Private: (or should be) Returns iterator of internal container
get_internal_iterator()297         const internal_iteratorT& get_internal_iterator() const {
298             return it;
299         }
300 
301         /// Returns true if this is non-local or cached value
is_cached()302         bool is_cached() const {
303             return value != nullptr;
304         }
305 
306         template <typename Archive>
serialize(const Archive &)307         void serialize(const Archive&) {
308             MADNESS_EXCEPTION("Serializing DC iterator ... why?", false);
309         }
310 
311     private:
312         template <class iteratorT>
313         friend class WorldContainerIterator;
314 
315         template <class iteratorT>
copy(const WorldContainerIterator<iteratorT> & other)316         void copy(const WorldContainerIterator<iteratorT>& other) {
317             if (static_cast<const void*>(this) != static_cast<const void*>(&other)) {
318                 delete value;
319                 if(other.is_cached()) {
320                     value = new value_type(* other.value);
321                     it = internal_iteratorT();
322                 } else {
323                     it = other.it;
324                     value = nullptr;
325                 }
326             }
327         }
328     };
329 
330     /// Internal implementation of distributed container to facilitate shallow copy
331 
332     /// \ingroup worlddc
333     template <typename keyT, typename valueT, typename hashfunT >
334     class WorldContainerImpl
335         : public WorldObject< WorldContainerImpl<keyT, valueT, hashfunT> >
336         , public WorldDCRedistributeInterface<keyT>
337 #ifndef MADNESS_DISABLE_SHARED_FROM_THIS
338         , public std::enable_shared_from_this<WorldContainerImpl<keyT, valueT, hashfunT> >
339 #endif // MADNESS_DISABLE_SHARED_FROM_THIS
340     {
341     public:
342         typedef typename std::pair<const keyT,valueT> pairT;
343         typedef const pairT const_pairT;
344         typedef WorldContainerImpl<keyT,valueT,hashfunT> implT;
345 
346         typedef ConcurrentHashMap< keyT,valueT,hashfunT > internal_containerT;
347 
348 	//typedef WorldObject< WorldContainerImpl<keyT, valueT, hashfunT> > worldobjT;
349 
350         typedef typename internal_containerT::iterator internal_iteratorT;
351         typedef typename internal_containerT::const_iterator internal_const_iteratorT;
352         typedef typename internal_containerT::accessor accessor;
353         typedef typename internal_containerT::const_accessor const_accessor;
354         typedef WorldContainerIterator<internal_iteratorT> iteratorT;
355         typedef WorldContainerIterator<internal_iteratorT> iterator;
356         typedef WorldContainerIterator<internal_const_iteratorT> const_iteratorT;
357         typedef WorldContainerIterator<internal_const_iteratorT> const_iterator;
358 
359         friend class WorldContainer<keyT,valueT,hashfunT>;
360 
361 //         template <typename containerT, typename datumT>
362 //         inline
363 //         static
364 //         typename containerT::iterator replace(containerT& c, const datumT& d) {
365 //             std::pair<typename containerT::iterator,bool> p = c.insert(d);
366 //             if (!p.second) p.first->second = d.second;   // Who's on first?
367 //             return p.first;
368 //         }
369 
370     private:
371 
372         WorldContainerImpl();   // Inhibit default constructor
373 
374         std::shared_ptr< WorldDCPmapInterface<keyT> > pmap;///< Function/class to map from keys to owning process
375         const ProcessID me;                      ///< My MPI rank
376         internal_containerT local;               ///< Locally owned data
377         std::vector<keyT>* move_list;            ///< Tempoary used to record data that needs redistributing
378 
379         /// Handles find request
find_handler(ProcessID requestor,const keyT & key,const RemoteReference<FutureImpl<iterator>> & ref)380         void find_handler(ProcessID requestor, const keyT& key, const RemoteReference< FutureImpl<iterator> >& ref) {
381             internal_iteratorT r = local.find(key);
382             if (r == local.end()) {
383                 //print("find_handler: failure:", key);
384                 this->send(requestor, &implT::find_failure_handler, ref);
385             }
386             else {
387                 //print("find_handler: success:", key, r->first, r->second);
388                 this->send(requestor, &implT::find_success_handler, ref, *r);
389             }
390         }
391 
392         /// Handles successful find response
find_success_handler(const RemoteReference<FutureImpl<iterator>> & ref,const pairT & datum)393         void find_success_handler(const RemoteReference< FutureImpl<iterator> >& ref, const pairT& datum) {
394             FutureImpl<iterator>* f = ref.get();
395             f->set(iterator(datum));
396             //print("find_success_handler: success:", datum.first, datum.second, f->get()->first, f->get()->second);
397             // Todo: Look at this again.
398 //            ref.reset(); // Matching inc() in find() where ref was made
399         }
400 
401         /// Handles unsuccessful find response
find_failure_handler(const RemoteReference<FutureImpl<iterator>> & ref)402         void find_failure_handler(const RemoteReference< FutureImpl<iterator> >& ref) {
403             FutureImpl<iterator>* f = ref.get();
404             f->set(end());
405             //print("find_failure_handler");
406             // Todo: Look at this again.
407 //            ref.reset(); // Matching inc() in find() where ref was made
408         }
409 
410     public:
411 
WorldContainerImpl(World & world,const std::shared_ptr<WorldDCPmapInterface<keyT>> & pm,const hashfunT & hf)412         WorldContainerImpl(World& world,
413                            const std::shared_ptr< WorldDCPmapInterface<keyT> >& pm,
414                            const hashfunT& hf)
415                 : WorldObject< WorldContainerImpl<keyT, valueT, hashfunT> >(world)
416                 , pmap(pm)
417                 , me(world.mpi.rank())
418                 , local(5011, hf) {
419             pmap->register_callback(this);
420         }
421 
~WorldContainerImpl()422         virtual ~WorldContainerImpl() {
423             pmap->deregister_callback(this);
424         }
425 
get_pmap()426         const std::shared_ptr< WorldDCPmapInterface<keyT> >& get_pmap() const {
427             return pmap;
428         }
429 
get_hash()430         hashfunT& get_hash() const { return local.get_hash(); }
431 
is_local(const keyT & key)432         bool is_local(const keyT& key) const {
433             return owner(key) == me;
434         }
435 
owner(const keyT & key)436         ProcessID owner(const keyT& key) const {
437             return pmap->owner(key);
438         }
439 
probe(const keyT & key)440         bool probe(const keyT& key) const {
441             ProcessID dest = owner(key);
442             if (dest == me)
443                 return local.find(key) != local.end();
444             else
445                 return false;
446         }
447 
size()448         std::size_t size() const {
449             return local.size();
450         }
451 
insert(const pairT & datum)452         void insert(const pairT& datum) {
453             ProcessID dest = owner(datum.first);
454             if (dest == me) {
455                 // Was using iterator ... try accessor ?????
456                 accessor acc;
457                 local.insert(acc,datum.first);
458                 acc->second = datum.second;
459             }
460             else {
461   	        // Must be send (not task) for sequential consistency (and relies on single-threaded remote server)
462                 this->send(dest, &implT::insert, datum);
463             }
464         }
465 
insert_acc(accessor & acc,const keyT & key)466         bool insert_acc(accessor& acc, const keyT& key) {
467             MADNESS_ASSERT(owner(key) == me);
468             return local.insert(acc,key);
469         }
470 
insert_const_acc(const_accessor & acc,const keyT & key)471         bool insert_const_acc(const_accessor& acc, const keyT& key) {
472             MADNESS_ASSERT(owner(key) == me);
473             return local.insert(acc,key);
474         }
475 
clear()476         void clear() {
477             local.clear();
478         }
479 
480 
erase(const keyT & key)481         void erase(const keyT& key) {
482             ProcessID dest = owner(key);
483             if (dest == me) {
484                 local.erase(key);
485             }
486             else {
487                 void(implT::*eraser)(const keyT&) = &implT::erase;
488                 this->send(dest, eraser, key);
489             }
490         }
491 
492         template <typename InIter>
erase(InIter it)493         void erase(InIter it) {
494             MADNESS_ASSERT(!it.is_cached());
495             MADNESS_ASSERT(it != end());
496             erase(it->first);
497         }
498 
499         template <typename InIter>
erase(InIter first,InIter last)500         void erase(InIter first, InIter last) {
501             InIter it = first;
502             do {
503                 first++;
504                 erase(it->first);
505                 it = first;
506             } while(first != last);
507         }
508 
begin()509         iterator begin() {
510             return iterator(local.begin());
511         }
512 
begin()513         const_iterator begin() const {
514             return const_iterator(local.begin());
515         }
516 
end()517         iterator end() {
518             return iterator(local.end());
519         }
520 
end()521         const_iterator end() const {
522             return const_iterator(local.end());
523         }
524 
find(const keyT & key)525         Future<const_iterator> find(const keyT& key) const {
526             // Ugliness here to avoid replicating find() and
527             // associated handlers for const.  Assumption is that
528             // const and non-const iterators are identical except for
529             // const attribute ... at some point probably need to do
530             // the right thing.
531             Future<iterator> r = const_cast<implT*>(this)->find(key);
532             return *(Future<const_iterator>*)(&r);
533         }
534 
535 
find(const keyT & key)536         Future<iterator> find(const keyT& key) {
537             ProcessID dest = owner(key);
538             if (dest == me) {
539                 return Future<iterator>(iterator(local.find(key)));
540             } else {
541                 Future<iterator> result;
542                 this->send(dest, &implT::find_handler, me, key, result.remote_ref(this->get_world()));
543                 return result;
544             }
545         }
546 
find(accessor & acc,const keyT & key)547         bool find(accessor& acc, const keyT& key) {
548             if (owner(key) != me) return false;
549             return local.find(acc,key);
550         }
551 
552 
find(const_accessor & acc,const keyT & key)553         bool find(const_accessor& acc, const keyT& key) const {
554             if (owner(key) != me) return false;
555             return local.find(acc,key);
556         }
557 
558 
559         // Used to forward call to item member function
560         template <typename memfunT>
MEMFUN_RETURNT(memfunT)561         MEMFUN_RETURNT(memfunT)
562         itemfun(const keyT& key, memfunT memfun) {
563             accessor acc;
564             local.insert(acc, key);
565             return (acc->second.*memfun)();
566         }
567 
568         // Used to forward call to item member function
569         template <typename memfunT, typename arg1T>
MEMFUN_RETURNT(memfunT)570         MEMFUN_RETURNT(memfunT)
571         itemfun(const keyT& key, memfunT memfun, const arg1T& arg1) {
572             accessor acc;
573             local.insert(acc, key);
574             return (acc->second.*memfun)(arg1);
575         }
576 
577         // Used to forward call to item member function
578         template <typename memfunT, typename arg1T, typename arg2T>
MEMFUN_RETURNT(memfunT)579         MEMFUN_RETURNT(memfunT)
580         itemfun(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2) {
581             accessor acc;
582             local.insert(acc, key);
583             return (acc->second.*memfun)(arg1,arg2);
584         }
585 
586         // Used to forward call to item member function
587         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T>
MEMFUN_RETURNT(memfunT)588         MEMFUN_RETURNT(memfunT)
589         itemfun(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3) {
590             accessor acc;
591             local.insert(acc, key);
592             return (acc->second.*memfun)(arg1,arg2,arg3);
593         }
594 
595         // Used to forward call to item member function
596         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T>
MEMFUN_RETURNT(memfunT)597         MEMFUN_RETURNT(memfunT)
598         itemfun(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4) {
599             accessor acc;
600             local.insert(acc, key);
601             return (acc->second.*memfun)(arg1,arg2,arg3,arg4);
602         }
603 
604         // Used to forward call to item member function
605         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T>
MEMFUN_RETURNT(memfunT)606         MEMFUN_RETURNT(memfunT)
607         itemfun(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5) {
608             accessor acc;
609             local.insert(acc, key);
610             return (acc->second.*memfun)(arg1,arg2,arg3,arg4,arg5);
611         }
612 
613         // Used to forward call to item member function
614         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T, typename arg6T>
MEMFUN_RETURNT(memfunT)615         MEMFUN_RETURNT(memfunT)
616         itemfun(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5, const arg6T& arg6) {
617             accessor acc;
618             local.insert(acc, key);
619             return (acc->second.*memfun)(arg1,arg2,arg3,arg4,arg5,arg6);
620         }
621 
622         // Used to forward call to item member function
623         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T, typename arg6T, typename arg7T>
MEMFUN_RETURNT(memfunT)624         MEMFUN_RETURNT(memfunT)
625         itemfun(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3,
626 				const arg4T& arg4, const arg5T& arg5, const arg6T& arg6, const arg7T& arg7) {
627             accessor acc;
628             local.insert(acc, key);
629             return (acc->second.*memfun)(arg1,arg2,arg3,arg4,arg5,arg6,arg7);
630         }
631 
632         // First phase of redistributions changes pmap and makes list of stuff to move
redistribute_phase1(const std::shared_ptr<WorldDCPmapInterface<keyT>> & newpmap)633         void redistribute_phase1(const std::shared_ptr< WorldDCPmapInterface<keyT> >& newpmap) {
634             pmap = newpmap;
635             move_list = new std::vector<keyT>();
636             for (typename internal_containerT::iterator iter=local.begin(); iter!=local.end(); ++iter) {
637                 if (owner(iter->first) != me) move_list->push_back(iter->first);
638             }
639         }
640 
641 	struct P2Op {
642 	  implT * impl;
643 	  typedef Range<typename std::vector<keyT>::const_iterator> rangeT;
P2OpP2Op644 	  P2Op(implT* impl) : impl(impl) {}
P2OpP2Op645     	  P2Op(const P2Op& p) : impl(p.impl) {}
operatorP2Op646 	  bool operator()(typename rangeT::iterator& iterator) const {
647 	    typename internal_containerT::iterator iter = impl->local.find(*iterator);
648 	    MADNESS_ASSERT(iter != impl->local.end());
649 
650 	    //impl->insert(*iter);
651 	    impl->task(impl->owner(*iterator), &implT::insert, *iter);
652 
653 	    impl->local.erase(iter); // delete local copy of the data
654 	    return true;
655 	  }
656 	};
657 
658         // Second phase moves data
redistribute_phase2()659         void redistribute_phase2() {
660 	  this->get_world().taskq.for_each(typename P2Op::rangeT(move_list->begin(), move_list->end()), P2Op(this));
661 	    //std::vector<keyT>& mvlist = *move_list;
662             //for (unsigned int i=0; i<move_list->size(); ++i) {
663             //    typename internal_containerT::iterator iter = local.find(mvlist[i]);
664             //    MADNESS_ASSERT(iter != local.end());
665             //    insert(*iter);
666             //    local.erase(iter);
667             //}
668             //delete move_list;
669         }
670 
671         // Third phase cleans up
redistribute_phase3()672         void redistribute_phase3() {
673 	   delete move_list;
674         }
675     };
676 
677 
678     /// Makes a distributed container with specified attributes
679 
680     /// \ingroup worlddc
681     ///
682     /// There is no communication or syncronization associated with
683     /// making a new container, but every process must invoke the
684     /// constructor for each container in the same order.  This is so
685     /// that we can assign each container a unique ID without any
686     /// communication.  Since remotely invoked operations may start
687     /// happening before local construction, messages on not yet
688     /// constructed containers are buffered pending construction.
689     ///
690     /// Similarly, when a container is destroyed, the actual
691     /// destruction is deferred until a synchronization point
692     /// (world.gop.fence()) in order to eliminate the need to fence
693     /// before destroying every container.
694     ///
695     /// The distribution of data between processes is controlled by
696     /// the process map (Pmap) class.  The default is uniform
697     /// hashing based upon a strong (Bob Jenkins, lookup3) bytewise
698     /// hash of the key.
699     ///
700     /// All operations, including constructors and destructors, are
701     /// non-blocking and return immediately.  If communication occurs
702     /// it is asynchronous, otherwise operations are local.
703     template <typename keyT, typename valueT, typename hashfunT = Hash<keyT> >
704     class WorldContainer : public archive::ParallelSerializableObject {
705     public:
706         typedef WorldContainer<keyT,valueT,hashfunT> containerT;
707         typedef WorldContainerImpl<keyT,valueT,hashfunT> implT;
708         typedef typename implT::pairT pairT;
709         typedef typename implT::iterator iterator;
710         typedef typename implT::const_iterator const_iterator;
711         typedef typename implT::accessor accessor;
712         typedef typename implT::const_accessor const_accessor;
713         typedef Future<iterator> futureT;
714         typedef Future<const_iterator> const_futureT;
715 
716     private:
717         std::shared_ptr<implT> p;
718 
check_initialized()719         inline void check_initialized() const {
720             MADNESS_ASSERT(p);
721         }
722     public:
723 
724         /// Makes an uninitialized container (no communication)
725 
726         /// The container is useless until assigned to from a fully
727         /// constructed container.  There is no need to worry about
728         /// default constructors being executed in order.
WorldContainer()729         WorldContainer()
730                 : p()
731         {}
732 
733 
734         /// Makes an initialized, empty container with default data distribution (no communication)
735 
736         /// A unique ID is associated with every distributed container
737         /// within a world.  In order to avoid synchronization when
738         /// making a container, we have to assume that all processes
739         /// execute this constructor in the same order (does not apply
740         /// to the non-initializing, default constructor).
741         WorldContainer(World& world, bool do_pending=true, const hashfunT& hf = hashfunT())
p(new implT (world,std::shared_ptr<WorldDCPmapInterface<keyT>> (new WorldDCDefaultPmap<keyT,hashfunT> (world,hf)),hf))742             : p(new implT(world,
743                           std::shared_ptr< WorldDCPmapInterface<keyT> >(new WorldDCDefaultPmap<keyT, hashfunT>(world, hf)),
744                           hf))
745         {
746             if(do_pending)
747                 p->process_pending();
748         }
749 
750         /// Makes an initialized, empty container (no communication)
751 
752         /// A unique ID is associated with every distributed container
753         /// within a world.  In order to avoid synchronization when
754         /// making a container, we have to assume that all processes
755         /// execute this constructor in the same order (does not apply
756         /// to the non-initializing, default constructor).
757         WorldContainer(World& world,
758                        const std::shared_ptr< WorldDCPmapInterface<keyT> >& pmap,
759                        bool do_pending=true,
760                        const hashfunT& hf = hashfunT())
p(new implT (world,pmap,hf))761             : p(new implT(world, pmap, hf))
762         {
763             if(do_pending)
764                 p->process_pending();
765         }
766 
767 
768         /// Copy constructor is shallow (no communication)
769 
770         /// The copy refers to exactly the same container as other
771         /// which must be initialized.
WorldContainer(const WorldContainer & other)772         WorldContainer(const WorldContainer& other)
773             : p(other.p)
774         {
775             check_initialized();
776         }
777 
778         /// Assignment is shallow (no communication)
779 
780         /// The copy refers to exactly the same container as other
781         /// which must be initialized.
782         containerT& operator=(const containerT& other) {
783             if (this != &other) {
784                 other.check_initialized();
785                 p = other.p;
786             }
787             return *this;
788         }
789 
790         /// Returns the world associated with this container
get_world()791         World& get_world() const {
792             check_initialized();
793             return p->get_world();
794         }
795 
796 
797         /// Inserts/replaces key+value pair (non-blocking communication if key not local)
replace(const pairT & datum)798         void replace(const pairT& datum) {
799             check_initialized();
800             p->insert(datum);
801         }
802 
803 
804         /// Inserts/replaces key+value pair (non-blocking communication if key not local)
replace(const keyT & key,const valueT & value)805         void replace(const keyT& key, const valueT& value) {
806             replace(pairT(key,value));
807         }
808 
809 
810         /// Write access to LOCAL value by key. Returns true if found, false otherwise (always false for remote).
find(accessor & acc,const keyT & key)811         bool find(accessor& acc, const keyT& key) {
812             check_initialized();
813             return p->find(acc,key);
814         }
815 
816 
817         /// Read access to LOCAL value by key. Returns true if found, false otherwise (always false for remote).
find(const_accessor & acc,const keyT & key)818         bool find(const_accessor& acc, const keyT& key) const {
819             check_initialized();
820             return p->find(acc,key);
821         }
822 
823 
824         /// Write access to LOCAL value by key. Returns true if inserted, false if already exists (throws if remote)
insert(accessor & acc,const keyT & key)825         bool insert(accessor& acc, const keyT& key) {
826             check_initialized();
827             return p->insert_acc(acc,key);
828         }
829 
830 
831         /// Read access to LOCAL value by key. Returns true if inserted, false if already exists (throws if remote)
insert(const_accessor & acc,const keyT & key)832         bool insert(const_accessor& acc, const keyT& key) {
833             check_initialized();
834             return p->insert_acc(acc,key);
835         }
836 
837 
838         /// Inserts pairs (non-blocking communication if key(s) not local)
839         template <typename input_iterator>
replace(input_iterator & start,input_iterator & end)840         void replace(input_iterator& start, input_iterator& end) {
841             check_initialized();
842             std::for_each(start,end,std::bind1st(std::mem_fun(&containerT::insert),this));
843         }
844 
845 
846         /// Returns true if local data is immediately available (no communication)
probe(const keyT & key)847         bool probe(const keyT& key) const {
848             check_initialized();
849             return p->probe(key);
850         }
851 
852 
853         /// Returns processor that logically owns key (no communication)
854 
855         /// Local remapping may have changed its physical location, but all
856         /// operations should forward correctly.
owner(const keyT & key)857         inline ProcessID owner(const keyT& key) const {
858             check_initialized();
859             return p->owner(key);
860         }
861 
862 
863         /// Returns true if the key maps to the local processor (no communication)
is_local(const keyT & key)864         bool is_local(const keyT& key) const {
865             check_initialized();
866             return p->is_local(key);
867         }
868 
869 
870         /// Returns a future iterator (non-blocking communication if key not local)
871 
872         /// Like an std::map an iterator "points" to an std::pair<const keyT,valueT>.
873         ///
874         /// Refer to Future for info on how to avoid blocking.
find(const keyT & key)875         Future<iterator> find(const keyT& key) {          //
876             check_initialized();
877             return p->find(key);
878         }
879 
880 
881         /// Returns a future iterator (non-blocking communication if key not local)
882 
883         /// Like an std::map an iterator "points" to an std::pair<const keyT,valueT>.
884         ///
885         /// Refer to Future for info on how to avoid blocking.
find(const keyT & key)886         Future<const_iterator> find(const keyT& key) const {
887             check_initialized();
888             return const_cast<const implT*>(p.get())->find(key);
889         }
890 
891 
892         /// Returns an iterator to the beginning of the \em local data (no communication)
begin()893         iterator begin() {
894             check_initialized();
895             return p->begin();
896         }
897 
898 
899         /// Returns an iterator to the beginning of the \em local data (no communication)
begin()900         const_iterator begin() const {
901             check_initialized();
902             return const_cast<const implT*>(p.get())->begin();
903         }
904 
905         /// Returns an iterator past the end of the \em local data (no communication)
end()906         iterator end() {
907             check_initialized();
908             return p->end();
909         }
910 
911         /// Returns an iterator past the end of the \em local data (no communication)
end()912         const_iterator end() const {
913             check_initialized();
914             return const_cast<const implT*>(p.get())->end();
915         }
916 
917         /// Erases entry from container (non-blocking comm if remote)
918 
919         /// Missing keys are quietly ignored.
920         ///
921         /// Note that erasing an entry may invalidate iterators on the
922         /// remote end.  This is just the same as what happens when
923         /// using STL iterators on an STL container in a sequential
924         /// algorithm.
erase(const keyT & key)925         void erase(const keyT& key) {
926             check_initialized();
927             p->erase(key);
928         }
929 
930         /// Erases entry corresponding to \em local iterator (no communication)
erase(const iterator & it)931         void erase(const iterator& it) {
932             check_initialized();
933             p->erase(it);
934         }
935 
936         /// Erases range defined by \em local iterators (no communication)
erase(const iterator & start,const iterator & finish)937         void erase(const iterator& start, const iterator& finish) {
938             check_initialized();
939             p->erase(start,finish);
940         }
941 
942 
943         /// Clears all \em local data (no communication)
944 
945         /// Invalidates all iterators
clear()946         void clear() {
947             check_initialized();
948             p->clear();
949         }
950 
951         /// Returns the number of \em local entries (no communication)
size()952         std::size_t size() const {
953             check_initialized();
954             return p->size();
955         }
956 
957         /// Returns shared pointer to the process mapping
get_pmap()958         inline const std::shared_ptr< WorldDCPmapInterface<keyT> >& get_pmap() const {
959             check_initialized();
960             return p->get_pmap();
961         }
962 
963         /// Returns a reference to the hashing functor
get_hash()964         hashfunT& get_hash() const {
965             check_initialized();
966             return p->get_hash();
967         }
968 
969         /// Process pending messages
970 
971         /// If the constructor was given \c do_pending=false then you
972         /// \em must invoke this routine in order to process both
973         /// prior and future messages.
process_pending()974         inline void process_pending() {
975             check_initialized();
976             p->process_pending();
977         }
978 
979         /// Sends message "resultT memfun()" to item (non-blocking comm if remote)
980 
981         /// If item does not exist it is made with the default constructor.
982         ///
983         /// Future arguments must be ready for remote messages.
984         ///
985         /// Returns a future result (Future<void> may be ignored).
986         ///
987         /// The method executes with a write lock on the item.
988         template <typename memfunT>
989         Future< MEMFUN_RETURNT(memfunT) >
send(const keyT & key,memfunT memfun)990         send(const keyT& key, memfunT memfun) {
991             check_initialized();
992             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT) = &implT:: template itemfun<memfunT>;
993             return p->send(owner(key), itemfun, key, memfun);
994         }
995 
996 
997         /// Sends message "resultT memfun(arg1T)" to item (non-blocking comm if remote)
998 
999         /// If item does not exist it is made with the default constructor.
1000         ///
1001         /// Future arguments must be ready for remote messages.
1002         ///
1003         /// Returns a future result (Future<void> may be ignored).
1004         ///
1005         /// The method executes with a write lock on the item.
1006         template <typename memfunT, typename arg1T>
1007         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,const memfunT & memfun,const arg1T & arg1)1008         send(const keyT& key, const memfunT& memfun, const arg1T& arg1) {
1009             check_initialized();
1010             // To work around bug in g++ 4.3.* use static cast as alternative mechanism to force type deduction
1011             MEMFUN_RETURNT(memfunT) (implT::*itemfun)(const keyT&, memfunT, const arg1T&) = &implT:: template itemfun<memfunT,arg1T>;
1012             return p->send(owner(key), itemfun, key, memfun, arg1);
1013             /*return p->send(owner(key),
1014                            static_cast<MEMFUN_RETURNT(memfunT)(implT::*)(const keyT&, memfunT, const arg1T&)>(&implT:: template itemfun<memfunT,arg1T>),
1015                            key, memfun, arg1);*/
1016         }
1017 
1018 
1019         /// Sends message "resultT memfun(arg1T,arg2T)" to item (non-blocking comm if remote)
1020 
1021         /// If item does not exist it is made with the default constructor.
1022         ///
1023         /// Future arguments must be ready for both local and remote messages.
1024         ///
1025         /// Returns a future result (Future<void> may be ignored).
1026         ///
1027         /// The method executes with a write lock on the item.
1028         template <typename memfunT, typename arg1T, typename arg2T>
1029         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2)1030         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2) {
1031             check_initialized();
1032             // To work around bug in g++ 4.3.* use static cast as alternative mechanism to force type deduction
1033             MEMFUN_RETURNT(memfunT) (implT::*itemfun)(const keyT&, memfunT, const arg1T&, const arg2T&) = &implT:: template itemfun<memfunT,arg1T,arg2T>;
1034             return p->send(owner(key), itemfun, key, memfun, arg1, arg2);
1035             /*return p->send(owner(key),
1036                            static_cast<MEMFUN_RETURNT(memfunT)(implT::*)(const keyT&, memfunT, const arg1T&, const arg2T&)>(&implT:: template itemfun<memfunT,arg1T,arg2T>), key, memfun, arg1, arg2);*/
1037         }
1038 
1039 
1040         /// Sends message "resultT memfun(arg1T,arg2T,arg3T)" to item (non-blocking comm if remote)
1041 
1042         /// If item does not exist it is made with the default constructor.
1043         ///
1044         /// Future arguments must be ready for both local and remote messages.
1045         ///
1046         /// Returns a future result (Future<void> may be ignored).
1047         ///
1048         /// The method executes with a write lock on the item.
1049         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T>
1050         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2,const arg3T & arg3)1051         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3) {
1052             check_initialized();
1053             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const arg1T&, const arg2T&, const arg3T&) = &implT:: template itemfun<memfunT,arg1T,arg2T,arg3T>;
1054             return p->send(owner(key), itemfun, key, memfun, arg1, arg2, arg3);
1055         }
1056 
1057 
1058         /// Sends message "resultT memfun(arg1T,arg2T,arg3T,arg4T)" to item (non-blocking comm if remote)
1059 
1060         /// If item does not exist it is made with the default constructor.
1061         ///
1062         /// Future arguments must be ready for both local and remote messages.
1063         ///
1064         /// Returns a future result (Future<void> may be ignored).
1065         ///
1066         /// The method executes with a write lock on the item.
1067         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T>
1068         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2,const arg3T & arg3,const arg4T & arg4)1069         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4) {
1070             check_initialized();
1071             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const arg1T&, const arg2T&, const arg3T&, const arg4T&) = &implT:: template itemfun<memfunT,arg1T,arg2T,arg3T,arg4T>;
1072             return p->send(owner(key), itemfun, key, memfun, arg1, arg2, arg3, arg4);
1073         }
1074 
1075 
1076         /// Sends message "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T)" to item (non-blocking comm if remote)
1077 
1078         /// If item does not exist it is made with the default constructor.
1079         ///
1080         /// Future arguments must be ready for both local and remote messages.
1081         ///
1082         /// Returns a future result (Future<void> may be ignored).
1083         ///
1084         /// The method executes with a write lock on the item.
1085         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T>
1086         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2,const arg3T & arg3,const arg4T & arg4,const arg5T & arg5)1087         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5) {
1088             check_initialized();
1089             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const arg1T&, const arg2T&, const arg3T&, const arg4T&, const arg5T&) = &implT:: template itemfun<memfunT,arg1T,arg2T,arg3T,arg4T,arg5T>;
1090             return p->send(owner(key), itemfun, key, memfun, arg1, arg2, arg3, arg4, arg5);
1091         }
1092 
1093 
1094         /// Sends message "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T,arg6T)" to item (non-blocking comm if remote)
1095 
1096         /// If item does not exist it is made with the default constructor.
1097         ///
1098         /// Future arguments must be ready for both local and remote messages.
1099         ///
1100         /// Returns a future result (Future<void> may be ignored).
1101         ///
1102         /// The method executes with a write lock on the item.
1103         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T, typename arg6T>
1104         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2,const arg3T & arg3,const arg4T & arg4,const arg5T & arg5,const arg6T & arg6)1105         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5, const arg6T& arg6) {
1106             check_initialized();
1107             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const arg1T&, const arg2T&, const arg3T&, const arg4T&, const arg5T&, const arg6T&) = &implT:: template itemfun<memfunT,arg1T,arg2T,arg3T,arg4T,arg5T,arg6T>;
1108             return p->send(owner(key), itemfun, key, memfun, arg1, arg2, arg3, arg4, arg5, arg6);
1109         }
1110 
1111 
1112         /// Sends message "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T,arg6T,arg7T)" to item (non-blocking comm if remote)
1113 
1114         /// If item does not exist it is made with the default constructor.
1115         ///
1116         /// Future arguments must be ready for both local and remote messages.
1117         ///
1118         /// Returns a future result (Future<void> may be ignored).
1119         ///
1120         /// The method executes with a write lock on the item.
1121         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T, typename arg6T, typename arg7T>
1122         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2,const arg3T & arg3,const arg4T & arg4,const arg5T & arg5,const arg6T & arg6,const arg7T & arg7)1123         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4,
1124 		     const arg5T& arg5, const arg6T& arg6, const arg7T& arg7) {
1125             check_initialized();
1126             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const arg1T&, const arg2T&, const arg3T&, const arg4T&, const arg5T&, const arg6T&, const arg7T&) = &implT:: template itemfun<memfunT,arg1T,arg2T,arg3T,arg4T,arg5T,arg6T,arg7T>;
1127             return p->send(owner(key), itemfun, key, memfun, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
1128         }
1129 
1130 
1131         /// Sends message "resultT memfun() const" to item (non-blocking comm if remote)
1132 
1133         /// The method executes with a write lock on the item.
1134         template <typename memfunT>
1135         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun)1136         send(const keyT& key, memfunT memfun) const {
1137             return const_cast<containerT*>(this)->send(key,memfun);
1138         }
1139 
1140         /// Sends message "resultT memfun(arg1T) const" to item (non-blocking comm if remote)
1141 
1142         /// The method executes with a write lock on the item.
1143         template <typename memfunT, typename arg1T>
1144         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1)1145         send(const keyT& key, memfunT memfun, const arg1T& arg1) const {
1146             return const_cast<containerT*>(this)->send(key,memfun,arg1);
1147         }
1148 
1149         /// Sends message "resultT memfun(arg1T,arg2T) const" to item (non-blocking comm if remote)
1150 
1151         /// The method executes with a write lock on the item.
1152         template <typename memfunT, typename arg1T, typename arg2T>
1153         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2)1154         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2) const {
1155             return const_cast<containerT*>(this)->send(key,memfun,arg1,arg2);
1156         }
1157 
1158 
1159         /// Sends message "resultT memfun(arg1T,arg2T,arg3T) const" to item (non-blocking comm if remote)
1160 
1161         /// The method executes with a write lock on the item.
1162         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T>
1163         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2,const arg3T & arg3)1164         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3) const {
1165             return const_cast<containerT*>(this)->send(key,memfun,arg1,arg2,arg3);
1166         }
1167 
1168         /// Sends message "resultT memfun(arg1T,arg2T,arg3T,arg4T) const" to item (non-blocking comm if remote)
1169 
1170         /// The method executes with a write lock on the item.
1171         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T>
1172         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2,const arg3T & arg3,const arg4T & arg4)1173         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4) const {
1174             return const_cast<containerT*>(this)->send(key,memfun,arg1,arg2,arg3,arg4);
1175         }
1176 
1177         /// Sends message "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T) const" to item (non-blocking comm if remote)
1178 
1179         /// The method executes with a write lock on the item.
1180         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T>
1181         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2,const arg3T & arg3,const arg4T & arg4,const arg5T & arg5)1182         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5) const {
1183             return const_cast<containerT*>(this)->send(key,memfun,arg1,arg2,arg3,arg4,arg5);
1184         }
1185 
1186         /// Sends message "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T,arg6T) const" to item (non-blocking comm if remote)
1187 
1188         /// The method executes with a write lock on the item.
1189         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T, typename arg6T>
1190         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2,const arg3T & arg3,const arg4T & arg4,const arg5T & arg5,const arg6T & arg6)1191         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3,
1192 			 const arg4T& arg4, const arg5T& arg5, const arg6T& arg6) const {
1193             return const_cast<containerT*>(this)->send(key,memfun,arg1,arg2,arg3,arg4,arg5,arg6);
1194         }
1195 
1196         /// Sends message "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T,arg6T,arg7T) const" to item (non-blocking comm if remote)
1197 
1198         /// The method executes with a write lock on the item.
1199         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T, typename arg6T, typename arg7T>
1200         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
send(const keyT & key,memfunT memfun,const arg1T & arg1,const arg2T & arg2,const arg3T & arg3,const arg4T & arg4,const arg5T & arg5,const arg6T & arg6,const arg7T & arg7)1201         send(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3,
1202 			 const arg4T& arg4, const arg5T& arg5, const arg6T& arg6, const arg7T& arg7) const {
1203             return const_cast<containerT*>(this)->send(key,memfun,arg1,arg2,arg3,arg4,arg5,arg6,arg7);
1204         }
1205 
1206 
1207         /// Adds task "resultT memfun()" in process owning item (non-blocking comm if remote)
1208 
1209         /// If item does not exist it is made with the default constructor.
1210         ///
1211         /// Future arguments for local tasks can generate dependencies, but for remote
1212         /// tasks all futures must be ready.
1213         ///
1214         /// Returns a future result (Future<void> may be ignored).
1215         ///
1216         /// The method executes with a write lock on the item.
1217         template <typename memfunT>
1218         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1219         task(const keyT& key, memfunT memfun, const TaskAttributes& attr = TaskAttributes()) {
1220             check_initialized();
1221             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT) = &implT:: template itemfun<memfunT>;
1222             return p->task(owner(key), itemfun, key, memfun, attr);
1223         }
1224 
1225         /// Adds task "resultT memfun(arg1T)" in process owning item (non-blocking comm if remote)
1226 
1227         /// If item does not exist it is made with the default constructor.
1228         ///
1229         /// Future arguments for local tasks can generate dependencies, but for remote
1230         /// tasks all futures must be ready.
1231         ///
1232         /// Returns a future result (Future<void> may be ignored).
1233         ///
1234         /// The method executes with a write lock on the item.
1235         template <typename memfunT, typename arg1T>
1236         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1237         task(const keyT& key, memfunT memfun, const arg1T& arg1, const TaskAttributes& attr = TaskAttributes()) {
1238             check_initialized();
1239             typedef REMFUTURE(arg1T) a1T;
1240             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const a1T&) = &implT:: template itemfun<memfunT,a1T>;
1241             return p->task(owner(key), itemfun, key, memfun, arg1, attr);
1242         }
1243 
1244         /// Adds task "resultT memfun(arg1T,arg2T)" in process owning item (non-blocking comm if remote)
1245 
1246         /// If item does not exist it is made with the default constructor.
1247         ///
1248         /// Future arguments for local tasks can generate dependencies, but for remote
1249         /// tasks all futures must be ready.
1250         ///
1251         /// Returns a future result (Future<void> may be ignored).
1252         ///
1253         /// The method executes with a write lock on the item.
1254         template <typename memfunT, typename arg1T, typename arg2T>
1255         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1256         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const TaskAttributes& attr = TaskAttributes()) {
1257             check_initialized();
1258             typedef REMFUTURE(arg1T) a1T;
1259             typedef REMFUTURE(arg2T) a2T;
1260             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const a1T&, const a2T&) = &implT:: template itemfun<memfunT,a1T,a2T>;
1261             return p->task(owner(key), itemfun, key, memfun, arg1, arg2, attr);
1262         }
1263 
1264         /// Adds task "resultT memfun(arg1T,arg2T,arg3T)" in process owning item (non-blocking comm if remote)
1265 
1266         /// If item does not exist it is made with the default constructor.
1267         ///
1268         /// Future arguments for local tasks can generate dependencies, but for remote
1269         /// tasks all futures must be ready.
1270         ///
1271         /// Returns a future result (Future<void> may be ignored).
1272         ///
1273         /// The method executes with a write lock on the item.
1274         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T>
1275         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1276         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const TaskAttributes& attr = TaskAttributes()) {
1277             check_initialized();
1278             typedef REMFUTURE(arg1T) a1T;
1279             typedef REMFUTURE(arg2T) a2T;
1280             typedef REMFUTURE(arg3T) a3T;
1281             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const a1T&, const a2T&, const a3T&) = &implT:: template itemfun<memfunT,a1T,a2T,a3T>;
1282             return p->task(owner(key), itemfun, key, memfun, arg1, arg2, arg3, attr);
1283         }
1284 
1285         /// Adds task "resultT memfun(arg1T,arg2T,arg3T,arg4T)" in process owning item (non-blocking comm if remote)
1286 
1287         /// If item does not exist it is made with the default constructor.
1288         ///
1289         /// Future arguments for local tasks can generate dependencies, but for remote
1290         /// tasks all futures must be ready.
1291         ///
1292         /// Returns a future result (Future<void> may be ignored).
1293         ///
1294         /// The method executes with a write lock on the item.
1295         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T>
1296         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1297         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const TaskAttributes& attr = TaskAttributes()) {
1298             check_initialized();
1299             typedef REMFUTURE(arg1T) a1T;
1300             typedef REMFUTURE(arg2T) a2T;
1301             typedef REMFUTURE(arg3T) a3T;
1302             typedef REMFUTURE(arg4T) a4T;
1303             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const a1T&, const a2T&, const a3T&, const a4T&) = &implT:: template itemfun<memfunT,a1T,a2T,a3T,a4T>;
1304             return p->task(owner(key), itemfun, key, memfun, arg1, arg2, arg3, arg4, attr);
1305         }
1306 
1307         /// Adds task "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T)" in process owning item (non-blocking comm if remote)
1308 
1309         /// If item does not exist it is made with the default constructor.
1310         ///
1311         /// Future arguments for local tasks can generate dependencies, but for remote
1312         /// tasks all futures must be ready.
1313         ///
1314         /// Returns a future result (Future<void> may be ignored).
1315         ///
1316         /// The method executes with a write lock on the item.
1317         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T>
1318         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1319         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5, const TaskAttributes& attr = TaskAttributes()) {
1320             check_initialized();
1321             typedef REMFUTURE(arg1T) a1T;
1322             typedef REMFUTURE(arg2T) a2T;
1323             typedef REMFUTURE(arg3T) a3T;
1324             typedef REMFUTURE(arg4T) a4T;
1325             typedef REMFUTURE(arg5T) a5T;
1326             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const a1T&, const a2T&, const a3T&, const a4T&, const a5T&) = &implT:: template itemfun<memfunT,a1T,a2T,a3T,a4T,a5T>;
1327             return p->task(owner(key), itemfun, key, memfun, arg1, arg2, arg3, arg4, arg5, attr);
1328         }
1329 
1330         /// Adds task "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T,arg6T)" in process owning item (non-blocking comm if remote)
1331 
1332         /// If item does not exist it is made with the default constructor.
1333         ///
1334         /// Future arguments for local tasks can generate dependencies, but for remote
1335         /// tasks all futures must be ready.
1336         ///
1337         /// Returns a future result (Future<void> may be ignored).
1338         ///
1339         /// The method executes with a write lock on the item.
1340         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T, typename arg6T>
1341         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1342         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5, const arg6T& arg6, const TaskAttributes& attr = TaskAttributes()) {
1343             check_initialized();
1344             typedef REMFUTURE(arg1T) a1T;
1345             typedef REMFUTURE(arg2T) a2T;
1346             typedef REMFUTURE(arg3T) a3T;
1347             typedef REMFUTURE(arg4T) a4T;
1348             typedef REMFUTURE(arg5T) a5T;
1349             typedef REMFUTURE(arg6T) a6T;
1350             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const a1T&, const a2T&, const a3T&, const a4T&, const a5T&, const a6T&) = &implT:: template itemfun<memfunT,a1T,a2T,a3T,a4T,a5T,a6T>;
1351             return p->task(owner(key), itemfun, key, memfun, arg1, arg2, arg3, arg4, arg5, arg6, attr);
1352         }
1353 
1354         /// Adds task "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T,arg6T,arg7T)" in process owning item (non-blocking comm if remote)
1355 
1356         /// If item does not exist it is made with the default constructor.
1357         ///
1358         /// Future arguments for local tasks can generate dependencies, but for remote
1359         /// tasks all futures must be ready.
1360         ///
1361         /// Returns a future result (Future<void> may be ignored).
1362         ///
1363         /// The method executes with a write lock on the item.
1364         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T, typename arg6T, typename arg7T>
1365         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1366         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5, const arg6T& arg6, const arg7T& arg7, const TaskAttributes& attr = TaskAttributes()) {
1367             check_initialized();
1368             typedef REMFUTURE(arg1T) a1T;
1369             typedef REMFUTURE(arg2T) a2T;
1370             typedef REMFUTURE(arg3T) a3T;
1371             typedef REMFUTURE(arg4T) a4T;
1372             typedef REMFUTURE(arg5T) a5T;
1373             typedef REMFUTURE(arg6T) a6T;
1374             typedef REMFUTURE(arg7T) a7T;
1375             MEMFUN_RETURNT(memfunT)(implT::*itemfun)(const keyT&, memfunT, const a1T&, const a2T&, const a3T&, const a4T&, const a5T&, const a6T&, const a7T&) = &implT:: template itemfun<memfunT,a1T,a2T,a3T,a4T,a5T,a6T,a7T>;
1376             return p->task(owner(key), itemfun, key, memfun, arg1, arg2, arg3, arg4, arg5, arg6, arg7, attr);
1377         }
1378 
1379         /// Adds task "resultT memfun() const" in process owning item (non-blocking comm if remote)
1380 
1381         /// The method executes with a write lock on the item.
1382         template <typename memfunT>
1383         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1384         task(const keyT& key, memfunT memfun,  const TaskAttributes& attr = TaskAttributes()) const {
1385             return const_cast<containerT*>(this)->task(key,memfun,attr);
1386         }
1387 
1388         /// Adds task "resultT memfun(arg1T) const" in process owning item (non-blocking comm if remote)
1389 
1390         /// The method executes with a write lock on the item.
1391         template <typename memfunT, typename arg1T>
1392         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1393         task(const keyT& key, memfunT memfun, const arg1T& arg1,  const TaskAttributes& attr = TaskAttributes()) const {
1394             return const_cast<containerT*>(this)->task(key,memfun,arg1,attr);
1395         }
1396 
1397         /// Adds task "resultT memfun(arg1T,arg2T) const" in process owning item (non-blocking comm if remote)
1398 
1399         /// The method executes with a write lock on the item.
1400         template <typename memfunT, typename arg1T, typename arg2T>
1401         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1402         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2,  const TaskAttributes& attr = TaskAttributes()) const {
1403             return const_cast<containerT*>(this)->task(key,memfun,arg1,arg2,attr);
1404         }
1405 
1406         /// Adds task "resultT memfun(arg1T,arg2T,arg3T) const" in process owning item (non-blocking comm if remote)
1407 
1408         /// The method executes with a write lock on the item.
1409         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T>
1410         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1411         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const TaskAttributes& attr = TaskAttributes()) const {
1412             return const_cast<containerT*>(this)->task(key,memfun,arg1,arg2,arg3,attr);
1413         }
1414 
1415         /// Adds task "resultT memfun(arg1T,arg2T,arg3T, arg4T) const" in process owning item (non-blocking comm if remote)
1416 
1417         /// The method executes with a write lock on the item.
1418         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T>
1419         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1420         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const TaskAttributes& attr = TaskAttributes()) const {
1421             return const_cast<containerT*>(this)->task(key,memfun,arg1,arg2,arg3,arg4,attr);
1422         }
1423 
1424         /// Adds task "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T) const" in process owning item (non-blocking comm if remote)
1425 
1426         /// The method executes with a write lock on the item.
1427         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T>
1428         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1429         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5, const TaskAttributes& attr = TaskAttributes()) const {
1430             return const_cast<containerT*>(this)->task(key,memfun,arg1,arg2,arg3,arg4,arg5,attr);
1431         }
1432 
1433         /// Adds task "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T,arg6T) const" in process owning item (non-blocking comm if remote)
1434 
1435         /// The method executes with a write lock on the item.
1436         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T, typename arg6T>
1437         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1438         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5, const arg6T& arg6, const TaskAttributes& attr = TaskAttributes()) const {
1439             return const_cast<containerT*>(this)->task(key,memfun,arg1,arg2,arg3,arg4,arg5,arg6,attr);
1440         }
1441 
1442         /// Adds task "resultT memfun(arg1T,arg2T,arg3T,arg4T,arg5T,arg6T,arg7T) const" in process owning item (non-blocking comm if remote)
1443 
1444         /// The method executes with a write lock on the item.
1445         template <typename memfunT, typename arg1T, typename arg2T, typename arg3T, typename arg4T, typename arg5T, typename arg6T, typename arg7T>
1446         Future< REMFUTURE(MEMFUN_RETURNT(memfunT)) >
1447         task(const keyT& key, memfunT memfun, const arg1T& arg1, const arg2T& arg2, const arg3T& arg3, const arg4T& arg4, const arg5T& arg5, const arg6T& arg6, const arg7T& arg7, const TaskAttributes& attr = TaskAttributes()) const {
1448             return const_cast<containerT*>(this)->task(key,memfun,arg1,arg2,arg3,arg4,arg5,arg6,arg7,attr);
1449         }
1450 
1451 
1452         /// (de)Serialize --- *Local* data only to/from anything *except* Buffer*Archive and Parallel*Archive
1453 
1454         /// Advisable for *you* to fence before and after this to ensure consistency
1455         template <typename Archive>
serialize(const Archive & ar)1456         void serialize(const Archive& ar) {
1457             //
1458             // !! If you change the format of this stream make sure that
1459             // !! the parallel in/out archive below is compatible
1460             //
1461             const long magic = 5881828; // Sitar Indian restaurant in Knoxville
1462             unsigned long count = 0;
1463             check_initialized();
1464 
1465             if (Archive::is_output_archive) {
1466                 ar & magic;
1467                 for (iterator it=begin(); it!=end(); ++it) count++;
1468                 ar & count;
1469                 for (iterator it=begin(); it!=end(); ++it) ar & *it;
1470             }
1471             else {
1472                 long cookie = 0l;
1473                 ar & cookie;
1474                 MADNESS_ASSERT(cookie == magic);
1475                 ar & count;
1476                 while (count--) {
1477                     pairT datum;
1478                     ar & datum;
1479                     replace(datum);
1480                 }
1481             }
1482         }
1483 
1484         /// (de)Serialize --- !! ONLY for purpose of interprocess communication
1485 
1486         /// This just writes/reads the unique id to/from the Buffer*Archive.
serialize(const archive::BufferOutputArchive & ar)1487         void serialize(const archive::BufferOutputArchive& ar) {
1488             check_initialized();
1489             ar & static_cast<WorldObject<implT>*>(p.get());
1490         }
1491 
1492         /// (de)Serialize --- !! ONLY for purpose of interprocess communication
1493 
1494         /// This just writes/reads the unique id to/from the Buffer*Archive.
serialize(const archive::BufferInputArchive & ar)1495         void serialize(const archive::BufferInputArchive& ar) {
1496             WorldObject<implT>* ptr = nullptr;
1497             ar & ptr;
1498             MADNESS_ASSERT(ptr);
1499 
1500 #ifdef MADNESS_DISABLE_SHARED_FROM_THIS
1501             p.reset(static_cast<implT*>(ptr), [] (implT *p_) -> void {});
1502 #else
1503             p = static_cast<implT*>(ptr)->shared_from_this();
1504 #endif // MADNESS_DISABLE_SHARED_FROM_THIS
1505         }
1506 
1507         /// Returns the associated unique id ... must be initialized
id()1508         const uniqueidT& id() const {
1509             check_initialized();
1510             return p->id();
1511         }
1512 
1513         /// Destructor passes ownership of implementation to world for deferred cleanup
~WorldContainer()1514         virtual ~WorldContainer() {
1515             detail::deferred_cleanup(p->get_world(), p);
1516         }
1517 
1518         friend void swap<>(WorldContainer&, WorldContainer&);
1519     };
1520 
1521     /// Swaps the content of two WorldContainer objects. It should be called on all nodes.
1522 
1523     /// \ingroup worlddc
1524     template <typename keyT, typename valueT, typename hashfunT>
swap(WorldContainer<keyT,valueT,hashfunT> & dc0,WorldContainer<keyT,valueT,hashfunT> & dc1)1525     void swap(WorldContainer<keyT, valueT, hashfunT>& dc0, WorldContainer<keyT, valueT, hashfunT>& dc1) {
1526       std::swap(dc0.p, dc1.p);
1527     }
1528 
1529     namespace archive {
1530         /// Write container to parallel archive with optional fence
1531 
1532         /// \ingroup worlddc
1533         /// Each node (process) is served by a designated IO node.
1534         /// The IO node has a binary local file archive to which is
1535         /// first written a cookie and the number of servers.  The IO
1536         /// node then loops thru all of its clients and in turn tells
1537         /// each to write its data over an MPI stream, which is copied
1538         /// directly to the output file.  The stream contents are then
1539         /// cookie, no. of clients, foreach client (usual sequential archive).
1540         ///
1541         /// If ar.dofence() is true (default) fence is invoked before and
1542         /// after the IO. The fence is optional but it is of course
1543         /// necessary to be sure that all updates have completed
1544         /// before doing IO, and that all IO has completed before
1545         /// subsequent modifications. Also, there is always at least
1546         /// some synchronization between a client and its IO server.
1547         template <class keyT, class valueT>
1548         struct ArchiveStoreImpl< ParallelOutputArchive, WorldContainer<keyT,valueT> > {
1549             static void store(const ParallelOutputArchive& ar, const WorldContainer<keyT,valueT>& t) {
1550                 const long magic = -5881828; // Sitar Indian restaurant in Knoxville (negative to indicate parallel!)
1551                 typedef WorldContainer<keyT,valueT> dcT;
1552                 // typedef typename dcT::const_iterator iterator; // unused?
1553                 typedef typename dcT::pairT pairT;
1554                 World* world = ar.get_world();
1555                 Tag tag = world->mpi.unique_tag();
1556                 ProcessID me = world->rank();
1557                 if (ar.dofence()) world->gop.fence();
1558                 if (ar.is_io_node()) {
1559                     BinaryFstreamOutputArchive& localar = ar.local_archive();
1560                     localar & magic & ar.num_io_clients();
1561                     for (ProcessID p=0; p<world->size(); ++p) {
1562                         if (p == me) {
1563                             localar & t;
1564                         }
1565                         else if (ar.io_node(p) == me) {
1566                             world->mpi.Send(int(1),p,tag); // Tell client to start sending
1567                             archive::MPIInputArchive source(*world, p);
1568                             long cookie = 0l;
1569                             unsigned long count = 0ul;
1570 
1571                             ArchivePrePostImpl<BinaryFstreamOutputArchive,dcT>::preamble_store(localar);
1572 
1573                             source & cookie & count;
1574                             localar & cookie & count;
1575                             while (count--) {
1576                                 pairT datum;
1577                                 source & datum;
1578                                 localar & datum;
1579                             }
1580 
1581                             ArchivePrePostImpl<BinaryFstreamOutputArchive,dcT>::postamble_store(localar);
1582                         }
1583                     }
1584                 }
1585                 else {
1586                     ProcessID p = ar.my_io_node();
1587                     int flag;
1588                     world->mpi.Recv(flag,p,tag);
1589                     MPIOutputArchive dest(*world, p);
1590                     dest & t;
1591                     dest.flush();
1592                 }
1593                 if (ar.dofence()) world->gop.fence();
1594             }
1595         };
1596 
1597         template <class keyT, class valueT>
1598         struct ArchiveLoadImpl< ParallelInputArchive, WorldContainer<keyT,valueT> > {
1599             /// Read container from parallel archive
1600 
1601             /// \ingroup worlddc
1602             /// See store method above for format of file content.
1603             /// !!! We presently ASSUME that the number of writers and readers are
1604             /// the same.  This is frustrating but not a show stopper since you
1605             /// can always run a separate job to copy to a different number.
1606             ///
1607             /// The IO node simply reads all data and inserts entries.
1608             static void load(const ParallelInputArchive& ar, WorldContainer<keyT,valueT>& t) {
1609                 const long magic = -5881828; // Sitar Indian restaurant in Knoxville (negative to indicate parallel!)
1610                 // typedef WorldContainer<keyT,valueT> dcT; // unused
1611                 // typedef typename dcT::iterator iterator; // unused
1612                 // typedef typename dcT::pairT pairT; // unused
1613                 World* world = ar.get_world();
1614                 if (ar.dofence()) world->gop.fence();
1615                 if (ar.is_io_node()) {
1616                     long cookie = 0l;
1617                     int nclient = 0;
1618                     BinaryFstreamInputArchive& localar = ar.local_archive();
1619                     localar & cookie & nclient;
1620                     MADNESS_ASSERT(cookie == magic);
1621                     while (nclient--) {
1622                         localar & t;
1623                     }
1624                 }
1625                 if (ar.dofence()) world->gop.fence();
1626             }
1627         };
1628     }
1629 
1630 }
1631 
1632 ///@}
1633 
1634 #endif // MADNESS_WORLD_WORLDDC_H__INCLUDED
1635