Skip to content

Commit 5d63187

Browse files
MP4: Fix data race in ItemFactory lazy map initialization (#1331)
Concurrent calls to propertyKeyForName() and handlerTypeForName() (e.g. via batchMap during import) could race on the isEmpty() guard used for first-call lazy initialization. Replace isEmpty() guards with std::call_once / std::once_flag so that each map is initialized exactly once in a thread-safe manner. Using call_once (rather than eager construction in the base class constructor) preserves virtual dispatch, allowing ItemFactory subclasses to override nameHandlerMap() and namePropertyMap() correctly. Both property maps are initialized together in a single once_flag since nameForPropertyKey is derived from namePropertyMap.
1 parent f32b503 commit 5d63187

1 file changed

Lines changed: 13 additions & 11 deletions

File tree

taglib/mp4/mp4itemfactory.cpp

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "mp4itemfactory.h"
2727

28+
#include <mutex>
2829
#include <utility>
2930

3031
#include "tbytevector.h"
@@ -47,6 +48,8 @@ class ItemFactory::ItemFactoryPrivate
4748
NameHandlerMap handlerTypeForName;
4849
Map<ByteVector, String> propertyKeyForName;
4950
Map<String, ByteVector> nameForPropertyKey;
51+
mutable std::once_flag handlerMapOnce;
52+
mutable std::once_flag propertyMapsOnce;
5053
};
5154

5255
ItemFactory ItemFactory::factory;
@@ -239,9 +242,11 @@ std::pair<String, StringList> ItemFactory::itemToProperty(
239242

240243
String ItemFactory::propertyKeyForName(const ByteVector &name) const
241244
{
242-
if(d->propertyKeyForName.isEmpty()) {
245+
std::call_once(d->propertyMapsOnce, [this] {
243246
d->propertyKeyForName = namePropertyMap();
244-
}
247+
for(const auto &[k, t] : std::as_const(d->propertyKeyForName))
248+
d->nameForPropertyKey[t] = k;
249+
});
245250
String key = d->propertyKeyForName.value(name);
246251
if(key.isEmpty() && name.startsWith(freeFormPrefix)) {
247252
key = name.mid(std::size(freeFormPrefix) - 1);
@@ -251,14 +256,11 @@ String ItemFactory::propertyKeyForName(const ByteVector &name) const
251256

252257
ByteVector ItemFactory::nameForPropertyKey(const String &key) const
253258
{
254-
if(d->nameForPropertyKey.isEmpty()) {
255-
if(d->propertyKeyForName.isEmpty()) {
256-
d->propertyKeyForName = namePropertyMap();
257-
}
258-
for(const auto &[k, t] : std::as_const(d->propertyKeyForName)) {
259+
std::call_once(d->propertyMapsOnce, [this] {
260+
d->propertyKeyForName = namePropertyMap();
261+
for(const auto &[k, t] : std::as_const(d->propertyKeyForName))
259262
d->nameForPropertyKey[t] = k;
260-
}
261-
}
263+
});
262264
ByteVector name = d->nameForPropertyKey.value(key);
263265
if(name.isEmpty() && !key.isEmpty()) {
264266
const auto &firstChar = key[0];
@@ -317,9 +319,9 @@ ItemFactory::NameHandlerMap ItemFactory::nameHandlerMap() const
317319
ItemFactory::ItemHandlerType ItemFactory::handlerTypeForName(
318320
const ByteVector &name) const
319321
{
320-
if(d->handlerTypeForName.isEmpty()) {
322+
std::call_once(d->handlerMapOnce, [this] {
321323
d->handlerTypeForName = nameHandlerMap();
322-
}
324+
});
323325
auto type = d->handlerTypeForName.value(name, ItemHandlerType::Unknown);
324326
if (type == ItemHandlerType::Unknown && name.size() == 4) {
325327
type = ItemHandlerType::Text;

0 commit comments

Comments
 (0)