如何实现条件_variable?

发布于 2025-01-26 23:52:49 字数 8067 浏览 2 评论 0 原文

标题问题的较长版本是:

在我的计算机上, sizeof(std :: condition_variable)是72个字节。 这些72个字节是用什么?

注意: std :: condition_variable 的大小取决于实现。

附录 notify_one 和成员对象。我将从等待开始。 等待带有谓词。

    template <class _Predicate>
    void wait(unique_lock<mutex>& _Lck, _Predicate _Pred) { // wait for signal and test predicate
        while (!_Pred()) {
            wait(_Lck);
        }
    }

上面的等待调用no-prodicate 等待

    void wait(unique_lock<mutex>& _Lck) { // wait for signal
        // Nothing to do to comply with LWG-2135 because std::mutex lock/unlock are nothrow
        _Cnd_wait(_Mycnd(), _Lck.mutex()->_Mymtx());
    }

此等待调用 _CND_WAIT on _MYCND() _CND_WAIT 找到

int _Cnd_wait(const _Cnd_t cond, const _Mtx_t mtx) { // wait until signaled
    const auto cs = static_cast<Concurrency::details::stl_critical_section_interface*>(_Mtx_getconcrtcs(mtx));
    _Mtx_clear_owner(mtx);
    cond->_get_cv()->wait(cs);
    _Mtx_reset_owner(mtx);
    return _Thrd_success; // TRANSITION, ABI: Always returns _Thrd_success
}

_CND_T 是指向 _CND_INTERNAL_IMP_T_T 的指针。

using _Cnd_t = struct _Cnd_internal_imp_t*;

struct _cnd_internal_imp_t 是定义的

struct _Cnd_internal_imp_t { // condition variable implementation for ConcRT
    std::aligned_storage_t<Concurrency::details::stl_condition_variable_max_size,
        Concurrency::details::stl_condition_variable_max_alignment>
        cv;

    [[nodiscard]] Concurrency::details::stl_condition_variable_interface* _get_cv() noexcept {
        // get pointer to implementation
        return reinterpret_cast<Concurrency::details::stl_condition_variable_interface*>(&cv);
    }
};

我现在正在查看行 cond-&gt; _get_cv() - &gt; wait(cs); 。要理解这一行,我需要查看并发::详细信息:: stl_condition_variable_interface 的成员 wait 函数。这是一个

        class __declspec(novtable) stl_condition_variable_interface {
        public:
            virtual void wait(stl_critical_section_interface*)                   = 0;
            virtual bool wait_for(stl_critical_section_interface*, unsigned int) = 0;
            virtual void notify_one()                                            = 0;
            virtual void notify_all()                                            = 0;
            virtual void destroy()                                               = 0;
        };

编辑2

cond-&gt; _get_cv()是抽象类 stl_condition_variable_interface 的指针。在施工期间的某个时候, 将被调用以设置虚拟指针。 The virtual pointer for this object will point to the vtable for either stl_condition_variable_vista given

就我而言,虚拟指针指向 stl_condition_variable_win7 的表。

        class stl_condition_variable_win7 final : public stl_condition_variable_interface {
        public:
            stl_condition_variable_win7() {
                InitializeConditionVariable(&m_condition_variable);
            }

            ~stl_condition_variable_win7()                                  = delete;
            stl_condition_variable_win7(const stl_condition_variable_win7&) = delete;
            stl_condition_variable_win7& operator=(const stl_condition_variable_win7&) = delete;

            void destroy() override {}

            void wait(stl_critical_section_interface* lock) override {
                if (!stl_condition_variable_win7::wait_for(lock, INFINITE)) {
                    std::terminate();
                }
            }

            bool wait_for(stl_critical_section_interface* lock, unsigned int timeout) override {
                return SleepConditionVariableSRW(&m_condition_variable,
                           static_cast<stl_critical_section_win7*>(lock)->native_handle(), timeout, 0)
                    != 0;
            }

            void notify_one() override {
                WakeConditionVariable(&m_condition_variable);
            }

            void notify_all() override {
                WakeAllConditionVariable(&m_condition_variable);
            }

        private:
            CONDITION_VARIABLE m_condition_variable;
        };

因此,我的72或8个字节保留用于存储 condition_variable ,而的精华等待是调用 SleepConditionVariablesrw 。描述了此功能

结束编辑2

附录a

std :: condition_variable is

aligned_storage_t<_Cnd_internal_imp_size, _Cnd_internal_imp_alignment> _Cnd_storage;

std :: procention_variable 包含以下成员函数,该功能允许 _cnd_storage 要解释为 _CND_T

    _Cnd_t _Mycnd() noexcept { // get pointer to _Cnd_internal_imp_t inside _Cnd_storage
        return reinterpret_cast<_Cnd_t>(&_Cnd_storage);
    }

sizeof(std :: condition_variable) sizeof(_cnd_storage)给出,该在 xthreads.h 中定义。

// Size and alignment for _Mtx_internal_imp_t and _Cnd_internal_imp_t
#ifdef _CRT_WINDOWS
#ifdef _WIN64
_INLINE_VAR constexpr size_t _Mtx_internal_imp_size      = 32;
_INLINE_VAR constexpr size_t _Mtx_internal_imp_alignment = 8;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size      = 16;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_alignment = 8;
#else // _WIN64
_INLINE_VAR constexpr size_t _Mtx_internal_imp_size      = 20;
_INLINE_VAR constexpr size_t _Mtx_internal_imp_alignment = 4;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size      = 8;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_alignment = 4;
#endif // _WIN64
#else // _CRT_WINDOWS
#ifdef _WIN64
_INLINE_VAR constexpr size_t _Mtx_internal_imp_size      = 80;
_INLINE_VAR constexpr size_t _Mtx_internal_imp_alignment = 8;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size      = 72;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_alignment = 8;
#else // _WIN64
_INLINE_VAR constexpr size_t _Mtx_internal_imp_size      = 48;
_INLINE_VAR constexpr size_t _Mtx_internal_imp_alignment = 4;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size      = 40;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_alignment = 4;
#endif // _WIN64
#endif // _CRT_WINDOWS

编辑1/附录B

在发布问题后,我对此进行了考虑,我不确定如何使其与其他问题一起流动。 std :: condition_variable 的唯一成员是

aligned_storage_t<_Cnd_internal_imp_size, _Cnd_internal_imp_alignment> _Cnd_storage;

将其解释为 _cnd_internal_imp_t _cnd_internal_imp_t 的唯一成员是

std::aligned_storage_t<Concurrency::details::stl_condition_variable_max_size, Concurrency::details::stl_condition_variable_max_alignment> cv;

stl_condition_variable_max_size!= _cnd_ind_internal_imp_size 。实际上,这在此

static_assert(sizeof(_Cnd_internal_imp_t) <= _Cnd_internal_imp_size, "incorrect _Cnd_internal_imp_size");

这意味着72个字节中的一些可能是“未使用的”。

结束编辑1

问题:

  1. std :: procention_variable 保留72个字节,以 condition> procention_variable (请参阅编辑2)。这些72个字节是用什么?
  2. std :: condition_variable 如何使用更少的字节逃脱?似乎在某些计算机上 std :: procenty_variable s只有8个字节大。看:
      _inline_var constexpr size_t _cnd_internal_imp_size = 8;
     

A longer version of the title question would be:

On my machine, sizeof(std::condition_variable) is 72 bytes.
What are these 72 bytes used for?

Note: The size of std::condition_variable depends on the implementation. Some examples sizes are given in Appendix A.

To understand how std::condition_variable works, I am satisfied to understand wait, notify_one, and member objects. I will start with wait. wait with a predicate is given below.

    template <class _Predicate>
    void wait(unique_lock<mutex>& _Lck, _Predicate _Pred) { // wait for signal and test predicate
        while (!_Pred()) {
            wait(_Lck);
        }
    }

The above wait calls the no-predicate wait.

    void wait(unique_lock<mutex>& _Lck) { // wait for signal
        // Nothing to do to comply with LWG-2135 because std::mutex lock/unlock are nothrow
        _Cnd_wait(_Mycnd(), _Lck.mutex()->_Mymtx());
    }

This wait calls _Cnd_wait on _Mycnd(). _Cnd_wait is found here.

int _Cnd_wait(const _Cnd_t cond, const _Mtx_t mtx) { // wait until signaled
    const auto cs = static_cast<Concurrency::details::stl_critical_section_interface*>(_Mtx_getconcrtcs(mtx));
    _Mtx_clear_owner(mtx);
    cond->_get_cv()->wait(cs);
    _Mtx_reset_owner(mtx);
    return _Thrd_success; // TRANSITION, ABI: Always returns _Thrd_success
}

_Cnd_t is a pointer to a _Cnd_internal_imp_t .

