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

Fixed double delete in CAPITimer and add documentation for platform networking CAPI integration #6994

Merged
merged 8 commits into from
Sep 25, 2023

Conversation

michael-wb
Copy link
Contributor

@michael-wb michael-wb commented Sep 21, 2023

What, How & Why?

There was a double delete occurring of the callback handler when a CAPITimer object was destroyed in the CAPI implementation after having called realm_sync_socket_callback_complete() to report timer status. Broke up the realm_sync_socket_callback_complete() to use 4 separate functions instead:

  • realm_sync_socket_timer_complete() - to be used when a timer expires or an error occurs
  • realm_sync_socket_timer_canceled() - to be used when a timer has been canceled
  • realm_sync_socket_post_complete() - to be used when a callback handler added using post() is ready to run
  • realm_sync_socket_write_complete() - to be used when a websocket write operation has completed

The CAPITimer's callback handler will only be destroyed when the CAPITimer object is destroyed.

Added documentation to the Platform Networking functions in realm.h to describe their purpose to help with future custom platform networking implementations.

FIxes #6993

☑️ ToDos

  • 📝 Changelog update
  • [ ] 🚦 Tests (or not relevant)
  • [ ] C-API, if public C++ API changed.

@michael-wb michael-wb self-assigned this Sep 21, 2023
@cla-bot cla-bot bot added the cla: yes label Sep 21, 2023
@coveralls-official
Copy link

coveralls-official bot commented Sep 21, 2023

Pull Request Test Coverage Report for Build michael.wilkersonbarker_855

  • 20 of 33 (60.61%) changed or added relevant lines in 2 files are covered.
  • 75 unchanged lines in 13 files lost coverage.
  • Overall coverage increased (+0.04%) to 91.242%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/realm/object-store/c_api/socket_provider.cpp 19 32 59.38%
Files with Coverage Reduction New Missed Lines %
src/realm/object-store/c_api/socket_provider.cpp 2 66.43%
src/realm/object-store/sync/sync_manager.cpp 2 86.96%
src/realm/sync/instruction_applier.cpp 2 70.21%
src/realm/sync/noinst/server/server_history.cpp 2 67.69%
test/test_lang_bind_helper.cpp 2 93.3%
src/realm/sync/noinst/client_reset_operation.cpp 4 87.23%
src/realm/sync/transform.cpp 4 61.71%
src/realm/unicode.cpp 4 90.15%
src/realm/index_string.cpp 6 87.76%
src/realm/bplustree.cpp 7 75.72%
Totals Coverage Status
Change from base Build 1704: 0.04%
Covered Lines: 232543
Relevant Lines: 254864

💛 - Coveralls

@michael-wb michael-wb force-pushed the mwb/fix-capitimer-double-delete branch from d672cd4 to f56d770 Compare September 21, 2023 19:46
Copy link
Contributor

@nhachicha nhachicha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this branch & it fixed the double delete issue 👍 left some minor comment

src/realm.h Outdated
RLM_API void realm_sync_socket_websocket_connected(realm_websocket_observer_t* realm_websocket_observer,
const char* protocol);

/**
* To be called when an error occurs - the actual error value with be provided when the websocket_closed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* To be called when an error occurs - the actual error value with be provided when the websocket_closed
* To be called when an error occurs - the actual error value will be provided when the websocket_closed

@@ -4069,17 +4088,79 @@ RLM_API realm_sync_socket_t* realm_sync_socket_new(
realm_sync_socket_websocket_async_write_func_t websocket_write_func,
realm_sync_socket_websocket_free_func_t websocket_free_func);

RLM_API void realm_sync_socket_callback_complete(realm_sync_socket_callback_t* realm_callback, realm_errno_e status,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to be called after a websocket_async_write_func to indicate error/complete via the callback provided in

realm_sync_socket_callback_t* realm_callback);

we can use alternatively realm_sync_socket_post_complete ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right - I forgot about that callback. I'll create a new function realm_sync_socket_write_complete() so it's obvious when it should be called (even though it does the same as post_complete)


// These values were originally provided to the socket_provider instance by the CAPI
// implementation when it was created
realm_userdata_t m_userdata = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is initialized via the ctor, it is also passed to

m_timer_cancel(m_userdata, m_timer);
m_timer_free(m_userdata, m_timer);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an overlook, so it should be.

@michael-wb michael-wb requested a review from ironage September 22, 2023 19:49
* @param reason a string describing details about the error that occurred or empty string if no error.
* NOTE: This function must be called by the event loop execution thread.
*/
RLM_API void realm_sync_socket_timer_complete(realm_sync_socket_callback_t* timer_handler, realm_errno_e status,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aren't these two new api calls leaking the callback if realm_sync_socket_post_complete is never called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two callbacks are meant to be used with the CAPITimer, which will release the callback handler when the timer is destroyed.
Do you think it would be better to not call realm_release(m_handler) in the destructor and rely on the call to timer_complete/timer_canceled to delete m_handler?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I should rethink the callback handler a bit differently so the callback isn't called multiple times...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's an option. I just saw you kept the release in dtor, so there is no leak. But, how is double free gone? (can't the callback be invoked followed by release, and then when the timer is gone there is a new release?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The double free is gone, since you're supposed to use the timer_complete() or timer_canceled() fcns, which doesn't call release, so there's only one release when the timer is destroyed (if the CAPI is implemented properly), however, the callback could be called multiple times depending on how the timer is implemented. (cancel is always called in the destructor to make sure the timer is stopped)

Copy link
Collaborator

@danieltabacaru danieltabacaru Sep 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but post calls release.. That was the initial issue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you're not supposed to use post (or write_complete) with the timer handler, which are different objects from the callback handlers provided to the post or async_write functions.

* @param timer_handler the timer callback handler that was provided when the timer was created.
* NOTE: This function must be called by the event loop execution thread.
*/
RLM_API void realm_sync_socket_timer_canceled(realm_sync_socket_callback_t* timer_handler);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should test the new api calls

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could even recreate the initial issue using TestSyncTimer in c_api.cpp

Copy link
Collaborator

@danieltabacaru danieltabacaru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I would add a few more tests though.

@michael-wb
Copy link
Contributor Author

Looks good. I would add a few more tests though.

Created RCORE-1793/ #6998 to add more tests as a separate task

Copy link
Contributor

@nhachicha nhachicha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested again using the new realm_sync_socket_write_complete looks good!

@michael-wb michael-wb merged commit a9c8f99 into master Sep 25, 2023
@michael-wb michael-wb deleted the mwb/fix-capitimer-double-delete branch September 25, 2023 18:02
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Double delete in CAPITimer causing Kotlin SDK to crash
3 participants