• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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