using _Cnd_t = struct _Cnd_internal_imp_t*;

The struct _Cnd_internal_imp_t is defined here.

struct _Cnd_internal_imp_t { // condition variable implementation for ConcRT
    std::aligned_storage_t<Concurrency::details::stl_condition_variable_max_size,
        Concurrency::details::stl_condition_variable_max_alignment>
        cv;

    [[nodiscard]] Concurrency::details::stl_condition_variable_interface* _get_cv() noexcept {
        // get pointer to implementation
        return reinterpret_cast<Concurrency::details::stl_condition_variable_interface*>(&cv);
    }
};

I am now looking at the line cond->_get_cv()->wait(cs);. To understand this line, I need to see Concurrency::details::stl_condition_variable_interface's member wait function. This is a virtual function.

        class __declspec(novtable) stl_condition_variable_interface {
        public:
            virtual void wait(stl_critical_section_interface*)                   = 0;
            virtual bool wait_for(stl_critical_section_interface*, unsigned int) = 0;
            virtual void notify_one()                                            = 0;
            virtual void notify_all()                                            = 0;
            virtual void destroy()                                               = 0;
        };

Edit 2

cond->_get_cv() is a pointer to an abstract class stl_condition_variable_interface. At some point during construction, create_stl_condition_variable will be called to set the virtual pointer. The virtual pointer for this object will point to the vtable for either stl_condition_variable_vista given here or stl_condition_variable_win7 given here. The top answer to this stack overflow question explains some of the details.

In my case, the virtual pointer points to the table for stl_condition_variable_win7.

        class stl_condition_variable_win7 final : public stl_condition_variable_interface {
        public:
            stl_condition_variable_win7() {
                InitializeConditionVariable(&m_condition_variable);
            }

            ~stl_condition_variable_win7()                                  = delete;
            stl_condition_variable_win7(const stl_condition_variable_win7&) = delete;
            stl_condition_variable_win7& operator=(const stl_condition_variable_win7&) = delete;

            void destroy() override {}

            void wait(stl_critical_section_interface* lock) override {
                if (!stl_condition_variable_win7::wait_for(lock, INFINITE)) {
                    std::terminate();
                }
            }

            bool wait_for(stl_critical_section_interface* lock, unsigned int timeout) override {
                return SleepConditionVariableSRW(&m_condition_variable,
                           static_cast<stl_critical_section_win7*>(lock)->native_handle(), timeout, 0)
                    != 0;
            }

            void notify_one() override {
                WakeConditionVariable(&m_condition_variable);
            }

            void notify_all() override {
                WakeAllConditionVariable(&m_condition_variable);
            }

        private:
            CONDITION_VARIABLE m_condition_variable;
        };

So my 72 or 8 bytes are reserved to store a CONDITION_VARIABLE and the essense of wait is to call SleepConditionVariableSRW. This function is described here.

END EDIT 2

Appendix A

The only member object of std::condition_variable is

aligned_storage_t<_Cnd_internal_imp_size, _Cnd_internal_imp_alignment> _Cnd_storage;

std::condition_variable contains the below member function which allows _Cnd_storage to be interpreted as a _Cnd_t.

    _Cnd_t _Mycnd() noexcept { // get pointer to _Cnd_internal_imp_t inside _Cnd_storage
        return reinterpret_cast<_Cnd_t>(&_Cnd_storage);
    }

sizeof(std::condition_variable) is given by the sizeof(_Cnd_storage), which is defined in xthreads.h.

// Size and alignment for _Mtx_internal_imp_t and _Cnd_internal_imp_t
#ifdef _CRT_WINDOWS
#ifdef _WIN64
_INLINE_VAR constexpr size_t _Mtx_internal_imp_size      = 32;
_INLINE_VAR constexpr size_t _Mtx_internal_imp_alignment = 8;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size      = 16;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_alignment = 8;
#else // _WIN64
_INLINE_VAR constexpr size_t _Mtx_internal_imp_size      = 20;
_INLINE_VAR constexpr size_t _Mtx_internal_imp_alignment = 4;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size      = 8;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_alignment = 4;
#endif // _WIN64
#else // _CRT_WINDOWS
#ifdef _WIN64
_INLINE_VAR constexpr size_t _Mtx_internal_imp_size      = 80;
_INLINE_VAR constexpr size_t _Mtx_internal_imp_alignment = 8;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size      = 72;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_alignment = 8;
#else // _WIN64
_INLINE_VAR constexpr size_t _Mtx_internal_imp_size      = 48;
_INLINE_VAR constexpr size_t _Mtx_internal_imp_alignment = 4;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size      = 40;
_INLINE_VAR constexpr size_t _Cnd_internal_imp_alignment = 4;
#endif // _WIN64
#endif // _CRT_WINDOWS

