christyjquinn
Topic Author
Posts: 17
Joined: 17 Aug 2022, 17:03

Commands and being stack allocated in view models

27 Mar 2023, 19:45

Hello,

In the commands tutorial there is a note about stack allocating commands:
In this sample DelegateCommand is not allocated in the heap to reduce allocations as explained in the C++ Architecture Guide. This is one of those scenarios where it is safe to do so and reference counting is not needed.
Why is it safe in this case and in what cases is it not safe?

Thanks!
 
christyjquinn
Topic Author
Posts: 17
Joined: 17 Aug 2022, 17:03

Re: Commands and being stack allocated in view models

27 Mar 2023, 20:24

A little more context for why I am asking this. I'm sometimes hitting a crash where a stack allocated command is decremented after being released. The command is stack allocated in a view model and bound to a button and game pad trigger as follows:
<b:Interaction.Triggers>
	<noesis:GamepadTrigger FiredOn="ButtonDown" Button="Cancel">
		<b:InvokeCommandAction Command="{Binding CloseCommand}" />
	</noesis:GamepadTrigger>
</b:Interaction.Triggers>
...
<Button Command="{Binding CloseCommand}">
...
And here is the stack trace:
#0 0x10034d763 in Noesis::AtomicInteger::FetchAndDecrement() Atomic.inl:57
#1 0x10034d6f6 in Noesis::AtomicInteger::operator--() Atomic.inl:123
#2 0x10034d620 in Noesis::BaseRefCounted::InternalRelease() const BaseRefCounted.inl:63
#3 0x10034ef76 in Noesis::BaseRefCounted::Release() const BaseRefCounted.inl:37
#4 0x105dd70ef in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:77
#5 0x105dd7044 in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:74
#6 0x100990fcc in Noesis::ValueStorageManagerImpl<Noesis::Ptr<Noesis::BaseComponent> >::Destroy(void**) const ValueStorageManagerImpl.inl:121
#7 0x10673d11f in Noesis::StoredValue::DestroyAllValues(Noesis::DependencyProperty const*)
#8 0x106737384 in Noesis::DependencyObject::~DependencyObject()
#9 0x103cf3509 in NoesisApp::AttachableObject::~AttachableObject() AttachableObject.cpp:32
#10 0x103e1ae06 in NoesisApp::TriggerAction::~TriggerAction() TriggerAction.Interactivity.cpp:24
#11 0x103ddd386 in NoesisApp::TargetedTriggerAction::~TargetedTriggerAction() TargetedTriggerAction.cpp:28
#12 0x103d84494 in NoesisApp::TargetedTriggerActionT<Noesis::DependencyObject>::~TargetedTriggerActionT() TargetedTriggerAction.h:88
#13 0x103d84657 in NoesisApp::InvokeCommandAction::~InvokeCommandAction() InvokeCommandAction.cpp:25
#14 0x103d84674 in NoesisApp::InvokeCommandAction::~InvokeCommandAction() InvokeCommandAction.cpp:24
#15 0x103d84718 in NoesisApp::InvokeCommandAction::~InvokeCommandAction() InvokeCommandAction.cpp:24
#16 0x106739212 in Noesis::DependencyObject::OnDestroy() const
#17 0x10675116c in Noesis::BaseFreezableCollection::~BaseFreezableCollection()
#18 0x103e1ef64 in Noesis::FreezableCollection<NoesisApp::TriggerAction>::~FreezableCollection() FreezableCollection.h:26
#19 0x103e1c82e in NoesisApp::AttachableCollection<NoesisApp::TriggerAction>::~AttachableCollection() AttachableCollection.inl:26
#20 0x103e1bf9b in NoesisApp::TriggerActionCollection::~TriggerActionCollection() TriggerActionCollection.cpp:29
#21 0x103e1c8f4 in NoesisApp::TriggerActionCollection::~TriggerActionCollection() TriggerActionCollection.cpp:20
#22 0x103e1c9f8 in NoesisApp::TriggerActionCollection::~TriggerActionCollection() TriggerActionCollection.cpp:20
#23 0x106739212 in Noesis::DependencyObject::OnDestroy() const
#24 0x10034d6aa in Noesis::BaseRefCounted::InternalRelease() const BaseRefCounted.inl:67
#25 0x10034ef76 in Noesis::BaseRefCounted::Release() const BaseRefCounted.inl:37
#26 0x105dd70ef in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:77
#27 0x105dd7044 in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:74
#28 0x100990fcc in Noesis::ValueStorageManagerImpl<Noesis::Ptr<Noesis::BaseComponent> >::Destroy(void**) const ValueStorageManagerImpl.inl:121
#29 0x106737384 in Noesis::DependencyObject::~DependencyObject()
#30 0x103cf3509 in NoesisApp::AttachableObject::~AttachableObject() AttachableObject.cpp:32
#31 0x103e22156 in NoesisApp::TriggerBase::~TriggerBase() TriggerBase.Interactivity.cpp:34
#32 0x103d6a454 in NoesisApp::TriggerBaseT<Noesis::UIElement>::~TriggerBaseT() TriggerBase.h:92
#33 0x103d6a436 in NoesisApp::GamepadTrigger::~GamepadTrigger() GamepadTrigger.cpp:28
#34 0x103d6a474 in NoesisApp::GamepadTrigger::~GamepadTrigger() GamepadTrigger.cpp:27
#35 0x103d6a518 in NoesisApp::GamepadTrigger::~GamepadTrigger() GamepadTrigger.cpp:27
#36 0x106739212 in Noesis::DependencyObject::OnDestroy() const
#37 0x10675116c in Noesis::BaseFreezableCollection::~BaseFreezableCollection()
#38 0x103dd6224 in Noesis::FreezableCollection<NoesisApp::TriggerBase>::~FreezableCollection() FreezableCollection.h:26
#39 0x103e2728e in NoesisApp::AttachableCollection<NoesisApp::TriggerBase>::~AttachableCollection() AttachableCollection.inl:26
#40 0x103e26c0b in NoesisApp::TriggerCollection::~TriggerCollection() TriggerCollection.cpp:29
#41 0x103e27354 in NoesisApp::TriggerCollection::~TriggerCollection() TriggerCollection.cpp:20
#42 0x103e27458 in NoesisApp::TriggerCollection::~TriggerCollection() TriggerCollection.cpp:20
#43 0x106739212 in Noesis::DependencyObject::OnDestroy() const
#44 0x10034d6aa in Noesis::BaseRefCounted::InternalRelease() const BaseRefCounted.inl:67
#45 0x10034ef76 in Noesis::BaseRefCounted::Release() const BaseRefCounted.inl:37
#46 0x105dd70ef in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:77
#47 0x105dd7044 in Noesis::Ptr<Noesis::BaseComponent>::~Ptr() Ptr.inl:74
#48 0x100990fcc in Noesis::ValueStorageManagerImpl<Noesis::Ptr<Noesis::BaseComponent> >::Destroy(void**) const ValueStorageManagerImpl.inl:121
#49 0x106737384 in Noesis::DependencyObject::~DependencyObject()
#50 0x1067c486c in Noesis::FrameworkElement::~FrameworkElement()
#51 0x100607b04 in Noesis::UserControl::~UserControl() UserControl.h:23
#52 0x105d4cce4 in MyView::~MyView() MyView.xaml.h:7
#53 0x105d4cbb4 in MyView::~MyView() MyView.xaml.h:7
#54 0x105d4cbd8 in MyView::~MyView() MyView.xaml.h:7
#55 0x106739212 in Noesis::DependencyObject::OnDestroy() const
#56 0x10034d6aa in Noesis::BaseRefCounted::InternalRelease() const BaseRefCounted.inl:67
#57 0x1005cc016 in Noesis::FrameworkElement::Release() const FrameworkElement.h:511
#58 0x1068fa475 in Noesis::UIElement::BubblingEvent(Noesis::RoutedEventArgs const&)
#59 0x10683d5bf in Noesis::Keyboard::KeyDown(Noesis::Key)
 
