dimanche 21 mai 2023

Extern global variable usage in Standard Library

  1. Global Variables are usually discouraged. What is the rational behind Global Variables with external linkage in C++ Standard Library?

  2. Is it true that extern variable is only declaration but not definition?

An example is the gcc implementation of std::call_once in mutex.h Thread local Global Variables with external linkage are declared:

  extern __thread void* __once_callable;
  extern __thread void (*__once_call)();

in

  /// @cond undocumented
# ifdef _GLIBCXX_HAVE_TLS
  // If TLS is available use thread-local state for the type-erased callable
  // that is being run by std::call_once in the current thread.
  extern __thread void* __once_callable;
  extern __thread void (*__once_call)();

  // RAII type to set up state for pthread_once call.
  struct once_flag::_Prepare_execution
  {
    template<typename _Callable>
      explicit
      _Prepare_execution(_Callable& __c)
      {
    // Store address in thread-local pointer:
    __once_callable = std::__addressof(__c);
    // Trampoline function to invoke the closure via thread-local pointer:
    __once_call = [] { (*static_cast<_Callable*>(__once_callable))(); };
      }

    ~_Prepare_execution()
    {
      // PR libstdc++/82481
      __once_callable = nullptr;
      __once_call = nullptr;
    }

    _Prepare_execution(const _Prepare_execution&) = delete;
    _Prepare_execution& operator=(const _Prepare_execution&) = delete;
  };
# else
  // Without TLS use a global std::mutex and store the callable in a
  // global std::function.
  extern function<void()> __once_functor;

  extern void
  __set_once_functor_lock_ptr(unique_lock<mutex>*);

  extern mutex&
  __get_once_mutex();

  // RAII type to set up state for pthread_once call.
  struct once_flag::_Prepare_execution
  {
    template<typename _Callable>
      explicit
      _Prepare_execution(_Callable& __c)
      {
    // Store the callable in the global std::function
    __once_functor = __c;
    __set_once_functor_lock_ptr(&_M_functor_lock);
      }

    ~_Prepare_execution()
    {
      if (_M_functor_lock)
    __set_once_functor_lock_ptr(nullptr);
    }

  private:
    // XXX This deadlocks if used recursively (PR 97949)
    unique_lock<mutex> _M_functor_lock{__get_once_mutex()};

    _Prepare_execution(const _Prepare_execution&) = delete;
    _Prepare_execution& operator=(const _Prepare_execution&) = delete;
  };
# endif
  1. If __once_callable & __once_call are declared but not defined, where are they defined? I didn't find definitions of them in Standard Library header files in devtoolset-11 without extern. Are they defined in source files?

Aucun commentaire:

Enregistrer un commentaire