1 // Copyright 2019 The Marl Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef marl_thread_h 16 #define marl_thread_h 17 18 #include "containers.h" 19 #include "export.h" 20 21 #include <functional> 22 23 namespace marl { 24 25 // Thread provides an OS abstraction for threads of execution. 26 class Thread { 27 public: 28 using Func = std::function<void()>; 29 30 // Core identifies a logical processor unit. 31 // How a core is identified varies by platform. 32 struct Core { 33 struct Windows { 34 uint8_t group; // Group number 35 uint8_t index; // Core within the processor group 36 }; 37 struct Pthread { 38 uint16_t index; // Core number 39 }; 40 union { 41 Windows windows; 42 Pthread pthread; 43 }; 44 45 // Comparison functions 46 MARL_NO_EXPORT inline bool operator==(Core) const; 47 MARL_NO_EXPORT inline bool operator<(Core) const; 48 }; 49 50 // Affinity holds the affinity mask for a thread - a description of what cores 51 // the thread is allowed to run on. 52 struct Affinity { 53 // supported is true if marl supports controlling thread affinity for this 54 // platform. 55 #if defined(_WIN32) || \ 56 (defined(__linux__) && !defined(__ANDROID__) && !defined(__BIONIC__)) || \ 57 defined(__FreeBSD__) 58 static constexpr bool supported = true; 59 #else 60 static constexpr bool supported = false; 61 #endif 62 63 // Policy is an interface that provides a get() method for returning an 64 // Affinity for the given thread by id. 65 class Policy { 66 public: ~PolicyAffinity67 virtual ~Policy() {} 68 69 // anyOf() returns a Policy that returns an Affinity for a number of 70 // available cores in affinity. 71 // 72 // Windows requires that each thread is only associated with a 73 // single affinity group, so the Policy's returned affinity will contain 74 // cores all from the same group. 75 MARL_EXPORT static std::shared_ptr<Policy> anyOf( 76 Affinity&& affinity, 77 Allocator* allocator = Allocator::Default); 78 79 // oneOf() returns a Policy that returns an affinity with a single enabled 80 // core from affinity. The single enabled core in the Policy's returned 81 // affinity is: 82 // affinity[threadId % affinity.count()] 83 MARL_EXPORT static std::shared_ptr<Policy> oneOf( 84 Affinity&& affinity, 85 Allocator* allocator = Allocator::Default); 86 87 // get() returns the thread Affinity for the given thread by id. 88 MARL_EXPORT virtual Affinity get(uint32_t threadId, 89 Allocator* allocator) const = 0; 90 }; 91 92 MARL_EXPORT Affinity(Allocator*); 93 94 MARL_EXPORT Affinity(Affinity&&); 95 96 MARL_EXPORT Affinity& operator=(Affinity&&); 97 98 MARL_EXPORT Affinity(const Affinity&, Allocator* allocator); 99 100 // all() returns an Affinity with all the cores available to the process. 101 MARL_EXPORT static Affinity all(Allocator* allocator = Allocator::Default); 102 103 MARL_EXPORT Affinity(std::initializer_list<Core>, Allocator* allocator); 104 105 MARL_EXPORT Affinity(const containers::vector<Core, 32>&, 106 Allocator* allocator); 107 108 // count() returns the number of enabled cores in the affinity. 109 MARL_EXPORT size_t count() const; 110 111 // operator[] returns the i'th enabled core from this affinity. 112 MARL_EXPORT Core operator[](size_t index) const; 113 114 // add() adds the cores from the given affinity to this affinity. 115 // This affinity is returned to allow for fluent calls. 116 MARL_EXPORT Affinity& add(const Affinity&); 117 118 // remove() removes the cores from the given affinity from this affinity. 119 // This affinity is returned to allow for fluent calls. 120 MARL_EXPORT Affinity& remove(const Affinity&); 121 122 private: 123 Affinity(const Affinity&) = delete; 124 125 containers::vector<Core, 32> cores; 126 }; 127 128 MARL_EXPORT Thread() = default; 129 130 MARL_EXPORT Thread(Thread&&); 131 132 MARL_EXPORT Thread& operator=(Thread&&); 133 134 // Start a new thread using the given affinity that calls func. 135 MARL_EXPORT Thread(Affinity&& affinity, Func&& func); 136 137 MARL_EXPORT ~Thread(); 138 139 // join() blocks until the thread completes. 140 MARL_EXPORT void join(); 141 142 // setName() sets the name of the currently executing thread for displaying 143 // in a debugger. 144 MARL_EXPORT static void setName(const char* fmt, ...); 145 146 // numLogicalCPUs() returns the number of available logical CPU cores for 147 // the system. 148 MARL_EXPORT static unsigned int numLogicalCPUs(); 149 150 private: 151 Thread(const Thread&) = delete; 152 Thread& operator=(const Thread&) = delete; 153 154 class Impl; 155 Impl* impl = nullptr; 156 }; 157 158 //////////////////////////////////////////////////////////////////////////////// 159 // Thread::Core 160 //////////////////////////////////////////////////////////////////////////////// 161 // Comparison functions 162 bool Thread::Core::operator==(Core other) const { 163 return pthread.index == other.pthread.index; 164 } 165 166 bool Thread::Core::operator<(Core other) const { 167 return pthread.index < other.pthread.index; 168 } 169 170 } // namespace marl 171 172 #endif // marl_thread_h 173