I have the following code:
// button is a view
{
auto dialog = VSTGUI::owned<MyContainer>(new MyContainer({0, 0, 100, 100}));
DLOG_F(INFO, "before beginModalViewSession...");
auto id = button->getFrame()->beginModalViewSession(dialog);
DLOG_F(INFO, "after beginModalViewSession...");
button->getFrame()->endModalViewSession(*id);
DLOG_F(INFO, "endModalViewSession...");
}
This crashes because the view is deleted twice. (MyViewContainer simply extend CViewContainer to log constructor/destructor/remember/forget/beforeDelete)
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:33 INFO| MyContainer
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:102 INFO| before beginModalViewSession...
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:44 INFO| MyContainer::remember
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:44 INFO| MyContainer::remember
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:44 INFO| MyContainer::remember
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:49 INFO| MyContainer::forget
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:104 INFO| after beginModalViewSession...
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:44 INFO| MyContainer::remember
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:49 INFO| MyContainer::forget
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:49 INFO| MyContainer::forget
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:49 INFO| MyContainer::forget
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:49 INFO| MyContainer::forget
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:56 INFO| MyContainer::beforeDelete
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:38 INFO| ~MyContainer
2020-08-06 14:19:11.197 ( 5.874s) [ 2860]JTPTextButtonController:106 INFO| endModalViewSession...
Note how the container is deleted during the call to endModalViewSession when I am expecting it to be deleted when the block ends…
The API for beginModalViewSession states “view new modal view (ownership is shared with the caller)” so I wasn’t expecting the modal view session to take ownership of the view…
What am I missing?
Thanks
Yan
1 Like
I tweaked the code further like this to clearly point out what is going on:
{
DLOG_F(INFO, "Start of block");
auto dialog = VSTGUI::owned<MyContainer>(new MyContainer({0, 0, 100, 100}));
DLOG_F(INFO, "before beginModalViewSession...");
auto id = button->getFrame()->beginModalViewSession(dialog);
DLOG_F(INFO, "after beginModalViewSession.");
DLOG_F(INFO, "before endModalViewSession...");
button->getFrame()->endModalViewSession(*id);
DLOG_F(INFO, "after endModalViewSession.");
DLOG_F(INFO, "End of block");
}
DLOG_F(INFO, "After block");
And this is the output:
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:102 INFO| Start of block
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:33 INFO| MyContainer
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:104 INFO| before beginModalViewSession...
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:44 INFO| MyContainer::remember
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:44 INFO| MyContainer::remember
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:44 INFO| MyContainer::remember
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:49 INFO| MyContainer::forget
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:106 INFO| after beginModalViewSession.
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:107 INFO| before endModalViewSession...
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:44 INFO| MyContainer::remember
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:49 INFO| MyContainer::forget
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:49 INFO| MyContainer::forget
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:49 INFO| MyContainer::forget
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:49 INFO| MyContainer::forget
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:56 INFO| MyContainer::beforeDelete
2020-08-07 07:55:12.724 ( 4.803s) [ 8A01]JTPTextButtonController:38 INFO| ~MyContainer
2020-08-07 07:55:12.724 ( 4.804s) [ 8A01]JTPTextButtonController:109 INFO| after endModalViewSession.
2020-08-07 07:55:12.724 ( 4.804s) [ 8A01]JTPTextButtonController:110 INFO| End of block
Exception: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
Or in other words:
beginModalViewSession does 3 remember + 1 forget => 2 remember
endModalViewSession does 1 remember and 4 forget => 3 forget
This would make sense IF the modal view session API was taking ownership of the view provided. As I pointed out this is the API for this call
/** begin a new modal view session
*
* A modal view session is active until endModalViewSession is called and in that time all UI
* events are only dispatched to the modal view or its child views.
* Modal view sessions can be stacked but must be ended in the same order.
*
* @param view new modal view (ownership is shared with the caller)
* @return a unique session identifier
*/
So which one is wrong? Is the code meant to take ownership and the comment is wrong? Or is the comment right and the code is wrong and is missing a remember (or doing one too may forget)?
Thanks
Yan
Hi,
this is unit tested, please see vstgui/cframe_test.cpp at develop · steinbergmedia/vstgui · GitHub
And I don’t see an issue here.
You can add
EXPECT (view->getNbReference () == 1);
at the end of the test to verify that the reference count is as expected.
Or do you expect something else?
Cheers,
Arne
I think what is wrong is that there is one more call to “forget” than “remember”.
If you change your unit test this way:
TEST(setModalView,
auto frame = owned (new CFrame (CRect (0, 0, 100, 100), nullptr));
auto view = shared (new View ());
auto nbReferences = view->getNbReference (); // here nbReference == 2
EXPECT (frame->getModalView () == nullptr);
auto session = frame->beginModalViewSession (view);
EXPECT (session);
EXPECT (frame->getModalView () == view);
auto container = shared (new CViewContainer (CRect (0, 0, 0, 0)));
auto session2 = frame->beginModalViewSession (container);
EXPECT (session2)
EXPECT (frame->getModalView () == container);
EXPECT (frame->endModalViewSession (*session) == false);
EXPECT (frame->endModalViewSession (*session2) == true);
EXPECT (frame->getModalView () == view);
EXPECT (frame->endModalViewSession (*session) == true);
EXPECT (frame->getModalView () == nullptr);
EXPECT (view->getNbReference () == nbReferences); // here nbReferences == 1 => will fail
);
Then this test will fail.
I used “owned” in my code when you are using “shared” (for the view) because in my mind I own the view. I give it to the modal view session API so that you can use it. But this API is doing more “forget” than “remember” and I don’t think that is a good thing. It should be neutral.
It’s fine to leave it the way you have it but then I think you should change the documentation to state that it is taking ownership of the view provided.
Ah, now I see. I will change the documentation as the behaviour is now the same as adding a view to a container.