Edit 1/Appendix B

I thought about this after posting the question, and I am not sure how to make it flow with the rest. std::condition_variable's only member is

aligned_storage_t<_Cnd_internal_imp_size, _Cnd_internal_imp_alignment> _Cnd_storage;

which is interpreted as _Cnd_internal_imp_t. _Cnd_internal_imp_t's only member is

std::aligned_storage_t<Concurrency::details::stl_condition_variable_max_size, Concurrency::details::stl_condition_variable_max_alignment> cv;

It is possible that stl_condition_variable_max_size != _Cnd_internal_imp_size. In fact, this implied in this line

static_assert(sizeof(_Cnd_internal_imp_t) <= _Cnd_internal_imp_size, "incorrect _Cnd_internal_imp_size");

This would mean that it is possible that some of the 72 bytes are "unused."

END EDIT 1

Questions:

  1. std::condition_variable reserves 72 bytes for a CONDITION_VARIABLE (see Edit 2). What are these 72 bytes used for?
  2. How could a std::condition_variable get away with fewer bytes? It appears as though on some machines std::condition_variables are only 8 bytes big. See:
    _INLINE_VAR constexpr size_t _Cnd_internal_imp_size      = 8;
    

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

彼岸花ソ最美的依靠 2025-02-02 23:52:49

std :: condition_variable 为条件_variable保留72个字节(请参阅编辑2)。这些72个字节是用什么?

条件变量的另一个实现是由并发运行时(Concrt)支持的。在Visual Studio 2012中,这是唯一的实现,但事实证明不是很好。

从VS 2015开始,由实际的条件_variable 提供更好的实现。有一个多态性来为不同的Windows版本创建不同的实现,因为 condition_variable 可以使用Windows Vista,并且A 完整 srwlock 从Windows 7. The polymorphism uses placement new rather than unions to hide the implementation details and to make the implementation conformant by

因此,有一个用于多个实现的地方,其中的具体是最大的。

否则, sizeof(condition_variable)== sizeof(void*)以及 sizeof(srwlock)== sizeof(void*),尽管它们不是内部指针。如果 code> condition_variable / srwlock 使用实现,则浪费了其余大小。

从Visual Studio 2019开始,Windows XP不再得到VS工具集的支持(由VS 2019支持安装VS 2017 Toolset的VS 2019支持)。因此,” norefloll noreferrer“> noreflow noreferrer”>我的pr 后续pr 删除了混合结构包装器。

从Visual Studio 2022开始,Windows Vista不再得到VS工具集的支持/a>删除 srwlock 多态性正在飞行。

仍然是由于VS 2015,VS 2017,VS 2019和VS 2022之间的ABI兼容性,因此不可能减少 condition> condition_variable 的大小。

mutex 构造函数中摆脱新的位置,并解决了具有 mutex sonstructor non- constexpr 的符合性问题(我的尝试失败)。

因此,vs 2019和vs 2022仍然必须为具体实施预留空间,这不再使用。

随着ABI的下一个ABI破坏版本的发布,很有可能实施条件_variable 将更改。


std :: pordition_variable如何以较少的字节逃脱?

_CRT_WINDOWS 实现不需要支持Windows XP,因此没有Concrt后备。仍然出于维护原因,它仍然与通常的配置共享实现。

std::condition_variable reserves 72 bytes for a CONDITION_VARIABLE (see Edit 2). What are these 72 bytes used for?

There was another implementation of condition variable that was backed by Concurrency Runtime (ConcRT). In Visual Studio 2012 it was the only implementation, but it turned out to be not very good.

Starting from VS 2015, there is better implementation backed by the actual CONDITION_VARIABLE. There is a polymorphism to create different implementations for different Windows versions, as CONDITION_VARIABLE is available starting Windows Vista, and a complete SRWLOCK is available starting in Windows 7. The polymorphism uses placement new rather than unions to hide the implementation details and to make the implementation conformant by making it a standard-layout class.

So, there is a place for multiple implementations, out of which the ConcRT is the largest.

