skia2/include/private/SkOnce.h
mtklein d9dd428211 Modernize and trim down SkOnce.
The API and implementation are very much simplified.
You may not want to bother reading the diff.

As is our trend, SkOnce now uses <atomic> directly.

Member initialization means we don't need SK_DECLARE_STATIC_ONCE.
SkSpinlock already works this same way.

All uses of the old API taking an external bool* and Lock* were pessimal,
so I have not carried this sort of API forward.  It's simpler, faster,
and more space-efficient to always use this single SkOnce class interface.

SkOnce weighs 2 bytes: a done bool and an SkSpinlock, also a bool internally.
This API refactoring opens up the opportunity to fuse those into a single
three-state byte if we'd like.

No public API changes.
TBR=reed@google.com

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1894893002

Review URL: https://codereview.chromium.org/1894893002
2016-04-18 08:09:11 -07:00

41 lines
1.1 KiB
C++

/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkOnce_DEFINED
#define SkOnce_DEFINED
#include "../private/SkSpinlock.h"
#include <atomic>
#include <utility>
// SkOnce provides call-once guarantees for Skia, much like std::once_flag/std::call_once().
//
// There should be no particularly error-prone gotcha use cases when using SkOnce.
// It works correctly as a class member, a local, a global, a function-scoped static, whatever.
class SkOnce {
public:
template <typename Fn, typename... Args>
void operator()(Fn&& fn, Args&&... args) {
// Vanilla double-checked locking.
if (!fDone.load(std::memory_order_acquire)) {
fLock.acquire();
if (!fDone.load(std::memory_order_relaxed)) {
fn(std::forward<Args>(args)...);
fDone.store(true, std::memory_order_release);
}
fLock.release();
}
}
private:
std::atomic<bool> fDone{false};
SkSpinlock fLock;
};
#endif // SkOnce_DEFINED