1 | /* |
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 | |
11 | #include "include/private/SkThreadAnnotations.h" |
12 | #include <atomic> |
13 | #include <utility> |
14 | |
15 | // 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. |
19 | |
20 | class SkOnce { |
21 | public: |
22 | constexpr SkOnce() = default; |
23 | |
24 | template <typename Fn, typename... Args> |
25 | void operator()(Fn&& fn, Args&&... args) { |
26 | auto state = fState.load(std::memory_order_acquire); |
27 | |
28 | if (state == Done) { |
29 | return; |
30 | } |
31 | |
32 | // 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, |
34 | std::memory_order_relaxed, |
35 | 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); |
39 | } |
40 | |
41 | // Some other thread is calling fn(). |
42 | // We'll just spin here acquiring until it releases Done into fState. |
43 | SK_POTENTIALLY_BLOCKING_REGION_BEGIN; |
44 | while (fState.load(std::memory_order_acquire) != Done) { /*spin*/ } |
45 | SK_POTENTIALLY_BLOCKING_REGION_END; |
46 | } |
47 | |
48 | private: |
49 | enum State : uint8_t { NotStarted, Claimed, Done}; |
50 | std::atomic<uint8_t> fState{NotStarted}; |
51 | }; |
52 | |
53 | #endif // SkOnce_DEFINED |
54 | |