Otherwise, sizeof(CONDITION_VARIABLE) == sizeof(void*), as well as sizeof(SRWLOCK) == sizeof(void*), though they aren't pointers internally. The rest of the size is wasted, if CONDITION_VARIABLE / SRWLOCK implementation is used.

Starting from Visual Studio 2019, Windows XP is no longer supported by the VS toolset (it is supported by VS 2019 by the ability to install VS 2017 toolset). So ConcRT dependency and the ability to create pre-Vista condition_variable was removed by my PR. A follow-up PR removed ConcRT structure wrappers.

Starting from Visual Studio 2022, Windows Vista is no longer supported by the VS toolset either, my other PR to remove the SRWLOCK polymorphism is in flight.

Still due to the ABI compatibility between VS 2015, VS 2017, VS 2019, and VS 2022, it is not possible to reduce the size of condition_variable.

Getting rid of placement new in mutex constructor and fixing the conformance issue with having mutex constructor non-constexpr is also hard (my attempt has failed).

So, VS 2019 and VS 2022 still have to reserve space for the ConcRT implementation, which is no longer used.

With the next ABI breaking release of Visual Studio it is highly likely that the implementation of condition_variable will change.


How could a std::condition_variable get away with fewer bytes?

_CRT_WINDOWS implementation never needed to support Windows XP, so does not have ConcRT fallback. Still it shares the implementation with the usual configuration, apparently for maintenance reasons.

幸福不弃 2025-02-02 23:52:49

这是一个不完整的答案,但确实提供了更多信息。

std :: condition_variable 调用一个函数的构造函数,该函数在 _cnd_storage

condition_variable() {
  _Cnd_init_in_situ(_Mycnd());
}

/code 定义时存储的数据有2个指针,或一个指针和一个指针大小的整数;第一个可能是虚拟函数指针(指向 stl_condition_variable_interface ),另一个是状态。

根据您使用的操作系统和库提供的内容,或多或少需要在条件变量实现中进行机械。

该实现可能是在您似乎无法访问的源代码中。

https://github.com/ojdkbuild/tools_toolchain_vs2017bt_1416/blob/blob/master/master/vc/tools/tools/mmsvc/14.16.27023/crt/crt/src/src/stl/stl/cond.c.到并发::详细信息:: create_stl_condition_variable(cond-&gt; _get_cv())

在这里:: _ procenty_variable 。但是,它似乎并不是那里创建的东西(它没有虚拟基础)。它有两个成员:

void * volatile _M_pWaitChain;
Concurrency::critical_section _M_lock;

可能与实际存储的成员相似(因为这是对类似事物的先前实现)。对于 std procention_variable ,关键部分可能是冗余的,因为它具有外部静音。

_M_PWAITCHAIN 中,我不能说。

所有这些都不完整。我确实知道,现代状况变量知道何时握住锁定时会发出信号,并在释放互惠符时与线程醒来的互动。即,操作系统调度物质内部的低水平。

This is an incomplete answer, but it does provide more information.

The constructor of std::condition_variable calls a function that creates the implementation of the condition variable within _Cnd_storage

condition_variable() {
  _Cnd_init_in_situ(_Mycnd());
}

when _CRT_WINDOWS is defined, it appears that the data stored there is 2 pointers, or one pointer and one integer the size of a pointer; the first of which is probably a virtual function pointer (pointing at the stl_condition_variable_interface), and the other one is the state.

Depending on what the OS and libraries you are using provide, more or less machinery needs to be in the condition variable implementation.

That implementation may be in source code you do not appear to have access to.

https://github.com/ojdkbuild/tools_toolchain_vs2017bt_1416/blob/master/VC/Tools/MSVC/14.16.27023/crt/src/stl/cond.c appears to be _Cnd_init_in_situ, which simply forwards to Concurrency::details::create_stl_condition_variable(cond->_get_cv()).

Here is a VS2013 Concurrency::detalis::_Condition_variable. It, however, does not appear to be what is created there (it has no virtual base). It has two members:

void * volatile _M_pWaitChain;
Concurrency::critical_section _M_lock;

which may be similar to what is actually stored there (as it was a previous implementation for something similar). The critical section is probably redundant for a std condition_variable, as it has an external mutex to work with.

What is in the _M_pWaitChain I cannot say, other than from its name.

All of this isn't complete. I do know that modern condition variables know when they are signaled if they are holding the lock, and interact with which thread wakes up when the mutex is released; ie, low level internal to OS scheduling stuff.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文