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 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 // count() returns the number of enabled cores in the affinity. 103 MARL_EXPORT size_t count() const; 104 105 // operator[] returns the i'th enabled core from this affinity. 106 MARL_EXPORT Core operator[](size_t index) const; 107 108 // add() adds the cores from the given affinity to this affinity. 109 // This affinity is returned to allow for fluent calls. 110 MARL_EXPORT Affinity& add(const Affinity&); 111 112 // remove() removes the cores from the given affinity from this affinity. 113 // This affinity is returned to allow for fluent calls. 114 MARL_EXPORT Affinity& remove(const Affinity&); 115 116 private: 117 Affinity(const Affinity&) = delete; 118 119 containers::vector<Core, 32> cores; 120 }; 121 122 MARL_EXPORT Thread() = default; 123 124 MARL_EXPORT Thread(Thread&&); 125 126 MARL_EXPORT Thread& operator=(Thread&&); 127 128 // Start a new thread using the given affinity that calls func. 129 MARL_EXPORT Thread(Affinity&& affinity, Func&& func); 130 131 MARL_EXPORT ~Thread(); 132 133 // join() blocks until the thread completes. 134 MARL_EXPORT void join(); 135 136 // setName() sets the name of the currently executing thread for displaying 137 // in a debugger. 138 MARL_EXPORT static void setName(const char* fmt, ...); 139 140 // numLogicalCPUs() returns the number of available logical CPU cores for 141 // the system. 142 MARL_EXPORT static unsigned int numLogicalCPUs(); 143 144 private: 145 Thread(const Thread&) = delete; 146 Thread& operator=(const Thread&) = delete; 147 148 class Impl; 149 Impl* impl = nullptr; 150 }; 151 152 //////////////////////////////////////////////////////////////////////////////// 153 // Thread::Core 154 //////////////////////////////////////////////////////////////////////////////// 155 // Comparison functions 156 bool Thread::Core::operator==(const Core& other) const { 157 return pthread.index == other.pthread.index; 158 } 159 160 bool Thread::Core::operator<(const Core& other) const { 161 return pthread.index < other.pthread.index; 162 } 163 164 } // namespace marl 165 166 #endif // marl_thread_h 167