is boost::property_tree::ptree thread safe?

Because boost json parser depends on boost::spirit, and spirit is not thread-safety default.

You can add this macro before any ptree header file to resolve it.

#define BOOST_SPIRIT_THREADSAFE
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

TL;DR:

My suggestion: use the atomic swap idiom

ptree my_shared;
mutex shared_ptree_lock;

{
    ptree parsed;     // temporary
    read_json(ss,pt); // this may take a while (or even fail)

    lock_guard hold(shared_ptree_lock);
    std::swap(pt, my_shared); // swap under lock
}

Now, whether you need to lock the shared tree before reading, depends on your knowledge of the threading context (in other words, depends whether you know your tree can be being modified at the same time).

To make things insanely flexible, do the same through shared_ptr<ptree> - but this will bear considerable overhead. The pre is, that with the swap idiom, you won't have to lock things on the read side, since readers will happily continue reading the old tree, and if they are done reading and release the shared_ptr it will get destroyed in the end.


I'm not completely sure what you expect. With the property tree being accessed for writing from two threads is never going to be thread-safe without locking. Therefore, I assume that you mean, is property tree threadsafe for reading while simultaneously parsing it somewhere else.

Here, my primary expectation is: no. C++ has a culture of 'pay-for-what-you-need' you won't see any thread-safe general-purpose classes. There would be the option of

  • a preprocessor #define to switch on thread-safety
  • a policy template parameter that govern's the behaviour

After looking at the source code, surprisingly, it looks as if it was almost thread safe. But not fully :)

It appears that there is no #define or flag to set to make property tree thread safe, so you're stuck with locking.

Rationale:

Looking at internal_read_json I see that it only accesses the stream (which should be private to this reader anyway, as sharing streams across multiple (concurrent) users is hardly ever useful1), and then, very correctly, only swaps the ptree's (pt) root node out with the parser's context tree.

Obviously, the atomic swap functionality is mainly there for exception safety (you don't want to change your ptree if an exception occurred halfway parsing the JSON). However, IFF the swap operation were to be thread-safe, this would also make the access to pt thread-safe.

Alas, onto the ptree_implementation, we see that the swap is not threadsafe:

template<class K, class D, class C> inline
void basic_ptree<K, D, C>::swap(basic_ptree<K, D, C> &rhs)
{
    m_data.swap(rhs.m_data);
    // Void pointers, no ADL necessary
    std::swap(m_children, rhs.m_children);
}

For one, you could have a race condition between swapping m_data and m_children, further more, the swaps are standard, not atomic swaps.


1besides istringstream obviously not being thread-safe as it is a C++98 standard library class