Skip to content

Commit 41113df

Browse files
authored
CDRIVER-4736 add an api to set custom sleep function (#1442)
1 parent 4ef84ec commit 41113df

File tree

7 files changed

+161
-3
lines changed

7 files changed

+161
-3
lines changed

src/libmongoc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@ set (HEADERS
695695
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-server-api.h
696696
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-server-description.h
697697
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-client-session.h
698+
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-sleep.h
698699
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-socket.h
699700
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-stream-tls-libressl.h
700701
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-stream-tls-openssl.h
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include "mongoc-prelude.h"
2+
3+
#ifndef MONGOC_SLEEP_H
4+
#define MONGOC_SLEEP_H
5+
6+
#include <bson/bson.h>
7+
8+
#include "mongoc-macros.h"
9+
10+
BSON_BEGIN_DECLS
11+
12+
/**
13+
* mongoc_usleep_func_t:
14+
* @usec: Number of microseconds to sleep for.
15+
* @user_data: User data provided to mongoc_client_set_usleep_impl().
16+
*/
17+
typedef void (*mongoc_usleep_func_t) (int64_t usec, void *user_data);
18+
19+
/**
20+
* mongoc_client_set_usleep_impl:
21+
* @usleep_func: A function to perform microsecond sleep.
22+
*
23+
* Sets the function to be called to perform sleep during scanning.
24+
* Returns the old function.
25+
* If old_user_data is not NULL, *old_user_data is set to the old user_data.
26+
* Not thread-safe.
27+
* Providing a `usleep_func` that does not sleep (e.g. coroutine suspension) is
28+
* not supported. Doing so is at the user's own risk.
29+
*/
30+
MONGOC_EXPORT (void)
31+
mongoc_client_set_usleep_impl (mongoc_client_t *client,
32+
mongoc_usleep_func_t usleep_func,
33+
void *user_data);
34+
35+
MONGOC_EXPORT (void)
36+
mongoc_usleep_default_impl (int64_t usec, void *user_data);
37+
38+
BSON_END_DECLS
39+
40+
#endif /* MONGOC_SLEEP_H */

src/libmongoc/src/mongoc/mongoc-topology-private.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "mongoc-crypt-private.h"
3030
#include "mongoc-ts-pool-private.h"
3131
#include "mongoc-shared-private.h"
32+
#include "mongoc-sleep.h"
3233

3334
#define MONGOC_TOPOLOGY_MIN_HEARTBEAT_FREQUENCY_MS 500
3435
#define MONGOC_TOPOLOGY_SOCKET_CHECK_INTERVAL_MS 5000
@@ -212,6 +213,11 @@ typedef struct _mongoc_topology_t {
212213
* topology. This could occur if the URI is invalid.
213214
* An invalid topology does not monitor servers. */
214215
bool valid;
216+
217+
// `usleep_fn` and `usleep_data` may be overridden by
218+
// `mongoc_client_set_usleep_impl`.
219+
mongoc_usleep_func_t usleep_fn;
220+
void *usleep_data;
215221
} mongoc_topology_t;
216222

217223
mongoc_topology_t *

src/libmongoc/src/mongoc/mongoc-topology.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ mongoc_topology_new (const mongoc_uri_t *uri, bool single_threaded)
405405
#endif
406406

407407
topology = (mongoc_topology_t *) bson_malloc0 (sizeof *topology);
408+
topology->usleep_fn = mongoc_usleep_default_impl;
408409
topology->session_pool =
409410
mongoc_server_session_pool_new_with_params (_server_session_init,
410411
_server_session_destroy,
@@ -1263,8 +1264,7 @@ mongoc_topology_select_server_id (mongoc_topology_t *topology,
12631264
server_id = 0;
12641265
goto done;
12651266
}
1266-
1267-
_mongoc_usleep (sleep_usec);
1267+
topology->usleep_fn (sleep_usec, topology->usleep_data);
12681268
}
12691269

12701270
/* takes up to connectTimeoutMS. sets "last_scan", clears "stale" */

src/libmongoc/src/mongoc/mongoc-util.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "mongoc-client-private.h" // WIRE_VERSION_* macros.
3131
#include "mongoc-client-session-private.h"
3232
#include "mongoc-trace-private.h"
33+
#include "mongoc-sleep.h"
3334

3435
const bson_validate_flags_t _mongoc_default_insert_vflags =
3536
BSON_VALIDATE_UTF8 | BSON_VALIDATE_UTF8_ALLOW_NULL |
@@ -84,10 +85,20 @@ _mongoc_hex_md5 (const char *input)
8485
return bson_strdup (digest_str);
8586
}
8687

88+
void
89+
mongoc_client_set_usleep_impl (mongoc_client_t *client,
90+
mongoc_usleep_func_t usleep_func,
91+
void *user_data)
92+
{
93+
client->topology->usleep_fn = usleep_func;
94+
client->topology->usleep_data = user_data;
95+
}
8796

8897
void
89-
_mongoc_usleep (int64_t usec)
98+
mongoc_usleep_default_impl (int64_t usec, void *user_data)
9099
{
100+
BSON_UNUSED (user_data);
101+
91102
#ifdef _WIN32
92103
LARGE_INTEGER ft;
93104
HANDLE timer;
@@ -105,6 +116,13 @@ _mongoc_usleep (int64_t usec)
105116
#endif
106117
}
107118

119+
void
120+
_mongoc_usleep (int64_t usec)
121+
{
122+
mongoc_usleep_default_impl (usec, NULL);
123+
}
124+
125+
108126
int64_t
109127
_mongoc_get_real_time_ms (void)
110128
{

src/libmongoc/src/mongoc/mongoc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "mongoc-handshake.h"
4848
#include "mongoc-opcode.h"
4949
#include "mongoc-log.h"
50+
#include "mongoc-sleep.h"
5051
#include "mongoc-socket.h"
5152
#include "mongoc-client-session.h"
5253
#include "mongoc-stream.h"

src/libmongoc/tests/test-mongoc-usleep.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
#include <mongoc/mongoc.h>
12
#include "mongoc/mongoc-util-private.h"
23
#include "TestSuite.h"
34
#include "test-libmongoc.h"
5+
#include <mock_server/mock-server.h>
6+
#include <mock_server/future.h>
7+
#include <mock_server/future-functions.h>
8+
#include <test-conveniences.h> // tmp_bson
49

510

611
static void
@@ -16,8 +21,95 @@ test_mongoc_usleep_basic (void)
1621
ASSERT_CMPTIME ((int) duration, 200 * 1000);
1722
}
1823

24+
static void
25+
custom_usleep_impl (int64_t usec, void *user_data)
26+
{
27+
if (user_data) {
28+
*(int64_t *) user_data = usec;
29+
}
30+
}
31+
32+
33+
// `test_mongoc_usleep_custom` tests a custom sleep function set in
34+
// `mongoc_client_set_usleep_impl` is applied when topology scanning sleeps.
35+
static void
36+
test_mongoc_usleep_custom (void)
37+
{
38+
mock_server_t *server = mock_server_new ();
39+
mock_server_run (server);
40+
41+
mongoc_uri_t *uri = mongoc_uri_copy (mock_server_get_uri (server));
42+
// Tell single-threaded clients to reconnect if an error occcurs.
43+
mongoc_uri_set_option_as_bool (
44+
uri, MONGOC_URI_SERVERSELECTIONTRYONCE, false);
45+
46+
mongoc_client_t *client = test_framework_client_new_from_uri (uri, NULL);
47+
ASSERT (client);
48+
49+
// Bypass the five second cooldown to speed up test.
50+
_mongoc_topology_bypass_cooldown (client->topology);
51+
// Override `min_heartbeat_frequency_msec` to speed up test.
52+
client->topology->min_heartbeat_frequency_msec = 50;
53+
54+
// Override the sleep.
55+
int64_t last_sleep_dur = 0;
56+
mongoc_client_set_usleep_impl (client, custom_usleep_impl, &last_sleep_dur);
57+
58+
bson_error_t error;
59+
future_t *future = future_client_command_simple (client,
60+
"db",
61+
tmp_bson ("{'ping': 1}"),
62+
NULL /* read prefs */,
63+
NULL,
64+
&error);
65+
66+
// Client sends initial `isMaster`.
67+
{
68+
request_t *req = mock_server_receives_any_hello (server);
69+
ASSERT (req);
70+
// Fail the request.
71+
reply_to_request_with_hang_up (req);
72+
request_destroy (req);
73+
}
74+
75+
// Client sleeps for `min_heartbeat_frequency_msec`, then sends another
76+
// `isMaster`.
77+
{
78+
request_t *req = mock_server_receives_any_hello (server);
79+
ASSERT (req);
80+
reply_to_request_simple (
81+
req,
82+
tmp_str ("{ 'minWireVersion': %d, 'maxWireVersion' : %d, "
83+
"'isWritablePrimary': true}",
84+
WIRE_VERSION_MIN,
85+
WIRE_VERSION_MAX));
86+
request_destroy (req);
87+
}
88+
89+
// Expect custom sleep to have been called between making `isMaster` calls.
90+
ASSERT_CMPINT64 (last_sleep_dur, >, 0);
91+
92+
// Client sends "ping".
93+
{
94+
request_t *req = mock_server_receives_msg (
95+
server, MONGOC_MSG_NONE, tmp_bson ("{'ping': 1}"));
96+
ASSERT (req);
97+
reply_to_request_with_ok_and_destroy (req);
98+
}
99+
100+
bool ok = future_wait (future);
101+
ASSERT_OR_PRINT (ok, error);
102+
103+
future_destroy (future);
104+
mongoc_client_destroy (client);
105+
mongoc_uri_destroy (uri);
106+
mock_server_destroy (server);
107+
}
108+
19109
void
20110
test_usleep_install (TestSuite *suite)
21111
{
22112
TestSuite_Add (suite, "/Sleep/basic", test_mongoc_usleep_basic);
113+
TestSuite_AddMockServerTest (
114+
suite, "/Sleep/custom", test_mongoc_usleep_custom);
23115
}

0 commit comments

Comments
 (0)