Issue with Modal View Session

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.