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