Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] xmp thread safety #3011

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions include/exiv2/xmp_exiv2.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#define XMP_HPP_

// *****************************************************************************
#include <atomic>
#include "exiv2lib_export.h"

// included header files
Expand Down Expand Up @@ -355,7 +356,9 @@ class EXIV2API XmpParser {

@return True if the initialization was successful, else false.
*/
static bool initialize(XmpParser::XmpLockFct xmpLockFct = nullptr, void* pLockData = nullptr);
[[deprecated("xmpLockFct is no longer required")]] static bool initialize(XmpParser::XmpLockFct xmpLockFct, void* pLockData);

static bool initialize();
/*!
@brief Terminate the XMP Toolkit and unregister custom namespaces.

Expand All @@ -365,6 +368,9 @@ class EXIV2API XmpParser {
static void terminate();

private:

static bool initialize_unsafe();

/*!
@brief Register a namespace with the XMP Toolkit.
*/
Expand All @@ -382,9 +388,8 @@ class EXIV2API XmpParser {
static void registeredNamespaces(Exiv2::Dictionary&);

// DATA
static bool initialized_; //! Indicates if the XMP Toolkit has been initialized
static XmpLockFct xmpLockFct_;
static void* pLockData_;
static std::atomic_bool initialized_; //! Indicates if the XMP Toolkit has been initialized
static std::mutex global_mutex_; // meant to synchronize all xmp actions across all threads

friend class XmpProperties; // permit XmpProperties -> registerNs() and registeredNamespaces()

Expand Down
34 changes: 22 additions & 12 deletions src/xmp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -504,15 +504,12 @@ void XmpData::eraseFamily(XmpData::iterator& pos) {
}
}

bool XmpParser::initialized_ = false;
XmpParser::XmpLockFct XmpParser::xmpLockFct_ = nullptr;
void* XmpParser::pLockData_ = nullptr;
std::atomic_bool XmpParser::initialized_ = false;
std::mutex XmpParser::global_mutex_;

#ifdef EXV_HAVE_XMP_TOOLKIT
bool XmpParser::initialize(XmpParser::XmpLockFct xmpLockFct, void* pLockData) {
bool XmpParser::initialize_unsafe() {
if (!initialized_) {
xmpLockFct_ = xmpLockFct;
pLockData_ = pLockData;
initialized_ = SXMPMeta::Initialize();
#ifdef EXV_ADOBE_XMPSDK
SXMPMeta::RegisterNamespace("http://ns.adobe.com/lightroom/1.0/", "lr", nullptr);
Expand Down Expand Up @@ -565,12 +562,21 @@ bool XmpParser::initialize(XmpParser::XmpLockFct xmpLockFct, void* pLockData) {
return initialized_;
}
#else
bool XmpParser::initialize(XmpParser::XmpLockFct, void*) {
bool XmpParser::initialize_unsafe(XmpParser::XmpLockFct, void*) {
initialized_ = true;
return initialized_;
}
#endif

bool XmpParser::initialize([[maybe_unused]] XmpParser::XmpLockFct xmpLockFct, [[maybe_unused]] void* pLockData) {
return initialize();
}

bool XmpParser::initialize() {
auto scopedWriteLock = std::scoped_lock(XmpParser::global_mutex_);
return initialize_unsafe();
}

#ifdef EXV_HAVE_XMP_TOOLKIT
static XMP_Status nsDumper(void* refCon, XMP_StringPtr buffer, XMP_StringLen bufferSize) {
XMP_Status result = 0;
Expand Down Expand Up @@ -607,7 +613,8 @@ void XmpParser::registeredNamespaces(Exiv2::Dictionary& dict) {
bool bInit = !initialized_;
try {
if (bInit)
initialize();
bInit = initialize_unsafe();
auto scopedWriteLock = std::scoped_lock(XmpParser::global_mutex_);
SXMPMeta::DumpNamespaces(nsDumper, &dict);
if (bInit)
terminate();
Expand All @@ -633,8 +640,7 @@ void XmpParser::terminate() {
#ifdef EXV_HAVE_XMP_TOOLKIT
void XmpParser::registerNs(const std::string& ns, const std::string& prefix) {
try {
initialize();
AutoLock autoLock(xmpLockFct_, pLockData_);
initialize_unsafe();
SXMPMeta::DeleteNamespace(ns.c_str());
#ifdef EXV_ADOBE_XMPSDK
SXMPMeta::RegisterNamespace(ns.c_str(), prefix.c_str(), nullptr);
Expand Down Expand Up @@ -665,12 +671,14 @@ void XmpParser::unregisterNs(const std::string& /*ns*/) {
#ifdef EXV_HAVE_XMP_TOOLKIT
int XmpParser::decode(XmpData& xmpData, const std::string& xmpPacket) {
try {
auto scopedWriteLock = std::scoped_lock(XmpParser::global_mutex_);

xmpData.clear();
xmpData.setPacket(xmpPacket);
if (xmpPacket.empty())
return 0;

if (!initialize()) {
if (!initialize_unsafe()) {
#ifndef SUPPRESS_WARNINGS
EXV_ERROR << "XMP toolkit initialization failed.\n";
#endif
Expand Down Expand Up @@ -809,12 +817,14 @@ int XmpParser::decode(XmpData& xmpData, const std::string& xmpPacket) {
#ifdef EXV_HAVE_XMP_TOOLKIT
int XmpParser::encode(std::string& xmpPacket, const XmpData& xmpData, uint16_t formatFlags, uint32_t padding) {
try {
auto scopedWriteLock = std::scoped_lock(XmpParser::global_mutex_);

if (xmpData.empty()) {
xmpPacket.clear();
return 0;
}

if (!initialize()) {
if (!initialize_unsafe()) {
#ifndef SUPPRESS_WARNINGS
EXV_ERROR << "XMP toolkit initialization failed.\n";
#endif
Expand Down
Loading