Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-47518

mitigate dtor-order fiasco with a utility for defining static duration immortal objects

    XMLWordPrintable

    Details

    • Type: Improvement
    • Status: Closed
    • Priority: Major - P3
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 4.4.1, 4.7.0
    • Component/s: Internal Code
    • Labels:
      None
    • Backwards Compatibility:
      Fully Compatible
    • Backport Requested:
      v4.4
    • Sprint:
      Service arch 2020-05-18

      Description

      A template with an object-modeling API similar to std::optional,
      with a value(), typedef value_type, operator* operator->.

      Init examples from the unit test:

      TEST(ImmortalTest, StaticDurationIdiom) {
          static auto&& x = Immortal<Indestructible>();
          static_assert(std::is_same_v<decltype(x), Immortal<Indestructible>&&>);
      }
       
      TEST(ImmortalTest, DeducedValueTypeCopyInit) {
          static const Immortal m = Map{{"hello", 123}, {"bye", 456}};
          ASSERT_EQ(m->find("bye")->second, 456);
      }
       
      TEST(ImmortalTest, DeducedValueTypeExpression) {
          static const Immortal m = [] { return Map{{"hello", 123}, {"bye", 456}}; }();
          ASSERT_EQ(m->find("bye")->second, 456);
      }
       
      TEST(ImmortalTest, BraceInit) {
          static const Immortal<Map> m{{{"hello", 123}, {"bye", 456}}};
          ASSERT_EQ(m->find("bye")->second, 456);
      }
       
      TEST(ImmortalTest, ListInit) {
          static const Immortal<Map> m = {{{"hello", 123}, {"bye", 456}}};
          ASSERT_EQ(m->find("bye")->second, 456);
      }
       
      TEST(ImmortalTest, EmptyBrace) {
          static const Immortal<Map> empty{};
          ASSERT_TRUE(empty->empty());
      }
      

      x is convertible to an Indestructible&, and can sometimes be passed directly.
      We can also make the cast without naming the type with *x and x.value().

      Immortal forwards (via placement new) its constructor arguments to T(args...). This performs a placement new onto a std::aligned_buffer_t, so there is no allocation as there would be with the traditional approach, and there is no destructor call.

      Having a common idiom and place to hang documentation will be useful in cutting down on the very common misuses and bugs we have regarding init order and shutdown order.

      Importantly, it becomes a "greppable" and auditable best practice to identify and contain such objects, which are often suspects in startup and shutdown problems, or need to be called out in refactorings to reduce shared global state.

        Attachments

          Activity

            People

            Assignee:
            billy.donahue Billy Donahue
            Reporter:
            billy.donahue Billy Donahue
            Participants:
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved: