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