std::call_once() in Chromium?

130 views
Skip to first unread message

Benoit Lize

unread,
Jan 8, 2020, 8:05:17 AM1/8/20
to c...@chromium.org, Egor Pasko
Hi all,

std::call_once() seems like something that would be useful quite broadly in Chrome. It would allow us to get rid of some LazyInstance instances, as well as some weird-ish local static variables, while having a very clear meaning (and also looking familiar to those used to pthread_once()).

V8 already has a similar function, but not Chrome's base/, and "#include <mutex>" is banned in Chrome.

I understand why the STL's mutexes are banned, but std::call_once() uses atomics under the hood, and we do use std::atomic.

What do people think about allowing std::call_once()?

Thanks,

Benoit

Nico Weber

unread,
Jan 8, 2020, 8:19:28 AM1/8/20
to Benoit Lize, cxx, Egor Pasko
What does this do that a static local doesn't?

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAHPTcp-WiTW0S%2B3W1ZrE102hZQQ6%3DyebQS73BNcFLSDuXzk4tw%40mail.gmail.com.

Benoit Lize

unread,
Jan 8, 2020, 8:32:25 AM1/8/20
to Nico Weber, cxx, Egor Pasko
This came up as I was writing something like:

static bool initialized = InitializeOnce();
// Need to mark initialized as "used" somehow otherwise we have a warning.

This feels more convoluted (and error-prone) than writing:

std::call_once(InitializeOnce);

as you need to:
- mark the "useless" boolean as "used"
- make InitializeOnce() artificially return a bool rather than void
- convince yourself that the compiler will never remove the "dead" store.

Do you think making this simpler is worth it?

Thanks,

Benoit


Boris Sazonov

unread,
Jan 8, 2020, 8:45:06 AM1/8/20
to Benoit Lize, Nico Weber, cxx, Egor Pasko
As far as I know, with std::call_once you'll have to write:

std::once_flag g_initialize_flag;
// ...
std::call_once(g_initialize_flag, InitializeOnce);

FWIW, I still prefer std::call_once to a static local, as it seems clearer in two aspects:
  1. It is clearly thread safe.
  2. It can clearly block other threads if InitializeOnce blocks.

Regards,
Boris

Karl Wiberg

unread,
Jan 8, 2020, 8:54:05 AM1/8/20
to Boris Sazonov, Benoit Lize, Nico Weber, cxx, Egor Pasko
The syntactic noise from using a static local is quite manageable:

    static auto xx = (InitializeOnce(), 0);
    static_cast<void>(xx);


std::call_once looks slightly nicer at the point of call, but having to declare a std::once_flag somewhere else is a large readability drawback.

Gabriel Charette

unread,
Jan 10, 2020, 5:23:28 PM1/10/20
to Karl Wiberg, Boris Sazonov, Benoit Lize, Nico Weber, cxx, Egor Pasko
I've been thinking about an alternative to LazyInstance and NoDestructor for a while now that would be both single-line init (like LazyInstance) and non-destroyed in prod (like NoDestructor) but also provide constexpr-init for types that support it and be resettable for tests (we've seen numerous issues with singletons that leak state from one test to the next -- test retries often hide this). Should write it up into a doc... but here it goes in short:

LAZY_INIT_GLOBAL(var_type, var_name)
CONST_INIT_GLOBAL( var_type, var_name )

CONST_INIT_GLOBAL is equivalent to LAZY_INIT_GLOBAL by default but when compiling under Clang it can use [[clang::require_constant_initialization]] as an optimization.

and  LAZY_INIT_GLOBAL is:

#define LAZY_INIT_GLOBAL(var_type, var_name)      \
  struct {                                        \
    var_type& operator*() {                       \
      static RegisteredGlobal<var_type> instance; \
      return *instance;                           \
    }                                             \
  } var_name


Then *var_name is all that's needed to access the global; could also expose operator->() for consistency.

RegisteredGlobal<var_type> works like the lazy_task_runner.h logic; it's registered into a void in prod. But in tests we keep track of instances and recycle them between tests.

Reply all
Reply to author
Forward
0 new messages