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
23namespace marl {
24
25// Thread provides an OS abstraction for threads of execution.
26class 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:
66 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
159bool Thread::Core::operator==(const Core& other) const {
160 return pthread.index == other.pthread.index;
161}
162
163bool 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