#include "unicode/numberformatter.h"
#include "number_decimalquantity.h"
#include "number_formatimpl.h"
+#include "umutex.h"
using namespace icu;
using namespace icu::number;
}
template<typename Derived>
-Derived NumberFormatterSettings<Derived>::threshold(uint32_t threshold) const {
+Derived NumberFormatterSettings<Derived>::threshold(int32_t threshold) const {
Derived copy(*this);
copy.fMacros.threshold = threshold;
return copy;
}
LocalizedNumberFormatter::~LocalizedNumberFormatter() {
- delete fCompiled.load();
+ delete fCompiled;
}
FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode &status) const {
FormattedNumber
LocalizedNumberFormatter::formatImpl(impl::NumberFormatterResults *results, UErrorCode &status) const {
- uint32_t currentCount = fCallCount.load();
- if (currentCount <= fMacros.threshold && fMacros.threshold > 0) {
- currentCount = const_cast<LocalizedNumberFormatter *>(this)->fCallCount.fetch_add(1) + 1;
+ // fUnsafeCallCount contains memory to be interpreted as an atomic int, most commonly
+ // std::atomic<int32_t>. Since the type of atomic int is platform-dependent, we cast the
+ // bytes in fUnsafeCallCount to u_atomic_int32_t, a typedef for the platform-dependent
+ // atomic int type defined in umutex.h.
+ static_assert(sizeof(u_atomic_int32_t) <= sizeof(fUnsafeCallCount),
+ "Atomic integer size on this platform exceeds the size allocated by fUnsafeCallCount");
+ u_atomic_int32_t* callCount = reinterpret_cast<u_atomic_int32_t*>(
+ const_cast<LocalizedNumberFormatter*>(this)->fUnsafeCallCount);
+
+ // A positive value in the atomic int indicates that the data structure is not yet ready;
+ // a negative value indicates that it is ready. If, after the increment, the atomic int
+ // is exactly threshold, then it is the current thread's job to build the data structure.
+ // Note: We set the callCount to INT32_MIN so that if another thread proceeds to increment
+ // the atomic int, the value remains below zero.
+ int32_t currentCount = umtx_loadAcquire(*callCount);
+ if (0 <= currentCount && currentCount <= fMacros.threshold && fMacros.threshold > 0) {
+ currentCount = umtx_atomic_inc(callCount);
}
- const NumberFormatterImpl *compiled;
+
if (currentCount == fMacros.threshold && fMacros.threshold > 0) {
- compiled = NumberFormatterImpl::fromMacros(fMacros, status);
- U_ASSERT(fCompiled.load() == nullptr);
- const_cast<LocalizedNumberFormatter *>(this)->fCompiled.store(compiled);
- compiled->apply(results->quantity, results->string, status);
- } else if ((compiled = fCompiled.load()) != nullptr) {
+ // Build the data structure and then use it (slow to fast path).
+ const NumberFormatterImpl* compiled =
+ NumberFormatterImpl::fromMacros(fMacros, status);
+ U_ASSERT(fCompiled == nullptr);
+ const_cast<LocalizedNumberFormatter *>(this)->fCompiled = compiled;
+ umtx_storeRelease(*callCount, INT32_MIN);
compiled->apply(results->quantity, results->string, status);
+ } else if (currentCount < 0) {
+ // The data structure is already built; use it (fast path).
+ U_ASSERT(fCompiled != nullptr);
+ fCompiled->apply(results->quantity, results->string, status);
} else {
+ // Format the number without building the data structure (slow path).
NumberFormatterImpl::applyStatic(fMacros, results->quantity, results->string, status);
}
#ifndef __NUMBERFORMATTER_H__
#define __NUMBERFORMATTER_H__
-#include <atomic>
-
#include "unicode/appendable.h"
#include "unicode/dcfmtsym.h"
#include "unicode/currunit.h"
*
* @internal
*/
-static uint32_t DEFAULT_THRESHOLD = 3;
+static constexpr int32_t DEFAULT_THRESHOLD = 3;
/** @internal */
class U_I18N_API SymbolsWrapper : public UMemory {
PluralRules *rules = nullptr; // no ownership
/** @internal */
- uint32_t threshold = DEFAULT_THRESHOLD;
+ int32_t threshold = DEFAULT_THRESHOLD;
Locale locale;
/**
*
* @internal ICU 60: This API is ICU internal only.
*/
- Derived threshold(uint32_t threshold) const;
+ Derived threshold(int32_t threshold) const;
#endif /* U_HIDE_INTERNAL_API */
~LocalizedNumberFormatter();
private:
- UPRV_SUPPRESS_DLL_INTERFACE_WARNING // Member is private and does not need to be exported
- std::atomic<const impl::NumberFormatterImpl *> fCompiled{nullptr};
- UPRV_SUPPRESS_DLL_INTERFACE_WARNING // Member is private and does not need to be exported
- std::atomic<uint32_t> fCallCount{0};
+ const impl::NumberFormatterImpl* fCompiled {nullptr};
+ char fUnsafeCallCount[8] {}; // internally cast to u_atomic_int32_t
LocalizedNumberFormatter() = default;