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

realm / realm-core / 1772

20 Oct 2023 07:34PM UTC coverage: 91.61% (+0.04%) from 91.567%
1772

push

Evergreen

web-flow
Added sync socket result enum for sync socket callback handlers in C API (#7015)

* Added sync socket result enum for sync socket callback handlers in C API
* Updated changelog
* CAPI: timer callbacks are now released by canceled/complete function
* Updated c_api tests to use all sync socket c_api functions
* Additional updates to sync socket c api test
* Added CAPI write callback manager to manage async write callbacks
* Pass error codes up to default socket provider for async_write_binary() callbacks
* Removed async write callback manager from CAPI
* Updated changelog after release
* clang format and updates from review
* Update async_write_binary() error handling to not throw exception
* Updated a few comments
* Another comment update
* Updates from review

94360 of 173622 branches covered (0.0%)

111 of 150 new or added lines in 8 files covered. (74.0%)

66 existing lines in 17 files now uncovered.

230703 of 251832 relevant lines covered (91.61%)

6730695.32 hits per line

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

74.48
/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_timer_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_userdata(userdata)
19
        , m_timer_create(create_timer_func)
20
        , m_timer_cancel(cancel_timer_func)
21
        , m_timer_free(free_timer_func)
22
    {
14✔
23
        m_timer = m_timer_create(userdata, delay_ms, handler);
14✔
24
    }
14✔
25

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

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

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

48
    // These values were originally provided to the socket_provider instance by the CAPI
49
    // implementation when it was created
50
    realm_userdata_t m_userdata = nullptr;
51
    realm_sync_socket_create_timer_func_t m_timer_create = nullptr;
52
    realm_sync_socket_timer_canceled_func_t m_timer_cancel = nullptr;
53
    realm_sync_socket_timer_free_func_t m_timer_free = nullptr;
54
};
55

56
static void realm_sync_socket_op_complete(realm_sync_socket_callback* realm_callback,
57
                                          realm_sync_socket_callback_result_e result, const char* reason)
58
{
64✔
59
    if (!realm_callback)
64✔
NEW
60
        return;
×
61

34✔
62
    (*realm_callback)(result, reason);
64✔
63
    realm_release(realm_callback);
64✔
64
}
64✔
65

66
RLM_API void realm_sync_socket_timer_complete(realm_sync_socket_timer_callback_t* timer_handler,
67
                                              realm_sync_socket_callback_result_e result, const char* reason)
68
{
5✔
69
    realm_sync_socket_op_complete(timer_handler, result, reason);
5✔
70
}
5✔
71

72
RLM_API void realm_sync_socket_timer_canceled(realm_sync_socket_timer_callback_t* timer_handler)
73
{
9✔
74
    realm_sync_socket_op_complete(timer_handler, RLM_ERR_SYNC_SOCKET_OPERATION_ABORTED, "Timer canceled");
9✔
75
}
9✔
76

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

1✔
97
        std::vector<const char*> protocols;
2✔
98
        for (size_t i = 0; i < endpoint.protocols.size(); ++i) {
20✔
99
            auto& protocol = endpoint.protocols[i];
18✔
100
            protocols.push_back(protocol.c_str());
18✔
101
        }
18✔
102
        capi_endpoint.protocols = protocols.data();
2✔
103
        capi_endpoint.num_protocols = protocols.size();
2✔
104
        capi_endpoint.is_ssl = endpoint.is_ssl;
2✔
105

1✔
106
        m_socket = m_websocket_connect(m_userdata, capi_endpoint, observer);
2✔
107
    }
2✔
108

109
    /// Destroys the web socket instance.
110
    ~CAPIWebSocket()
111
    {
2✔
112
        m_websocket_free(m_userdata, m_socket);
2✔
113
        realm_release(m_observer);
2✔
114
    }
2✔
115

116
    void async_write_binary(util::Span<const char> data, sync::SyncSocketProvider::FunctionHandler&& handler) final
117
    {
16✔
118
        auto shared_handler = std::make_shared<sync::SyncSocketProvider::FunctionHandler>(std::move(handler));
16✔
119
        m_websocket_async_write(m_userdata, m_socket, data.data(), data.size(),
16✔
120
                                new realm_sync_socket_write_callback_t(std::move(shared_handler)));
16✔
121
    }
16✔
122

123
private:
124
    // A pointer to the CAPI implementation's websocket instance. This is provided by
125
    // the m_websocket_connect() function when this websocket instance is created.
126
    realm_sync_socket_websocket_t m_socket = nullptr;
127

128
    // A wrapped reference to the websocket observer in the sync client that receives the
129
    // websocket status callbacks. This is provided by the Sync Client.
130
    realm_websocket_observer_t* m_observer = nullptr;
131

132
    // These values were originally provided to the socket_provider instance by the CAPI
133
    // implementation when it was created.
134
    realm_userdata_t m_userdata = nullptr;
135
    realm_sync_socket_connect_func_t m_websocket_connect = nullptr;
136
    realm_sync_socket_websocket_async_write_func_t m_websocket_async_write = nullptr;
137
    realm_sync_socket_websocket_free_func_t m_websocket_free = nullptr;
138
};
139

