1 // Copyright (c) 2007-2016 Hartmut Kaiser 2 // Copyright (c) 2011 Bryce Lelbach 3 // 4 // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 7 #include <hpx/runtime/naming/id_type.hpp> 8 #include <hpx/runtime/naming/name.hpp> 9 10 #include <hpx/error_code.hpp> 11 #include <hpx/exception.hpp> 12 #include <hpx/lcos/future.hpp> 13 #include <hpx/runtime/agas/addressing_service.hpp> 14 #include <hpx/runtime/components/server/destroy_component.hpp> 15 #include <hpx/runtime/launch_policy.hpp> 16 #include <hpx/runtime/naming/address.hpp> 17 #include <hpx/runtime/naming/split_gid.hpp> 18 #include <hpx/runtime/serialization/intrusive_ptr.hpp> 19 #include <hpx/runtime/serialization/serialize.hpp> 20 #include <hpx/runtime_fwd.hpp> 21 #include <hpx/state.hpp> 22 #include <hpx/throw_exception.hpp> 23 #include <hpx/traits/is_bitwise_serializable.hpp> 24 #include <hpx/util/assert.hpp> 25 #include <hpx/util/assert_owns_lock.hpp> 26 #include <hpx/util/bind.hpp> 27 #include <hpx/util/logging.hpp> 28 #include <hpx/util/unlock_guard.hpp> 29 30 #include <boost/io/ios_state.hpp> 31 32 #include <cstdint> 33 #include <functional> 34 #include <iomanip> 35 #include <iostream> 36 #include <memory> 37 #include <mutex> 38 #include <sstream> 39 #include <string> 40 #include <utility> 41 42 /////////////////////////////////////////////////////////////////////////////// 43 // 44 // Here is how our distributed garbage collection works 45 // 46 // Each id_type instance - while always referring to some (possibly remote) 47 // entity - can either be 'managed' or 'unmanaged'. If an id_type instance is 48 // 'unmanaged' it does not perform any garbage collection. Otherwise (if it's 49 // 'managed'), all of its copies are globally tracked which allows to 50 // automatically delete the entity a particular id_type instance is referring 51 // to after the last reference to it goes out of scope. 52 // 53 // An id_type instance is essentially a shared_ptr<> maintaining two reference 54 // counts: a local reference count and a global one. The local reference count 55 // is incremented whenever the id_type instance is copied locally, and decremented 56 // whenever one of the local copies goes out of scope. At the point when the last 57 // local copy goes out of scope, it returns its current share of the global 58 // reference count back to AGAS. The share of the global reference count owned 59 // by all copies of an id_type instance on a single locality is called its 60 // credit. Credits are issued in chunks which allows to create a global copy 61 // of an id_type instance (like passing it to another locality) without needing 62 // to talk to AGAS to request a global reference count increment. The referenced 63 // entity is freed when the global reference count falls to zero. 64 // 65 // Any newly created object assumes an initial credit. This credit is not 66 // accounted for by AGAS as long as no global increment or decrement requests 67 // are received. It is important to understand that there is no way to distinguish 68 // whether an object has already been deleted (and therefore no entry exists in 69 // the table storing the global reference count for this object) or whether the 70 // object is still alive but no increment/decrement requests have been received 71 // by AGAS yet. While this is a pure optimization to avoid storing global 72 // reference counts for all objects, it has implications for the implemented 73 // garbage collection algorithms at large. 74 // 75 // As long as an id_type instance is not sent to another locality (a locality 76 // different from the initial locality creating the referenced entity), all 77 // lifetime management for this entity can be handled purely local without 78 // even talking to AGAS. 79 // 80 // Sending an id_type instance to another locality (which includes using an 81 // id_type as the destination for an action) splits the current credit into 82 // two parts. One part stays with the id_type on the sending locality, the 83 // other part is sent along to the destination locality where it turns into the 84 // global credit associated with the remote copy of the id_type. As stated 85 // above, this allows to avoid talking to AGAS for incrementing the global 86 // reference count as long as there is sufficient global credit left in order 87 // to be split. 88 // 89 // The current share of the global credit associated with an id_type instance 90 // is encoded in the bits 88..92 of the underlying gid_type (encoded as the 91 // logarithm to the base 2 of the credit value). Bit 94 is a flag which is set 92 // whenever the credit is valid. Bit 95 encodes whether the given id_type 93 // has been split at any time. This information is needed to be able to decide 94 // whether a garbage collection can be assumed to be a purely local operation. 95 // Bit 93 is used by the locking scheme for gid_types. 96 // 97 // Credit splitting is performed without any additional AGAS traffic as long as 98 // sufficient credit is available. If the credit of the id_type to be split is 99 // exhausted (reaches the value '1') it has to be replenished. This operation 100 // is performed synchronously. This is done to ensure that AGAS has accounted 101 // for the requested credit increase. 102 // 103 // Note that both the id_type instance staying behind and the one sent along 104 // are replenished before sending out the parcel at the sending locality. 105 // 106 /////////////////////////////////////////////////////////////////////////////// 107 108 /////////////////////////////////////////////////////////////////////////////// 109 namespace hpx { namespace naming { namespace detail 110 { 111 struct gid_serialization_data; 112 }}} 113 114 HPX_IS_BITWISE_SERIALIZABLE(hpx::naming::detail::gid_serialization_data) 115 116 namespace hpx { namespace naming 117 { 118 namespace detail 119 { decrement_refcnt(detail::id_type_impl * p)120 void decrement_refcnt(detail::id_type_impl* p) 121 { 122 // do nothing if it's too late in the game 123 if (!get_runtime_ptr()) 124 { 125 delete p; // delete local gid representation in any case 126 return; 127 } 128 129 // Talk to AGAS only if this gid was split at some time in the past, 130 // i.e. if a reference actually left the original locality. 131 // Alternatively we need to go this way if the id has never been 132 // resolved, which means we don't know anything about the component 133 // type. 134 naming::address addr; 135 if ((gid_was_split(*p) || 136 !naming::get_agas_client().resolve_cached(*p, addr))) 137 { 138 // guard for wait_abort and other shutdown issues 139 try { 140 // decrement global reference count for the given gid, 141 std::int64_t credits = detail::get_credit_from_gid(*p); 142 HPX_ASSERT(0 != credits); 143 144 if (get_runtime_ptr()) 145 { 146 // Fire-and-forget semantics. 147 error_code ec(lightweight); 148 agas::decref(*p, credits, ec); 149 } 150 } 151 catch (hpx::exception const& e) { 152 LTM_(error) 153 << "Unhandled exception while executing decrement_refcnt:" 154 << e.what(); 155 } 156 } 157 else { 158 // If the gid was not split at any point in time we can assume 159 // that the referenced object is fully local. 160 HPX_ASSERT(addr.type_ != components::component_invalid); 161 162 // Third parameter is the count of how many components to destroy. 163 // FIXME: The address should still be in the cache, but it could 164 // be evicted. It would be nice to have a way to pass the address 165 // directly to destroy_component. 166 try { 167 components::server::destroy_component(*p, addr); 168 } 169 catch (hpx::exception const& e) { 170 // This request might come in too late and the thread manager 171 // was already stopped. We ignore the request if that's the 172 // case. 173 if (e.get_error() != invalid_status) { 174 throw; // rethrow if not invalid_status 175 } 176 else if (!threads::threadmanager_is(hpx::state_stopping)) { 177 throw; // rethrow if not stopping 178 } 179 } 180 } 181 delete p; // delete local gid representation in any case 182 } 183 184 // custom deleter for managed gid_types, will be called when the last 185 // copy of the corresponding naming::id_type goes out of scope gid_managed_deleter(id_type_impl * p)186 void gid_managed_deleter(id_type_impl* p) 187 { 188 // a credit of zero means the component is not (globally) reference 189 // counted 190 if (detail::has_credits(*p)) { 191 // execute the deleter directly 192 decrement_refcnt(p); 193 } 194 else { 195 delete p; // delete local gid representation if needed 196 } 197 } 198 199 // custom deleter for unmanaged gid_types, will be called when the last 200 // copy of the corresponding naming::id_type goes out of scope gid_unmanaged_deleter(id_type_impl * p)201 void gid_unmanaged_deleter (id_type_impl* p) 202 { 203 delete p; // delete local gid representation only 204 } 205 206 /////////////////////////////////////////////////////////////////////// get_deleter(id_type_management t)207 id_type_impl::deleter_type id_type_impl::get_deleter(id_type_management t) 208 { 209 switch (t) { 210 case unmanaged: 211 return &detail::gid_unmanaged_deleter; 212 213 case managed: 214 case managed_move_credit: 215 return &detail::gid_managed_deleter; 216 217 default: 218 HPX_ASSERT(false); // invalid management type 219 return &detail::gid_unmanaged_deleter; 220 } 221 return nullptr; 222 } 223 224 /////////////////////////////////////////////////////////////////////// 225 // prepare the given id, note: this function modifies the passed id preprocess_gid(serialization::output_archive & ar) const226 void id_type_impl::preprocess_gid(serialization::output_archive& ar) const 227 { 228 // unmanaged gids do not require any special handling 229 if (unmanaged == type_) 230 { 231 return; 232 } 233 234 if (ar.has_gid(*this)) 235 { 236 return; 237 } 238 239 HPX_ASSERT(has_credits(*this)); 240 241 // Request new credits from AGAS if needed (i.e. the remainder 242 // of the credit splitting is equal to one). 243 if (managed == type_) 244 { 245 ar.await_future( 246 split_gid_if_needed(const_cast<id_type_impl&>(*this)).then( 247 hpx::launch::sync, 248 [&ar, this](hpx::future<gid_type> && gid_future) 249 { 250 ar.add_gid(*this, gid_future.get()); 251 } 252 ) 253 ); 254 return; 255 } 256 } 257 258 /////////////////////////////////////////////////////////////////////// split_gid_if_needed(gid_type & gid)259 hpx::future<gid_type> split_gid_if_needed(gid_type& gid) 260 { 261 typedef std::unique_lock<gid_type::mutex_type> scoped_lock; 262 scoped_lock l(gid.get_mutex()); 263 return split_gid_if_needed_locked(l, gid); 264 } 265 postprocess_incref(gid_type & gid)266 gid_type postprocess_incref(gid_type &gid) 267 { 268 typedef std::unique_lock<gid_type::mutex_type> scoped_lock; 269 scoped_lock l(gid.get_mutex()); 270 271 gid_type new_gid = gid; // strips lock-bit 272 HPX_ASSERT(new_gid != invalid_gid); 273 274 // The old gid should have been marked as been split below 275 HPX_ASSERT(gid_was_split(gid)); 276 277 // Fill the new gid with our new credit and mark it as being split 278 naming::detail::set_credit_for_gid(new_gid, 279 static_cast<std::int64_t>(HPX_GLOBALCREDIT_INITIAL)); 280 set_credit_split_mask_for_gid(new_gid); 281 282 // Another concurrent split operation might have happened 283 // concurrently, we need to add the new split credits to the old 284 // and account for overflow. 285 286 // Get the current credit for our gid. If no other concurrent 287 // split has happened since we invoked incref below, the credit 288 // of this gid is equal to 2, otherwise it is larger. 289 std::int64_t src_credit = get_credit_from_gid(gid); 290 HPX_ASSERT(src_credit >= 2); 291 292 std::int64_t split_credit = 293 static_cast<std::int64_t>(HPX_GLOBALCREDIT_INITIAL) - 2; 294 std::int64_t new_credit = src_credit + split_credit; 295 std::int64_t overflow_credit = new_credit - 296 static_cast<std::int64_t>(HPX_GLOBALCREDIT_INITIAL); 297 298 new_credit 299 = (std::min)( 300 static_cast<std::int64_t>(HPX_GLOBALCREDIT_INITIAL), 301 new_credit); 302 naming::detail::set_credit_for_gid(gid, new_credit); 303 304 // Account for a possible overflow ... 305 if(overflow_credit > 0) 306 { 307 HPX_ASSERT(overflow_credit <= HPX_GLOBALCREDIT_INITIAL-1); 308 l.unlock(); 309 310 // Note that this operation may be asynchronous 311 agas::decref(new_gid, overflow_credit); 312 } 313 314 return new_gid; 315 } 316 split_gid_if_needed_locked(std::unique_lock<gid_type::mutex_type> & l,gid_type & gid)317 hpx::future<gid_type> split_gid_if_needed_locked( 318 std::unique_lock<gid_type::mutex_type> &l, gid_type& gid) 319 { 320 HPX_ASSERT_OWNS_LOCK(l); 321 322 if (naming::detail::has_credits(gid)) 323 { 324 // The splitting is happening in two parts: 325 // First get the current credit and split it: 326 // Case 1: credit == 1 ==> we need to request new credit from 327 // AGAS. This is happening asynchronously. 328 // Case 2: credit != 1 ==> Just fill with new credit 329 // 330 // Scenario that might happen: 331 // An id_type which needs to be split is being split concurrently 332 // while we unlock the lock to ask for more credit: 333 // This might lead to an overflow in the credit mask and 334 // needs to be accounted for by sending a decref with the 335 // excessive credit. 336 // 337 // An early decref can't happen as the id_type with the new 338 // credit is guaranteed to arrive only after we incremented the 339 // credit successfully in agas. 340 HPX_ASSERT(get_log2credit_from_gid(gid) > 0); 341 std::int16_t src_log2credits = get_log2credit_from_gid(gid); 342 343 // Credit exhaustion - we need to get more. 344 if(src_log2credits == 1) 345 { 346 // mark gid as being split 347 set_credit_split_mask_for_gid(gid); 348 349 l.unlock(); 350 351 // We add HPX_GLOBALCREDIT_INITIAL credits for the new gid 352 // and HPX_GLOBALCREDIT_INITIAL - 2 for the old one. 353 std::int64_t new_credit = 2 * 354 (static_cast<std::int64_t>(HPX_GLOBALCREDIT_INITIAL) - 1); 355 356 naming::gid_type new_gid = gid; // strips lock-bit 357 HPX_ASSERT(new_gid != invalid_gid); 358 return agas::incref(new_gid, new_credit) 359 .then( 360 hpx::launch::sync, 361 hpx::util::bind(postprocess_incref, std::ref(gid)) 362 ); 363 } 364 365 HPX_ASSERT(src_log2credits > 1); 366 367 naming::gid_type new_gid = split_credits_for_gid_locked(l, gid); 368 369 HPX_ASSERT(detail::has_credits(gid)); 370 HPX_ASSERT(detail::has_credits(new_gid)); 371 372 return hpx::make_ready_future(new_gid); 373 } 374 375 naming::gid_type new_gid = gid; // strips lock-bit 376 return hpx::make_ready_future(new_gid); 377 } 378 379 /////////////////////////////////////////////////////////////////////// move_gid(gid_type & gid)380 gid_type move_gid(gid_type& gid) 381 { 382 std::unique_lock<gid_type::mutex_type> l(gid.get_mutex()); 383 return move_gid_locked(std::move(l), gid); 384 } 385 move_gid_locked(std::unique_lock<gid_type::mutex_type> l,gid_type & gid)386 gid_type move_gid_locked( 387 std::unique_lock<gid_type::mutex_type> l, gid_type& gid) //-V813 388 { 389 HPX_ASSERT_OWNS_LOCK(l); 390 391 naming::gid_type new_gid = gid; // strips lock-bit 392 393 if (naming::detail::has_credits(gid)) 394 { 395 naming::detail::strip_credits_from_gid(gid); 396 } 397 398 return new_gid; 399 } 400 401 /////////////////////////////////////////////////////////////////////// split_credits_for_gid(gid_type & id)402 gid_type split_credits_for_gid(gid_type& id) 403 { 404 typedef std::unique_lock<gid_type::mutex_type> scoped_lock; 405 scoped_lock l(id.get_mutex()); 406 return split_credits_for_gid_locked(l, id); 407 } 408 split_credits_for_gid_locked(std::unique_lock<gid_type::mutex_type> & l,gid_type & id)409 gid_type split_credits_for_gid_locked( 410 std::unique_lock<gid_type::mutex_type>& l, gid_type& id) 411 { 412 HPX_ASSERT_OWNS_LOCK(l); 413 414 std::uint16_t log2credits = get_log2credit_from_gid(id); 415 HPX_ASSERT(log2credits > 0); 416 417 gid_type newid = id; // strips lock-bit 418 419 set_log2credit_for_gid(id, log2credits-1); 420 set_credit_split_mask_for_gid(id); 421 422 set_log2credit_for_gid(newid, log2credits-1); 423 set_credit_split_mask_for_gid(newid); 424 425 return newid; 426 } 427 428 /////////////////////////////////////////////////////////////////////// replenish_credits(gid_type & gid)429 std::int64_t replenish_credits(gid_type& gid) 430 { 431 typedef std::unique_lock<gid_type::mutex_type> scoped_lock; 432 scoped_lock l(gid); 433 return replenish_credits_locked(l, gid); 434 } 435 replenish_credits_locked(std::unique_lock<gid_type::mutex_type> & l,gid_type & gid)436 HPX_EXPORT std::int64_t replenish_credits_locked( 437 std::unique_lock<gid_type::mutex_type>& l, gid_type& gid) 438 { 439 std::int64_t added_credit = 0; 440 441 HPX_ASSERT(0 == get_credit_from_gid(gid)); 442 443 added_credit = naming::detail::fill_credit_for_gid(gid); 444 naming::detail::set_credit_split_mask_for_gid(gid); 445 446 gid_type unlocked_gid = gid; // strips lock-bit 447 448 std::int64_t result = 0; 449 { 450 hpx::util::unlock_guard<std::unique_lock<gid_type::mutex_type>> 451 ul(l); 452 result = agas::incref(launch::sync, unlocked_gid, added_credit); 453 } 454 455 return result; 456 } 457 add_credit_to_gid(gid_type & id,std::int64_t credits)458 std::int64_t add_credit_to_gid(gid_type& id, std::int64_t credits) 459 { 460 std::int64_t c = get_credit_from_gid(id); 461 462 c += credits; 463 set_credit_for_gid(id, c); 464 465 return c; 466 } 467 remove_credit_from_gid(gid_type & id,std::int64_t debit)468 std::int64_t remove_credit_from_gid(gid_type& id, std::int64_t debit) 469 { 470 std::int64_t c = get_credit_from_gid(id); 471 HPX_ASSERT(c > debit); 472 473 c -= debit; 474 set_credit_for_gid(id, c); 475 476 return c; 477 } 478 fill_credit_for_gid(gid_type & id,std::int64_t credits)479 std::int64_t fill_credit_for_gid(gid_type& id, 480 std::int64_t credits) 481 { 482 std::int64_t c = get_credit_from_gid(id); 483 HPX_ASSERT(c <= credits); 484 485 std::int64_t added = credits - c; 486 set_credit_for_gid(id, credits); 487 488 return added; 489 } 490 491 /////////////////////////////////////////////////////////////////////// 492 struct gid_serialization_data 493 { 494 gid_type gid_; 495 detail::id_type_management type_; 496 497 template <typename Archive> serializehpx::naming::detail::gid_serialization_data498 void serialize(Archive& ar, unsigned) 499 { 500 ar & gid_ & type_; 501 } 502 }; 503 504 // serialization save(serialization::output_archive & ar,unsigned) const505 void id_type_impl::save(serialization::output_archive& ar, unsigned) const 506 { 507 // Avoid performing side effects if the archive is not saving the 508 // data. 509 if(ar.is_preprocessing()) 510 { 511 preprocess_gid(ar); 512 gid_serialization_data data { *this, type_ }; 513 ar << data; 514 return; 515 } 516 517 id_type_management type = type_; 518 519 gid_type new_gid; 520 if (unmanaged == type_) 521 { 522 new_gid = *this; 523 } 524 else if(managed_move_credit == type_) 525 { 526 // all credits will be moved to the returned gid 527 new_gid = move_gid(const_cast<id_type_impl&>(*this)); 528 type = managed; 529 } 530 else 531 { 532 new_gid = ar.get_new_gid(*this); 533 HPX_ASSERT(new_gid != invalid_gid); 534 } 535 536 gid_serialization_data data { new_gid, type }; 537 ar << data; 538 } 539 load(serialization::input_archive & ar,unsigned)540 void id_type_impl::load(serialization::input_archive& ar, unsigned) 541 { 542 gid_serialization_data data; 543 ar >> data; 544 545 static_cast<gid_type&>(*this) = data.gid_; 546 type_ = static_cast<id_type_management>(data.type_); 547 548 if (detail::unmanaged != type_ && detail::managed != type_) 549 { 550 HPX_THROW_EXCEPTION(version_too_new, "id_type::load", 551 "trying to load id_type with unknown deleter"); 552 } 553 } 554 555 /// support functions for std::intrusive_ptr intrusive_ptr_add_ref(id_type_impl * p)556 void intrusive_ptr_add_ref(id_type_impl* p) 557 { 558 ++p->count_; 559 } 560 intrusive_ptr_release(id_type_impl * p)561 void intrusive_ptr_release(id_type_impl* p) 562 { 563 if (0 == --p->count_) 564 id_type_impl::get_deleter(p->get_management_type())(p); 565 } 566 } // detail 567 568 /////////////////////////////////////////////////////////////////////////// operator +(gid_type const & lhs,gid_type const & rhs)569 gid_type operator+ (gid_type const& lhs, gid_type const& rhs) 570 { 571 std::uint64_t lsb = lhs.id_lsb_ + rhs.id_lsb_; 572 std::uint64_t msb = lhs.id_msb_ + rhs.id_msb_; 573 574 #if defined(HPX_DEBUG) 575 // make sure we're using the operator+ in proper contexts only 576 std::uint64_t lhs_internal_bits = 577 detail::get_internal_bits(lhs.id_msb_); 578 579 std::uint64_t msb_test = 580 detail::strip_internal_bits_and_component_type_from_gid(lhs.id_msb_) + 581 detail::strip_internal_bits_and_locality_from_gid(rhs.id_msb_); 582 583 HPX_ASSERT(msb == (msb_test | lhs_internal_bits)); 584 #endif 585 586 if (lsb < lhs.id_lsb_ || lsb < rhs.id_lsb_) 587 ++msb; 588 589 return gid_type(msb, lsb); 590 } 591 operator -(gid_type const & lhs,gid_type const & rhs)592 gid_type operator- (gid_type const& lhs, gid_type const& rhs) 593 { 594 std::uint64_t lsb = lhs.id_lsb_ - rhs.id_lsb_; 595 std::uint64_t msb = lhs.id_msb_ - rhs.id_msb_; 596 597 // #if defined(HPX_DEBUG) 598 // // make sure we're using the operator- in proper contexts only 599 // std::uint64_t lhs_internal_bits = detail::get_internal_bits(lhs.id_msb_); 600 // 601 // std::uint64_t msb_test = 602 // detail::strip_internal_bits_and_locality_from_gid(lhs.id_msb_) - 603 // detail::strip_internal_bits_and_locality_from_gid(rhs.id_msb_); 604 // 605 // std::uint32_t lhs_locality_id = 606 // naming::get_locality_id_from_gid(lhs.id_msb_); 607 // std::uint32_t rhs_locality_id = 608 // naming::get_locality_id_from_gid(rhs.id_msb_); 609 // if (rhs_locality_id != naming::invalid_locality_id) 610 // { 611 // HPX_ASSERT(lhs_locality_id == rhs_locality_id); 612 // HPX_ASSERT(msb == naming::replace_locality_id( 613 // msb_test | lhs_internal_bits, naming::invalid_locality_id)); 614 // } 615 // else 616 // { 617 // HPX_ASSERT(msb == naming::replace_locality_id( 618 // msb_test | lhs_internal_bits, lhs_locality_id)); 619 // } 620 // #endif 621 622 if (lsb > lhs.id_lsb_) 623 --msb; 624 625 return gid_type(msb, lsb); 626 } 627 to_string() const628 std::string gid_type::to_string() const 629 { 630 std::ostringstream out; 631 out << std::hex 632 << std::right << std::setfill('0') << std::setw(16) << id_msb_ 633 << std::right << std::setfill('0') << std::setw(16) << id_lsb_; 634 return out.str(); 635 } 636 operator <<(std::ostream & os,gid_type const & id)637 std::ostream& operator<<(std::ostream& os, gid_type const& id) 638 { 639 boost::io::ios_flags_saver ifs(os); 640 if (id != naming::invalid_gid) 641 { 642 os << std::hex 643 << "{" << std::right << std::setfill('0') << std::setw(16) 644 << id.id_msb_ << ", " 645 << std::right << std::setfill('0') << std::setw(16) 646 << id.id_lsb_ << "}"; 647 } 648 else 649 { 650 os << "{invalid}"; 651 } 652 return os; 653 } 654 operator <<(std::ostream & os,id_type const & id)655 std::ostream& operator<<(std::ostream& os, id_type const& id) 656 { 657 if (!id) 658 { 659 os << "{invalid}"; 660 } 661 else 662 { 663 os << id.get_gid(); 664 } 665 return os; 666 } 667 668 /////////////////////////////////////////////////////////////////////////// save(serialization::output_archive & ar,const unsigned int version) const669 void gid_type::save( 670 serialization::output_archive& ar 671 , const unsigned int version) const 672 { 673 ar << id_msb_ << id_lsb_; 674 } 675 load(serialization::input_archive & ar,const unsigned int)676 void gid_type::load( 677 serialization::input_archive& ar 678 , const unsigned int /*version*/) 679 { 680 ar >> id_msb_ >> id_lsb_; 681 682 id_msb_ &= ~is_locked_mask; // strip lock-bit upon receive 683 } 684 685 /////////////////////////////////////////////////////////////////////////// save(serialization::output_archive & ar,const unsigned int version) const686 void id_type::save( 687 serialization::output_archive& ar, 688 const unsigned int version) const 689 { 690 // We serialize the intrusive ptr and use pointer tracking here. This 691 // avoids multiple credit splitting if we need multiple future await 692 // passes (they all work on the same archive). 693 ar << gid_; 694 } 695 load(serialization::input_archive & ar,const unsigned int version)696 void id_type::load( 697 serialization::input_archive& ar, 698 const unsigned int version) 699 { 700 ar >> gid_; 701 } 702 703 /////////////////////////////////////////////////////////////////////////// 704 char const* const management_type_names[] = 705 { 706 "unknown_deleter", // -1 707 "unmanaged", // 0 708 "managed", // 1 709 "managed_move_credit" // 2 710 }; 711 get_management_type_name(id_type::management_type m)712 char const* get_management_type_name(id_type::management_type m) 713 { 714 if (m < id_type::unknown_deleter || m > id_type::managed_move_credit) 715 return "invalid"; 716 return management_type_names[m + 1]; 717 } 718 }} 719 720 namespace hpx 721 { get_colocation_id(launch::sync_policy,naming::id_type const & id,error_code & ec)722 naming::id_type get_colocation_id(launch::sync_policy, 723 naming::id_type const& id, error_code& ec) 724 { 725 return agas::get_colocation_id(launch::sync, id, ec); 726 } 727 get_colocation_id(naming::id_type const & id)728 lcos::future<naming::id_type> get_colocation_id(naming::id_type const& id) 729 { 730 return agas::get_colocation_id(id); 731 } 732 } 733 734