Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix null function check for move_only_function assigned from a DLL and issues caused by an unloaded DLL that constructed or assigned an empty move_only_function #3038

Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 32 additions & 15 deletions stl/inc/functional
Original file line number Diff line number Diff line change
Expand Up @@ -1383,20 +1383,19 @@ public:
// (The RTTI savings may be significant as with lambdas and binds there may be many distinct callable types.
// Here we don't have a distinct wrapper class for each callable type, only distinct functions when needed.)

// _Move and _Destroy are nullptr if trivial. Besides optimization, this enables assigning emtpy function from
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
// a DLL that is unloaded later, and then safely moving/destroying that empty function

// Calls target
typename _Invoke_t<_Noexcept>::_Call _Invoke;
// Moves the data, including pointer to "vtable", AND destroys old data (not resetting its "vtable")
// nullptr if trivially move two pointers
void(__stdcall* _Move)(_Move_only_function_data&, _Move_only_function_data&) _NOEXCEPT_FNPTR;
// Destroys data (not resetting its "vtable")
// nullptr if no op destroy
void(__stdcall* _Destroy)(_Move_only_function_data&) _NOEXCEPT_FNPTR;
};

static constexpr _Impl_t _Null_move_only_function = {
_Function_not_callable<_Rx, _Types...>,
nullptr,
nullptr,
};

_Move_only_function_data _Data;

_Move_only_function_base() noexcept = default; // leaves fields uninitialized
Expand All @@ -1407,12 +1406,12 @@ public:
}

void _Construct_with_null() noexcept {
_Data._Impl = &_Null_move_only_function;
_Data._Impl = nullptr;
_Data._Set_large_fn_ptr(nullptr); // initialize, since we'll be copying it
}

void _Reset_to_null() noexcept {
_Data._Impl = &_Null_move_only_function;
_Data._Impl = nullptr;
}

template <class _Vt, class _VtInvQuals, class... _CTypes>
Expand All @@ -1426,14 +1425,14 @@ public:
}

static void _Checked_destroy(_Move_only_function_data& _Data) noexcept {
const auto _Impl = static_cast<const _Impl_t*>(_Data._Impl);
const auto _Impl = _Get_impl(_Data);
if (_Impl->_Destroy) {
_Impl->_Destroy(_Data);
}
}

static void _Checked_move(_Move_only_function_data& _Data, _Move_only_function_data& _Src) noexcept {
const auto _Impl = static_cast<const _Impl_t*>(_Src._Impl);
const auto _Impl = _Get_impl(_Src);
if (_Impl->_Move) {
_Impl->_Move(_Data, _Src);
} else {
Expand All @@ -1447,8 +1446,9 @@ public:
// It is more efficient to do the reverse - this way no temporary storage for the old target will be used.
// In some cases when some operations are trivial, it can be optimized,
// as the order change is unobservable, and everything is noexcept here.
const auto _Other_impl_move = static_cast<const _Impl_t*>(_Other._Data._Impl)->_Move;
const auto _This_impl_destroy = static_cast<const _Impl_t*>(_Data._Impl)->_Destroy;
const auto _This_impl = _Get_impl(_Data);
const auto _Other_impl_move = _Get_impl(_Other._Data)->_Move;
const auto _This_impl_destroy = _This_impl->_Destroy;

if (!_Other_impl_move) {
// Move is trivial, destroy first if needed
Expand All @@ -1462,7 +1462,13 @@ public:
} else {
// General case involving a temporary
_Move_only_function_data _Tmp;
_Checked_move(_Tmp, _Data);

if (_This_impl->_Move) {
_This_impl->_Move(_Tmp, _Data);
} else {
_Function_move_large(_Tmp, _Data);
}

_Other_impl_move(_Data, _Other._Data);
_This_impl_destroy(_Tmp);
}
Expand All @@ -1478,7 +1484,7 @@ public:
}

_NODISCARD bool _Is_null() const noexcept {
return _Data._Impl == &_Null_move_only_function;
return _Data._Impl == nullptr;
}

template <class _Vt>
Expand All @@ -1487,7 +1493,18 @@ public:
|| sizeof(_Vt) > _Move_only_function_data::_Buf_size<_Vt> || !is_nothrow_move_constructible_v<_Vt>;

_NODISCARD auto _Get_invoke() const noexcept {
return static_cast<const _Impl_t*>(_Data._Impl)->_Invoke;
return _Get_impl(_Data)->_Invoke;
}

_NODISCARD static const _Impl_t* _Get_impl(const _Move_only_function_data& _Data) noexcept {
static constexpr _Impl_t _Null_move_only_function = {
_Function_not_callable<_Rx, _Types...>,
nullptr,
nullptr,
};

const _Impl_t* const _Ret = static_cast<const _Impl_t*>(_Data._Impl);
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
return _Ret ? _Ret : &_Null_move_only_function;
}

template <class _Vt, class _VtInvQuals>
Expand Down