Skip to content

Commit 2135897

Browse files
CDRIVER-4147 A reference-counted shared pointer abstraction (#858)
* A shared-pointer abstraction * Make mongoc-shared a private API * Test aliasing to subobject
1 parent 0456d9a commit 2135897

File tree

7 files changed

+506
-1
lines changed

7 files changed

+506
-1
lines changed

src/libbson/src/bson/bson-macros.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,5 +335,4 @@
335335
abort (); \
336336
} while (0)
337337

338-
339338
#endif /* BSON_MACROS_H */

src/libmongoc/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ set (SOURCES ${SOURCES}
543543
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-client-session.c
544544
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-server-monitor.c
545545
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-set.c
546+
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-shared.c
546547
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-socket.c
547548
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-stream-buffered.c
548549
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-stream.c
@@ -607,6 +608,7 @@ set (HEADERS
607608
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-server-api.h
608609
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-server-description.h
609610
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-client-session.h
611+
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-shared-private.h
610612
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-socket.h
611613
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-stream-tls-libressl.h
612614
${PROJECT_SOURCE_DIR}/src/mongoc/mongoc-stream-tls-openssl.h
@@ -989,6 +991,7 @@ set (test-libmongoc-sources
989991
${PROJECT_SOURCE_DIR}/tests/test-mongoc-server-selection.c
990992
${PROJECT_SOURCE_DIR}/tests/test-mongoc-server-stream.c
991993
${PROJECT_SOURCE_DIR}/tests/test-mongoc-set.c
994+
${PROJECT_SOURCE_DIR}/tests/test-mongoc-shared.c
992995
${PROJECT_SOURCE_DIR}/tests/test-mongoc-socket.c
993996
${PROJECT_SOURCE_DIR}/tests/test-mongoc-speculative-auth.c
994997
${PROJECT_SOURCE_DIR}/tests/test-mongoc-stream.c

src/libmongoc/src/mongoc/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ set (src_libmongoc_src_mongoc_DIST_noinst_hs
148148
mongoc-server-stream-private.h
149149
mongoc-server-monitor-private.h
150150
mongoc-set-private.h
151+
mongoc-shared-private.h
151152
mongoc-socket-private.h
152153
mongoc-ssl-private.h
153154
mongoc-sspi-private.h
@@ -241,6 +242,7 @@ set (src_libmongoc_src_mongoc_DIST_cs
241242
mongoc-client-session.c
242243
mongoc-set.c
243244
mongoc-server-monitor.c
245+
mongoc-shared.c
244246
mongoc-socket.c
245247
mongoc-stream.c
246248
mongoc-stream-buffered.c
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/*
2+
* Copyright 2021 MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "mongoc-prelude.h"
18+
19+
#ifndef MONGOC_SHARED_H
20+
#define MONGOC_SHARED_H
21+
22+
#include <stddef.h>
23+
24+
/**
25+
* @brief A ref-counted thread-safe shared pointer to arbitrary data.
26+
*
27+
* `shared_ptr` instances manage the lifetime of a pointed-to object when the
28+
* precise time to destroy that managed object is indeterminate, such as with
29+
* shared state in a multithreaded or asynchronous program.
30+
*
31+
* The pointed-to object of a shared_ptr instance can be accessed via
32+
* the `ptr` member of the shared_ptr object. Assigning-to the `ptr` member of
33+
* a shared_ptr is legal, and can be done to change the pointed-to object of a
34+
* shared_ptr without changing which object is being managed. This can be done
35+
* to return a pointer to a subobject of the managed object without giving
36+
* out a reference to the full managed object.
37+
*
38+
* A new managed object with a `shared_ptr` is created with
39+
* `mongoc_shared_ptr_create`, which starts with an initial shared reference
40+
* count of `1`. To take another reference to keep the managed object alive,
41+
* use `mongoc_shared_ptr_copy`. When one is done with a managed resource, the
42+
* shared reference should be dropped using `mongoc_shared_ptr_reset_null`.
43+
*
44+
* When an operation on a shared_ptr causes the reference count to drop to zero,
45+
* the deleter that was given to create that shared state will immediately be
46+
* invoked with the pointed-to-data. The deleter runs in the thread that
47+
* caused the reference count to drop, so be aware that resetting or assigning
48+
* a shared_ptr can execute unseen code: Refrain from holding locks while
49+
* resetting/assigning a shared pointer.
50+
*/
51+
typedef struct mongoc_shared_ptr {
52+
/** Pointed-to data */
53+
void *ptr;
54+
/** Auxilary book-keeping. Do not touch. */
55+
struct _mongoc_shared_ptr_aux *_aux;
56+
} mongoc_shared_ptr;
57+
58+
/**
59+
* @brief A "null" pointer constant for a mongoc_shared_ptr.
60+
*/
61+
#define MONGOC_SHARED_PTR_NULL \
62+
{ \
63+
NULL, NULL \
64+
}
65+
66+
/**
67+
* @brief Reassign a shared pointer to manage the given resource
68+
*
69+
* @param ptr The shared pointer that will be rebound
70+
* @param pointee The pointer that we will point to.
71+
* @param deleter A deleter for `pointee`, to be called when the refcount
72+
* reaches zero.
73+
*
74+
* @note Equivalent to:
75+
*
76+
* mongoc_shared_ptr_reset_null(ptr);
77+
* *ptr = mongoc_shared_ptr_create(pointee, deleter);
78+
*/
79+
extern void
80+
mongoc_shared_ptr_reset (mongoc_shared_ptr *ptr,
81+
void *pointee,
82+
void (*deleter) (void *));
83+
84+
/**
85+
* @brief Reassign the given shared pointer to manage the same resource as
86+
* 'from'
87+
*
88+
* If `dest` manages an existing object, the reference count of that managed
89+
* object will be decremented. If this causes its refcount to reach zero, then
90+
* the deleter function will be executed with the pointee.
91+
*
92+
* @param dest The shared pointer to change
93+
* @param from The shared pointer to take from
94+
*
95+
* @note Equivalent to:
96+
*
97+
* mongoc_shared_ptr_reset_null(dest);
98+
* *dest = mongoc_shared_ptr_copy(from);
99+
*/
100+
extern void
101+
mongoc_shared_ptr_assign (mongoc_shared_ptr *dest, mongoc_shared_ptr from);
102+
103+
/**
104+
* @brief Reassign the given shared pointer to manage the same resource as
105+
* 'from'
106+
*
107+
* This atomic function is safe to call between threads when 'dest' may be
108+
* accessed simultaneously from another thread even if any of those accesses are
109+
* a write. However: Any potential reads *must* be done using
110+
* `mongoc_atomic_shared_ptr_load` and any potential writes *must* be done using
111+
* `mongoc_atomic_shared_ptr_store`.
112+
*
113+
* @param dest The shared pointer to change
114+
* @param from The shared pointer to take from
115+
*
116+
* Thread-safe equivalent of `mongoc_shared_ptr_assign`
117+
*/
118+
extern void
119+
mongoc_atomic_shared_ptr_store (mongoc_shared_ptr *dest,
120+
mongoc_shared_ptr from);
121+
122+
/**
123+
* @brief Create a copy of the given shared pointer. Increases the reference
124+
* count on the object.
125+
*
126+
* @param ptr The pointer to copy from
127+
* @returns a new shared pointer that has the same pointee as ptr
128+
*
129+
* @note Must later reset/reassign the returned shared pointer
130+
*/
131+
extern mongoc_shared_ptr
132+
mongoc_shared_ptr_copy (mongoc_shared_ptr ptr);
133+
134+
/**
135+
* @brief Like `mongoc_shared_ptr_copy`, but is thread-safe in case `*ptr`
136+
* may be accessed simultaneously from other threads even if any of those
137+
* accesses are writes. However: such potential writes *must* use
138+
* `mongoc_atomic_shared_ptr_store`.
139+
*
140+
* This is a thread-safe equivalent of `mongoc_shared_ptr_copy`.
141+
*
142+
* @note Must later reset/reassign the returned shared pointer
143+
*/
144+
extern mongoc_shared_ptr
145+
mongoc_atomic_shared_ptr_load (mongoc_shared_ptr const *ptr);
146+
147+
/**
148+
* @brief Release the ownership of the given shared pointer.
149+
*
150+
* The shared pointer object and ptr->ptr will be reset to NULL, and the
151+
* reference count of the managed object will be decremented.
152+
*
153+
* If this causes the refcount to reach zero, then the deleter function will
154+
* be executed with the pointee.
155+
*
156+
* @param ptr The pointer to release and set to NULL
157+
*
158+
* @note This function is not thread safe if other threads may be
159+
* writing to `ptr` simultaneously. To do a thread-safe null-reset of a shared
160+
* pointer, use mongoc_atomic_shared_ptr_store() with a null
161+
* mongoc_shared_ptr as the 'from' argument
162+
*/
163+
extern void
164+
mongoc_shared_ptr_reset_null (mongoc_shared_ptr *ptr);
165+
166+
/**
167+
* @brief Obtain the number of hard references to the resource managed by the
168+
* given shared pointer. This should only be used for diagnostic and assertive
169+
* purposes.
170+
*
171+
* @param ptr A non-null shared pointer to check
172+
* @return int A positive integer reference count
173+
*/
174+
extern int
175+
mongoc_shared_ptr_use_count (mongoc_shared_ptr ptr);
176+
177+
/**
178+
* @brief Check whether the given shared pointer is managing a resource.
179+
*
180+
* @note The ptr.ptr MAY be NULL while the shared pointer is still managing
181+
* a resource.
182+
*
183+
* @return true If the pointer is managing a resource
184+
* @return false Otherwise
185+
*/
186+
static int
187+
mongoc_shared_ptr_is_null (mongoc_shared_ptr ptr)
188+
{
189+
return ptr._aux == 0;
190+
}
191+
192+
/**
193+
* @brief Create a new shared pointer that manages the given resource, or NULL
194+
*
195+
* @param pointee The target of the pointer. Should be NULL or a dynamically
196+
* allocated data segment
197+
* @param deleter The deleter for the pointer. If `pointee` is non-NULL,
198+
* `deleter` must be non-NULL. This deleter will be called when the reference
199+
* count reaches zero. If should release the resources referred-to by `pointee`.
200+
*/
201+
extern mongoc_shared_ptr
202+
mongoc_shared_ptr_create (void *pointee, void (*deleter) (void *));
203+
204+
#endif /* MONGOC_SHARED_H */

0 commit comments

Comments
 (0)