1 | /* |
2 | * Copyright 2016 WebAssembly Community Group participants |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | // |
18 | // Threads helpers. |
19 | // |
20 | |
21 | #ifndef wasm_support_threads_h |
22 | #define wasm_support_threads_h |
23 | |
24 | #include <atomic> |
25 | #include <cassert> |
26 | #include <condition_variable> |
27 | #include <functional> |
28 | #include <memory> |
29 | #include <mutex> |
30 | #include <thread> |
31 | #include <vector> |
32 | |
33 | #include "compiler-support.h" |
34 | |
35 | namespace wasm { |
36 | |
37 | // The work state of a helper thread - is there more to do, |
38 | // or are we finished for now. |
39 | enum class ThreadWorkState { More, Finished }; |
40 | |
41 | class ThreadPool; |
42 | |
43 | // |
44 | // A helper thread. |
45 | // |
46 | // You can only create and destroy these on the main thread. |
47 | // |
48 | |
49 | class Thread { |
50 | ThreadPool* parent; |
51 | std::unique_ptr<std::thread> thread; |
52 | std::mutex mutex; |
53 | std::condition_variable condition; |
54 | bool done = false; |
55 | std::function<ThreadWorkState()> doWork = nullptr; |
56 | |
57 | public: |
58 | Thread(ThreadPool* parent); |
59 | ~Thread(); |
60 | |
61 | // Start to do work, calling doWork() until |
62 | // it returns false. |
63 | void work(std::function<ThreadWorkState()> doWork); |
64 | |
65 | private: |
66 | static void mainLoop(void* self); |
67 | }; |
68 | |
69 | // |
70 | // A pool of helper threads. |
71 | // |
72 | // There is only one, to avoid recursive pools using too many cores. |
73 | // |
74 | |
75 | class ThreadPool { |
76 | std::vector<std::unique_ptr<Thread>> threads; |
77 | bool running = false; |
78 | std::condition_variable condition; |
79 | std::atomic<size_t> ready; |
80 | |
81 | // A mutex for creating the pool safely |
82 | static std::mutex creationMutex; |
83 | // A mutex for work() so that the pool can only work on one |
84 | // thing at a time |
85 | static std::mutex workMutex; |
86 | // A mutex for communication with the worker threads |
87 | static std::mutex threadMutex; |
88 | |
89 | private: |
90 | void initialize(size_t num); |
91 | |
92 | public: |
93 | // Get the number of cores we can use. |
94 | static size_t getNumCores(); |
95 | |
96 | // Get the singleton threadpool. |
97 | static ThreadPool* get(); |
98 | |
99 | // Execute a bunch of tasks by the pool. This calls |
100 | // getTask() (in a thread-safe manner) to get tasks, and |
101 | // sends them to workers to be executed. This method |
102 | // blocks until all tasks are complete. |
103 | void work(std::vector<std::function<ThreadWorkState()>>& doWorkers); |
104 | |
105 | size_t size(); |
106 | |
107 | bool isRunning(); |
108 | |
109 | // Called by helper threads when they are free and ready. |
110 | void notifyThreadIsReady(); |
111 | |
112 | private: |
113 | void resetThreadsAreReady(); |
114 | |
115 | bool areThreadsReady(); |
116 | }; |
117 | |
118 | // Verify a code segment is only entered once. Usage: |
119 | // static OnlyOnce onlyOnce; |
120 | // onlyOnce.verify(); |
121 | |
122 | class OnlyOnce { |
123 | std::atomic<int> created; |
124 | |
125 | public: |
126 | OnlyOnce() { created.store(0); } |
127 | |
128 | void verify() { |
129 | [[maybe_unused]] auto before = created.fetch_add(1); |
130 | assert(before == 0); |
131 | } |
132 | }; |
133 | |
134 | } // namespace wasm |
135 | |
136 | #endif // wasm_support_threads_h |
137 | |