140
// Represents the websocket observer in the sync client that receives websocket status
141
// callbacks and passes them along to the WebSocketObserver object.
142
struct CAPIWebSocketObserver : sync::WebSocketObserver {
143
public:
144
    CAPIWebSocketObserver(std::unique_ptr<sync::WebSocketObserver> observer)
145
        : m_observer(std::move(observer))
146
    {
2✔
147
        REALM_ASSERT_EX(m_observer, "WebSocketObserver cannot be null");
2✔
148
    }
2✔
149

150
    ~CAPIWebSocketObserver() = default;
2✔
151

152
    void websocket_connected_handler(const std::string& protocol) final
153
    {
2✔
154
        m_observer->websocket_connected_handler(protocol);
2✔
155
    }
2✔
156

157
    void websocket_error_handler() final
158
    {
×
159
        m_observer->websocket_error_handler();
×
160
    }
×
161

162
    bool websocket_binary_message_received(util::Span<const char> data) final
163
    {
9✔
164
        return m_observer->websocket_binary_message_received(data);
9✔
165
    }
9✔
166

167
    bool websocket_closed_handler(bool was_clean, sync::websocket::WebSocketError code, std::string_view msg) final
168
    {
×
169
        return m_observer->websocket_closed_handler(was_clean, code, msg);
×
170
    }
×
171

172
private:
173
    std::unique_ptr<sync::WebSocketObserver> m_observer;
174
};
175

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

191
    CAPISyncSocketProvider() = default;
2✔
192
    CAPISyncSocketProvider(CAPISyncSocketProvider&& other)
193
        : m_userdata(std::exchange(other.m_userdata, nullptr))
194
        , m_userdata_free(std::exchange(other.m_userdata_free, nullptr))
195
        , m_post(std::exchange(other.m_post, nullptr))
196
        , m_timer_create(std::exchange(other.m_timer_create, nullptr))
197
        , m_timer_cancel(std::exchange(other.m_timer_cancel, nullptr))
198
        , m_timer_free(std::exchange(other.m_timer_free, nullptr))
199
        , m_websocket_connect(std::exchange(other.m_websocket_connect, nullptr))
200
        , m_websocket_async_write(std::exchange(other.m_websocket_async_write, nullptr))
201
        , m_websocket_free(std::exchange(other.m_websocket_free, nullptr))
202
    {
×
203
        // userdata_free can be null if userdata is not used
×
204
        if (m_userdata != nullptr) {
×
205
            REALM_ASSERT(m_userdata_free);
×
206
        }
×
207
        REALM_ASSERT(m_post);
×
208
        REALM_ASSERT(m_timer_create);
×
209
        REALM_ASSERT(m_timer_cancel);
×
210
        REALM_ASSERT(m_timer_free);
×
211
        REALM_ASSERT(m_websocket_connect);
×
212
        REALM_ASSERT(m_websocket_async_write);
×
213
        REALM_ASSERT(m_websocket_free);
×
214
    }
×
215

216
    ~CAPISyncSocketProvider()
217
    {
2✔
218
        if (m_userdata_free) {
2✔
219
            m_userdata_free(m_userdata);
2✔
220
        }
2✔
221
    }
2✔
222

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

235
    void post(FunctionHandler&& handler) final
236
    {
34✔
237
        auto shared_handler = std::make_shared<FunctionHandler>(std::move(handler));
34✔
238
        m_post(m_userdata, new realm_sync_socket_post_callback_t(std::move(shared_handler)));
34✔
239
    }
34✔
240

241
    SyncTimer create_timer(std::chrono::milliseconds delay, FunctionHandler&& handler) final
242
    {
14✔
243
        auto shared_handler = std::make_shared<FunctionHandler>(std::move(handler));
14✔
244
        return std::make_unique<CAPITimer>(m_userdata, delay.count(),
14✔
245
                                           new realm_sync_socket_timer_callback_t(std::move(shared_handler)),
14✔
246
                                           m_timer_create, m_timer_cancel, m_timer_free);
14✔
247
    }
14✔
248
};
249

250
} // namespace
251

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

275
RLM_API void realm_sync_socket_post_complete(realm_sync_socket_post_callback_t* post_handler,
276
                                             realm_sync_socket_callback_result_e result, const char* reason)
277
{
34✔
278
    realm_sync_socket_op_complete(post_handler, result, reason);
34✔
279
}
34✔
280

281
RLM_API void realm_sync_socket_write_complete(realm_sync_socket_write_callback_t* write_handler,
282
                                              realm_sync_socket_callback_result_e result, const char* reason)
283
{
16✔
284
    realm_sync_socket_op_complete(write_handler, result, reason);
16✔
285
}
16✔
286

287
RLM_API void realm_sync_socket_websocket_connected(realm_websocket_observer_t* realm_websocket_observer,
288
                                                   const char* protocol)
289
{
2✔
290
    if (realm_websocket_observer)
2✔
291
        realm_websocket_observer->get()->websocket_connected_handler(protocol);
2✔
292
}
2✔
293

294
RLM_API void realm_sync_socket_websocket_error(realm_websocket_observer_t* realm_websocket_observer)
295
{
×
NEW
296
    if (realm_websocket_observer)
×
NEW
297
        realm_websocket_observer->get()->websocket_error_handler();
×
UNCOV
298
}
×
299

300
RLM_API bool realm_sync_socket_websocket_message(realm_websocket_observer_t* realm_websocket_observer,
301
                                                 const char* data, size_t data_size)
302
{
9✔
303
    if (!realm_websocket_observer)
9✔
NEW
304
        return false;
×
305

5✔
306
    return realm_websocket_observer->get()->websocket_binary_message_received(util::Span{data, data_size});
9✔
307
}
9✔
308

309
RLM_API bool realm_sync_socket_websocket_closed(realm_websocket_observer_t* realm_websocket_observer, bool was_clean,
310
                                                realm_web_socket_errno_e code, const char* reason)
311
{
×
NEW
312
    if (!realm_websocket_observer)
×
NEW
313
        return false;
×
314

NEW
315
    return realm_websocket_observer->get()->websocket_closed_handler(
×
NEW
316
        was_clean, static_cast<sync::websocket::WebSocketError>(code), reason);
×
UNCOV
317
}
×
318

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

325
} // 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