• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 1706

25 Sep 2023 06:02PM CUT coverage: 91.215% (+0.02%) from 91.198%
1706

push

Evergreen

web-flow
Fixed double delete in CAPITimer and add documentation for platform networking CAPI integration (#6994)

* Fixed double delete in CAPITimer and added individual functions for handling callbacks.
* Added realm_sync_socket_write_complete() fcn and other updates from review
* Don't call handler functions more than once, esp for timers; updated documentation
* Updated some comments
* Updated changelog after release

95864 of 175718 branches covered (0.0%)

20 of 33 new or added lines in 2 files covered. (60.61%)

82 existing lines in 11 files now uncovered.

232491 of 254882 relevant lines covered (91.22%)

6729880.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.43
/src/realm/object-store/c_api/socket_provider.cpp
1
#include <realm/error_codes.hpp>
2
#include <realm/status.hpp>
3
#include <realm/object-store/c_api/util.hpp>
4
#include <realm/sync/socket_provider.hpp>
5
#include <realm/sync/network/websocket.hpp>
6

7
namespace realm::c_api {
8
namespace {
9

10
// THis class represents the timer resource that is returned to the sync client from the
11
// CAPI implementation details for canceling and deleting the timer resources.
12
struct CAPITimer : sync::SyncSocketProvider::Timer {
13
public:
14
    CAPITimer(realm_userdata_t userdata, int64_t delay_ms, realm_sync_socket_callback_t* handler,
15
              realm_sync_socket_create_timer_func_t create_timer_func,
16
              realm_sync_socket_timer_canceled_func_t cancel_timer_func,
17
              realm_sync_socket_timer_free_func_t free_timer_func)
18
        : m_handler(handler)
19
        , m_userdata(userdata)
20
        , m_timer_create(create_timer_func)
21
        , m_timer_cancel(cancel_timer_func)
22
        , m_timer_free(free_timer_func)
23
    {
14✔
24
        m_timer = m_timer_create(userdata, delay_ms, handler);
14✔
25
    }
14✔
26

27
    /// Cancels the timer and destroys the timer instance.
28
    ~CAPITimer()
29
    {
14✔
30
        // Make sure the timer is stopped, if not already
8✔
31
        m_timer_cancel(m_userdata, m_timer);
14✔
32
        m_timer_free(m_userdata, m_timer);
14✔
33
        realm_release(m_handler);
14✔
34
    }
14✔
35

36
    // Cancel the timer immediately - the CAPI implementation will need to call the
37
    // realm_sync_socket_timer_canceled function to notify the sync client that the
38
    // timer has been canceled and must be called in the same execution thread as
39
    // the timer complete.
40
    void cancel() override
41
    {
×
42
        m_timer_cancel(m_userdata, m_timer);
×
43
    }
×
44

45
private:
46
    // A pointer to the CAPI implementation's timer instance. This is provided by the
47
    // CAPI implementation when the create_timer_func function is called.
48
    realm_sync_socket_timer_t m_timer = nullptr;
49

50
    // A wrapped reference to the callback function to be called when the timer completes,
51
    // is canceled or an error occurs. This is provided by the Sync Client
52
    realm_sync_socket_callback_t* m_handler = nullptr;
53

54
    // These values were originally provided to the socket_provider instance by the CAPI
55
    // implementation when it was created
56
    realm_userdata_t m_userdata = nullptr;
57
    realm_sync_socket_create_timer_func_t m_timer_create = nullptr;
58
    realm_sync_socket_timer_canceled_func_t m_timer_cancel = nullptr;
59
    realm_sync_socket_timer_free_func_t m_timer_free = nullptr;
60
};
61

62
static void realm_sync_socket_op_complete(realm_sync_socket_callback* realm_callback, realm_errno_e status,
63
                                          const char* reason)
64
{
16✔
65
    if (realm_callback->get() != nullptr) {
16✔
66
        auto complete_status = status == realm_errno_e::RLM_ERR_NONE
16✔
67
                                   ? Status::OK()
16✔
68
                                   : Status{static_cast<ErrorCodes::Error>(status), reason};
8✔
69
        (*(realm_callback->get()))(complete_status);
16✔
70
        // Keep the container, but release the handler so it can't be called twice.
8✔
71
        realm_callback->reset();
16✔
72
    }
16✔
73
}
16✔
74

75
RLM_API void realm_sync_socket_timer_complete(realm_sync_socket_callback* timer_handler, realm_errno_e status,
76
                                              const char* reason)
NEW
77
{
×
NEW
78
    realm_sync_socket_op_complete(timer_handler, status, reason);
×
NEW
79
}
×
80

81
RLM_API void realm_sync_socket_timer_canceled(realm_sync_socket_callback* timer_handler)
NEW
82
{
×
NEW
83
    realm_sync_socket_op_complete(timer_handler, RLM_ERR_OPERATION_ABORTED, "Timer canceled");
×
NEW
84
}
×
85

86
// This class represents a websocket instance provided by the CAPI implememtation for sending
87
// and receiving data and connection state from the websocket. This class is used directly by
88
// the sync client.
89
struct CAPIWebSocket : sync::WebSocketInterface {
90
public:
91
    CAPIWebSocket(realm_userdata_t userdata, realm_sync_socket_connect_func_t websocket_connect_func,
92
                  realm_sync_socket_websocket_async_write_func_t websocket_write_func,
93
                  realm_sync_socket_websocket_free_func_t websocket_free_func, realm_websocket_observer_t* observer,
94
                  sync::WebSocketEndpoint&& endpoint)
95
        : m_observer(observer)
96
        , m_userdata(userdata)
97
        , m_websocket_connect(websocket_connect_func)
98
        , m_websocket_async_write(websocket_write_func)
99
        , m_websocket_free(websocket_free_func)
100
    {
2✔
101
        realm_websocket_endpoint_t capi_endpoint;
2✔
102
        capi_endpoint.address = endpoint.address.c_str();
2✔
103
        capi_endpoint.port = endpoint.port;
2✔
104
        capi_endpoint.path = endpoint.path.c_str();
2✔
105

1✔
106
        std::vector<const char*> protocols;
2✔
107
        for (size_t i = 0; i < endpoint.protocols.size(); ++i) {
20✔
108
            auto& protocol = endpoint.protocols[i];
18✔
109
            protocols.push_back(protocol.c_str());
18✔
110
        }
18✔
111
        capi_endpoint.protocols = protocols.data();
2✔
112
        capi_endpoint.num_protocols = protocols.size();
2✔
113
        capi_endpoint.is_ssl = endpoint.is_ssl;
2✔
114

1✔
115
        m_socket = m_websocket_connect(m_userdata, capi_endpoint, observer);
2✔
116
    }
2✔
117

118
    /// Destroys the web socket instance.
119
    ~CAPIWebSocket()
120
    {
2✔
121
        m_websocket_free(m_userdata, m_socket);
2✔
122
        realm_release(m_observer);
2✔
123
    }
2✔
124

125
    void async_write_binary(util::Span<const char> data, sync::SyncSocketProvider::FunctionHandler&& handler) final
126
    {
16✔
127
        auto shared_handler = std::make_shared<sync::SyncSocketProvider::FunctionHandler>(std::move(handler));
16✔
128
        m_websocket_async_write(m_userdata, m_socket, data.data(), data.size(),
16✔
129
                                new realm_sync_socket_callback_t(std::move(shared_handler)));
16✔
130
    }
16✔
131

132
private:
133
    // A pointer to the CAPI implementation's websocket instance. This is provided by
134
    // the m_websocket_connect() function when this websocket instance is created.
135
    realm_sync_socket_websocket_t m_socket = nullptr;
136

137
    // A wrapped reference to the websocket observer in the sync client that receives the
138
    // websocket status callbacks. This is provided by the Sync Client.
139
    realm_websocket_observer_t* m_observer = nullptr;
140

141
    // These values were originally provided to the socket_provider instance by the CAPI
142
    // implementation when it was created.
143
    realm_userdata_t m_userdata = nullptr;
144
    realm_sync_socket_connect_func_t m_websocket_connect = nullptr;
145
    realm_sync_socket_websocket_async_write_func_t m_websocket_async_write = nullptr;
146
    realm_sync_socket_websocket_free_func_t m_websocket_free = nullptr;
147
};
148

149
// Represents the websocket observer in the sync client that receives websocket status
150
// callbacks and passes them along to the WebSocketObserver object.
151
struct CAPIWebSocketObserver : sync::WebSocketObserver {
152
public:
153
    CAPIWebSocketObserver(std::unique_ptr<sync::WebSocketObserver> observer)
154
        : m_observer(std::move(observer))
155
    {
2✔
156
    }
2✔
157

158
    ~CAPIWebSocketObserver() = default;
2✔
159

160
    void websocket_connected_handler(const std::string& protocol) final
161
    {
2✔
162
        m_observer->websocket_connected_handler(protocol);
2✔
163
    }
2✔
164

165
    void websocket_error_handler() final
166
    {
×
167
        m_observer->websocket_error_handler();
×
168
    }
×
169

170
    bool websocket_binary_message_received(util::Span<const char> data) final
171
    {
9✔
172
        return m_observer->websocket_binary_message_received(data);
9✔
173
    }
9✔
174

175
    bool websocket_closed_handler(bool was_clean, sync::websocket::WebSocketError code, std::string_view msg) final
176
    {
×
177
        return m_observer->websocket_closed_handler(was_clean, code, msg);
×
178
    }
×
179

180
private:
181
    std::unique_ptr<sync::WebSocketObserver> m_observer;
182
};
183

184
// This is the primary resource for providing event loop, timer and websocket
185
// resources and synchronization for the Sync Client. The CAPI implementation
186
// needs to implement the "funct_t" functions provided to this class for connecting
187
// the implementation to the operations called by the Sync Client.
188
struct CAPISyncSocketProvider : sync::SyncSocketProvider {
189
    realm_userdata_t m_userdata = nullptr;
190
    realm_free_userdata_func_t m_userdata_free = nullptr;
191
    realm_sync_socket_post_func_t m_post = nullptr;
192
    realm_sync_socket_create_timer_func_t m_timer_create = nullptr;
193
    realm_sync_socket_timer_canceled_func_t m_timer_cancel = nullptr;
194
    realm_sync_socket_timer_free_func_t m_timer_free = nullptr;
195
    realm_sync_socket_connect_func_t m_websocket_connect = nullptr;
196
    realm_sync_socket_websocket_async_write_func_t m_websocket_async_write = nullptr;
197
    realm_sync_socket_websocket_free_func_t m_websocket_free = nullptr;
198

199
    CAPISyncSocketProvider() = default;
2✔
200
    CAPISyncSocketProvider(CAPISyncSocketProvider&& other)
201
        : m_userdata(std::exchange(other.m_userdata, nullptr))
202
        , m_userdata_free(std::exchange(other.m_userdata_free, nullptr))
203
        , m_post(std::exchange(other.m_post, nullptr))
204
        , m_timer_create(std::exchange(other.m_timer_create, nullptr))
205
        , m_timer_cancel(std::exchange(other.m_timer_cancel, nullptr))
206
        , m_timer_free(std::exchange(other.m_timer_free, nullptr))
207
        , m_websocket_connect(std::exchange(other.m_websocket_connect, nullptr))
208
        , m_websocket_async_write(std::exchange(other.m_websocket_async_write, nullptr))
209
        , m_websocket_free(std::exchange(other.m_websocket_free, nullptr))
210
    {
×
NEW
211
        // userdata_free can be null if userdata is not used
×
NEW
212
        if (m_userdata != nullptr) {
×
NEW
213
            REALM_ASSERT(m_userdata_free);
×
NEW
214
        }
×
215
        REALM_ASSERT(m_post);
×
216
        REALM_ASSERT(m_timer_create);
×
217
        REALM_ASSERT(m_timer_cancel);
×
218
        REALM_ASSERT(m_timer_free);
×
219
        REALM_ASSERT(m_websocket_connect);
×
220
        REALM_ASSERT(m_websocket_async_write);
×
221
        REALM_ASSERT(m_websocket_free);
×
222
    }
×
223

224
    ~CAPISyncSocketProvider()
225
    {
2✔
226
        if (m_userdata_free) {
2✔
227
            m_userdata_free(m_userdata);
2✔
228
        }
2✔
229
    }
2✔
230

231
    // Create a websocket object that will be returned to the Sync Client, which is expected to
232
    // begin connecting to the endpoint as soon as the object is created. The state and any data
233
    // received is passed to the socket observer via the helper functions defined below this class.
234
    std::unique_ptr<sync::WebSocketInterface> connect(std::unique_ptr<sync::WebSocketObserver> observer,
235
                                                      sync::WebSocketEndpoint&& endpoint) final
236
    {
2✔
237
        auto capi_observer = std::make_shared<CAPIWebSocketObserver>(std::move(observer));
2✔
238
        return std::make_unique<CAPIWebSocket>(m_userdata, m_websocket_connect, m_websocket_async_write,
2✔
239
                                               m_websocket_free, new realm_websocket_observer_t(capi_observer),
2✔
240
                                               std::move(endpoint));
2✔
241
    }
2✔
242

243
    void post(FunctionHandler&& handler) final
244
    {
16✔
245
        auto shared_handler = std::make_shared<FunctionHandler>(std::move(handler));
16✔
246
        m_post(m_userdata, new realm_sync_socket_callback_t(std::move(shared_handler)));
16✔
247
    }
16✔
248

249
    SyncTimer create_timer(std::chrono::milliseconds delay, FunctionHandler&& handler) final
250
    {
14✔
251
        auto shared_handler = std::make_shared<FunctionHandler>(std::move(handler));
14✔
252
        return std::make_unique<CAPITimer>(m_userdata, delay.count(),
14✔
253
                                           new realm_sync_socket_callback_t(std::move(shared_handler)),
14✔
254
                                           m_timer_create, m_timer_cancel, m_timer_free);
14✔
255
    }
14✔
256
};
257

258
} // namespace
259

260
RLM_API realm_sync_socket_t* realm_sync_socket_new(
261
    realm_userdata_t userdata, realm_free_userdata_func_t userdata_free, realm_sync_socket_post_func_t post_func,
262
    realm_sync_socket_create_timer_func_t create_timer_func,
263
    realm_sync_socket_timer_canceled_func_t cancel_timer_func, realm_sync_socket_timer_free_func_t free_timer_func,
264
    realm_sync_socket_connect_func_t websocket_connect_func,
265
    realm_sync_socket_websocket_async_write_func_t websocket_write_func,
266
    realm_sync_socket_websocket_free_func_t websocket_free_func)
267
{
2✔
268
    return wrap_err([&]() {
2✔
269
        auto capi_socket_provider = std::make_shared<CAPISyncSocketProvider>();
2✔
270
        capi_socket_provider->m_userdata = userdata;
2✔
271
        capi_socket_provider->m_userdata_free = userdata_free;
2✔
272
        capi_socket_provider->m_post = post_func;
2✔
273
        capi_socket_provider->m_timer_create = create_timer_func;
2✔
274
        capi_socket_provider->m_timer_cancel = cancel_timer_func;
2✔
275
        capi_socket_provider->m_timer_free = free_timer_func;
2✔
276
        capi_socket_provider->m_websocket_connect = websocket_connect_func;
2✔
277
        capi_socket_provider->m_websocket_async_write = websocket_write_func;
2✔
278
        capi_socket_provider->m_websocket_free = websocket_free_func;
2✔
279
        return new realm_sync_socket_t(std::move(capi_socket_provider));
2✔
280
    });
2✔
281
}
2✔
282

283
RLM_API void realm_sync_socket_post_complete(realm_sync_socket_callback* post_handler, realm_errno_e status,
284
                                             const char* reason)
285
{
16✔
286
    realm_sync_socket_op_complete(post_handler, status, reason);
16✔
287
    realm_release(post_handler);
16✔
288
}
16✔
289

290
RLM_API void realm_sync_socket_write_complete(realm_sync_socket_callback_t* write_handler, realm_errno_e status,
291
                                              const char* reason)
UNCOV
292
{
×
NEW
293
    realm_sync_socket_op_complete(write_handler, status, reason);
×
NEW
294
    realm_release(write_handler);
×
UNCOV
295
}
×
296

297
RLM_API void realm_sync_socket_websocket_connected(realm_websocket_observer_t* realm_websocket_observer,
298
                                                   const char* protocol)
299
{
×
300
    realm_websocket_observer->get()->websocket_connected_handler(protocol);
×
301
}
×
302

303
RLM_API void realm_sync_socket_websocket_error(realm_websocket_observer_t* realm_websocket_observer)
304
{
×
305
    realm_websocket_observer->get()->websocket_error_handler();
×
306
}
×
307

308
RLM_API void realm_sync_socket_websocket_message(realm_websocket_observer_t* realm_websocket_observer,
309
                                                 const char* data, size_t data_size)
310
{
×
311
    realm_websocket_observer->get()->websocket_binary_message_received(util::Span{data, data_size});
×
312
}
×
313

314
RLM_API void realm_sync_socket_websocket_closed(realm_websocket_observer_t* realm_websocket_observer, bool was_clean,
315
                                                realm_web_socket_errno_e status, const char* reason)
316
{
×
317
    realm_websocket_observer->get()->websocket_closed_handler(
×
NEW
318
        was_clean, static_cast<sync::websocket::WebSocketError>(status), reason);
×
319
}
×
320

321
RLM_API void realm_sync_client_config_set_sync_socket(realm_sync_client_config_t* config,
322
                                                      realm_sync_socket_t* sync_socket) RLM_API_NOEXCEPT
323
{
×
324
    config->socket_provider = *sync_socket;
×
325
}
×
326

327
} // namespace realm::c_api
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc