blob: 690efec8112b0e92923b1578e7e549231290fe71 [file] [log] [blame]
mtklein@google.com3a19fb52013-10-09 16:12:23 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SkOnce_DEFINED
9#define SkOnce_DEFINED
10
mtkleind9dd4282016-04-18 08:09:11 -070011#include <atomic>
12#include <utility>
Mike Kleinc0bd9f92019-04-23 12:05:21 -050013#include "include/core/SkTypes.h"
mtklein@google.com3a19fb52013-10-09 16:12:23 +000014
mtkleind9dd4282016-04-18 08:09:11 -070015// SkOnce provides call-once guarantees for Skia, much like std::once_flag/std::call_once().
16//
17// There should be no particularly error-prone gotcha use cases when using SkOnce.
18// It works correctly as a class member, a local, a global, a function-scoped static, whatever.
mtklein@google.com3a19fb52013-10-09 16:12:23 +000019
mtkleind9dd4282016-04-18 08:09:11 -070020class SkOnce {
mtklein1b818772014-06-02 11:26:59 -070021public:
mtkleine86e51f2016-04-29 13:58:18 -070022 constexpr SkOnce() = default;
23
mtkleind9dd4282016-04-18 08:09:11 -070024 template <typename Fn, typename... Args>
25 void operator()(Fn&& fn, Args&&... args) {
mtklein650f9e92016-04-20 13:49:15 -070026 auto state = fState.load(std::memory_order_acquire);
27
28 if (state == Done) {
29 return;
mtkleindf02d332016-04-20 10:54:54 -070030 }
mtklein650f9e92016-04-20 13:49:15 -070031
mtkleinb37c68a2016-05-04 13:57:30 -070032 // If it looks like no one has started calling fn(), try to claim that job.
33 if (state == NotStarted && fState.compare_exchange_strong(state, Claimed,
Lee Salzmanee1c73f2016-12-16 13:56:32 -050034 std::memory_order_relaxed,
mtkleinb37c68a2016-05-04 13:57:30 -070035 std::memory_order_relaxed)) {
36 // Great! We'll run fn() then notify the other threads by releasing Done into fState.
37 fn(std::forward<Args>(args)...);
38 return fState.store(Done, std::memory_order_release);
mtklein650f9e92016-04-20 13:49:15 -070039 }
40
mtkleinb37c68a2016-05-04 13:57:30 -070041 // Some other thread is calling fn().
42 // We'll just spin here acquiring until it releases Done into fState.
43 while (fState.load(std::memory_order_acquire) != Done) { /*spin*/ }
mtkleind9dd4282016-04-18 08:09:11 -070044 }
commit-bot@chromium.orgba9354b2014-02-10 19:58:49 +000045
commit-bot@chromium.orgba9354b2014-02-10 19:58:49 +000046private:
mtkleinb37c68a2016-05-04 13:57:30 -070047 enum State : uint8_t { NotStarted, Claimed, Done};
mtklein650f9e92016-04-20 13:49:15 -070048 std::atomic<uint8_t> fState{NotStarted};
commit-bot@chromium.orgba9354b2014-02-10 19:58:49 +000049};
50
mtklein@google.com3a19fb52013-10-09 16:12:23 +000051#endif // SkOnce_DEFINED