User avatar
sfernandez
Site Admin
Posts: 2866
Joined: 22 Dec 2011, 19:20

Re: Commands and being stack allocated in view models

28 Mar 2023, 11:19

Hi,
Why is it safe in this case and in what cases is it not safe?
In our Commands sample it is safe to use a stack allocated command because the lifetime of the ViewModel defining that command is longer than the Button using it.

In your example, the ViewModel set in the DataContext property is probably destroyed before the Triggers property is being released in the UserControl root, and that is why you are getting a crash. In situations where you can't make sure that the ViewModel will live longer than the references to the command, you have to use heap allocated commands.
 
User avatar
jsantos
Site Admin
Posts: 3747
Joined: 20 Jan 2012, 17:18
Contact:

Re: Commands and being stack allocated in view models

28 Mar 2023, 16:18

In profile configurations (NS_PROFILE enabled), BaseRefCounted has a check to detect problems with this:
inline BaseRefCounted::~BaseRefCounted()
{
    NS_CHECK([&]()
    {
        // Note that 1 is valid when the object lives is the stack or is being manually destroyed
        int32_t n = mRefCount.Load();
        NS_CHECK(n == 1 || n == 0, "Unexpected RefCount(%d) deleting object at %p", n, this);
        return true;
    }
    (), "_");
}
If you created the object in the stack, at destruction time the reference counter must be 1. If not, an error is raised.
 
christyjquinn
Topic Author
Posts: 17
Joined: 17 Aug 2022, 17:03

Re: Commands and being stack allocated in view models

29 Mar 2023, 19:02

Awesome thanks both, I suppose if we aren't having performance issues there is no harm allocating on the heap by default.

Who is online

Users browsing this forum: No registered users and 1 guest