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==(const Core&) const; 47 MARL_NO_EXPORT inline bool operator<(const 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) || (defined(__linux__) && !defined(__ANDROID__)) || \ 56 defined(__FreeBSD__) 57 static constexpr bool supported = true; 58 #else 59 static constexpr bool supported = false; 60 #endif 61 62 // Policy is an interface that provides a get() method for returning an 63 // Affinity for the given thread by id. 64 class Policy { 65 public: ~PolicyAffinity66 virtual ~Policy() {} 67 68 // anyOf() returns a Policy that returns an Affinity for a number of 69 // available cores in affinity. 70 // 71 // Windows requires that each thread is only associated with a 72 // single affinity group, so the Policy's returned affinity will contain 73 // cores all from the same group. 74 MARL_EXPORT static std::shared_ptr<Policy> anyOf( 75 Affinity&& affinity, 76 Allocator* allocator = Allocator::Default); 77 78 // oneOf() returns a Policy that returns an affinity with a single enabled 79 // core from affinity. The single enabled core in the Policy's returned 80 // affinity is: 81 // affinity[threadId % affinity.count()] 82 MARL_EXPORT static std::shared_ptr<Policy> oneOf( 83 Affinity&& affinity, 84 Allocator* allocator = Allocator::Default); 85 86 // get() returns the thread Affinity for the given thread by id. 87 MARL_EXPORT virtual Affinity get(uint32_t threadId, 88 Allocator* allocator) const = 0; 89 }; 90 91 MARL_EXPORT Affinity(Allocator*); 92 93 MARL_EXPORT Affinity(Affinity&&); 94 95 MARL_EXPORT Affinity(const Affinity&, Allocator* allocator); 96 97 // all() returns an Affinity with all the cores available to the process. 98 MARL_EXPORT static Affinity all(Allocator* allocator = Allocator::Default); 99 100 MARL_EXPORT Affinity(std::initializer_list<Core>, Allocator* allocator); 101 102 MARL_EXPORT Affinity(const containers::vector<Core, 32>&, 103 Allocator* allocator); 104 105 // count() returns the number of enabled cores in the affinity. 106 MARL_EXPORT size_t count() const; 107 108 // operator[] returns the i'th enabled core from this affinity. 109 MARL_EXPORT Core operator[](size_t index) const; 110 111 // add() adds the cores from the given affinity to this affinity. 112 // This affinity is returned to allow for fluent calls. 113 MARL_EXPORT Affinity& add(const Affinity&); 114 115 // remove() removes the cores from the given affinity from this affinity. 116 // This affinity is returned to allow for fluent calls. 117 MARL_EXPORT Affinity& remove(const Affinity&); 118 119 private: 120 Affinity(const Affinity&) = delete; 121 122 containers::vector<Core, 32> cores; 123 }; 124 125 MARL_EXPORT Thread() = default; 126 127 MARL_EXPORT Thread(Thread&&); 128 129 MARL_EXPORT Thread& operator=(Thread&&); 130 131 // Start a new thread using the given affinity that calls func. 132 MARL_EXPORT Thread(Affinity&& affinity, Func&& func); 133 134 MARL_EXPORT ~Thread(); 135 136 // join() blocks until the thread completes. 137 MARL_EXPORT void join(); 138 139 // setName() sets the name of the currently executing thread for displaying 140 // in a debugger. 141 MARL_EXPORT static void setName(const char* fmt, ...); 142 143 // numLogicalCPUs() returns the number of available logical CPU cores for 144 // the system. 145 MARL_EXPORT static unsigned int numLogicalCPUs(); 146 147 private: 148 Thread(const Thread&) = delete; 149 Thread& operator=(const Thread&) = delete; 150 151 class Impl; 152 Impl* impl = nullptr; 153 }; 154 155 //////////////////////////////////////////////////////////////////////////////// 156 // Thread::Core 157 //////////////////////////////////////////////////////////////////////////////// 158 // Comparison functions 159 bool Thread::Core::operator==(const Core& other) const { 160 return pthread.index == other.pthread.index; 161 } 162 163 bool Thread::Core::operator<(const Core& other) const { 164 return pthread.index < other.pthread.index; 165 } 166 167 } // namespace marl 168 169 #endif // marl_thread_h 170