Uploaded image for project: 'C++ Driver'
  1. C++ Driver
  2. CXX-1245

Guarantee thread safety for const methods in mongocxx API

    • Type: Icon: New Feature New Feature
    • Resolution: Unresolved
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: None
    • Component/s: API
    • Labels:
      None

      In order to make our mongocxx types compatible for use with the C++11 standard library, we must ensure that all const methods are thread safe (GotW #6a, You Don't Know "const" and "mutable"). Currently, many const methods on mongocxx types are not guaranteed to be thread safe because they call into the libmongoc API, which does not currently provide this thread safety guarantee.

      See the following excerpt from collection.cpp for an example. The thread safety of const method collection::read_preference() currently cannot be guaranteed, since it calls into libmongoc::read_prefs_copy() and libmongoc::collection_get_read_prefs(mongoc_collection_t*), which do not have their thread safety guaranteed:

         986	class read_preference collection::read_preference() const {
         987	    class read_preference rp(stdx::make_unique<read_preference::impl>(libmongoc::read_prefs_copy(
         988	        libmongoc::collection_get_read_prefs(_get_impl().collection_t))));
         989	    return rp;
         990	}
         991
      

      Many affected methods can be viewed by looking for calls to libmongoc/libbson in the output of git grep -A 4 "const {". Most of the affected mongocxx methods are getters, and most of the libmongoc functions they depend on are either:

      • libmongoc getters, like mongoc_collection_get_read_prefs()
      • libmongoc "copy constructors", like mongoc_read_prefs_copy()

      To fix this issue, we have several options:

      1. The C driver documents that certain API functions are thread safe (and does the implementation work required to make this happen, if any). The list of API functions might be as simple as all functions that match either mongoc_*_get_*() or mongoc_*_copy(), but we'd need further investigation to put together the official list.
      2. We remove all libmongoc functionality from the implementations of our existing const methods. To do this, we would change getters like collection::read_preference() to return a locally-cached copy of their read preference. If we do this, we would want to ensure ample test coverage of these changes to ensure that we're exactly replicating mongoc's equivalent functionality (and that we trigger test failures if mongoc ever changes it).
      3. We synchronize our calls into libmongoc. This approach has clear performance drawbacks.
      4. We remove "const" from all methods that call into mongoc functionality. This approach makes our API slightly less intuitive, and, in a way, leaks the fact that we're wrapping libmongoc.
      5. We document our types as not compatible with standard library functions when accessed without synchronization from multiple threads (essentially putting the synchronization onus on the user). This may also require prohibiting users from using mongocxx with any standard library implementation that uses multi-threaded algorithms (like libstdc++'s experimental parallel mode), since we already use mongocxx types internally with the standard library.

            Assignee:
            Unassigned Unassigned
            Reporter:
            rassi J Rassi
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: