blob: 68434cbf148cda2d66f3648b3246487a65632a7a [file] [log] [blame]
// Copyright 2019 The Marl Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef marl_thread_h
#define marl_thread_h
#include "containers.h"
#include "export.h"
#include <functional>
namespace marl {
// Thread provides an OS abstraction for threads of execution.
class Thread {
public:
using Func = std::function<void()>;
// Core identifies a logical processor unit.
// How a core is identified varies by platform.
struct Core {
struct Windows {
uint8_t group; // Group number
uint8_t index; // Core within the processor group
};
struct Pthread {
uint16_t index; // Core number
};
union {
Windows windows;
Pthread pthread;
};
// Comparison functions
MARL_NO_EXPORT inline bool operator==(const Core&) const;
MARL_NO_EXPORT inline bool operator<(const Core&) const;
};
// Affinity holds the affinity mask for a thread - a description of what cores
// the thread is allowed to run on.
struct Affinity {
// supported is true if marl supports controlling thread affinity for this
// platform.
#if defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__)) || \
defined(__FreeBSD__)
static constexpr bool supported = true;
#else
static constexpr bool supported = false;
#endif
// Policy is an interface that provides a get() method for returning an
// Affinity for the given thread by id.
class Policy {
public:
virtual ~Policy() {}
// anyOf() returns a Policy that returns an Affinity for a number of
// available cores in affinity.
//
// Windows requires that each thread is only associated with a
// single affinity group, so the Policy's returned affinity will contain
// cores all from the same group.
MARL_EXPORT static std::shared_ptr<Policy> anyOf(
Affinity&& affinity,
Allocator* allocator = Allocator::Default);
// oneOf() returns a Policy that returns an affinity with a single enabled
// core from affinity. The single enabled core in the Policy's returned
// affinity is:
// affinity[threadId % affinity.count()]
MARL_EXPORT static std::shared_ptr<Policy> oneOf(
Affinity&& affinity,
Allocator* allocator = Allocator::Default);
// get() returns the thread Affinity for the for the given thread by id.
MARL_EXPORT virtual Affinity get(uint32_t threadId,
Allocator* allocator) const = 0;
};
MARL_EXPORT Affinity(Allocator*);
MARL_EXPORT Affinity(Affinity&&);
MARL_EXPORT Affinity(const Affinity&, Allocator* allocator);
// all() returns an Affinity with all the cores available to the process.
MARL_EXPORT static Affinity all(Allocator* allocator = Allocator::Default);
MARL_EXPORT Affinity(std::initializer_list<Core>, Allocator* allocator);
// count() returns the number of enabled cores in the affinity.
MARL_EXPORT size_t count() const;
// operator[] returns the i'th enabled core from this affinity.
MARL_EXPORT Core operator[](size_t index) const;
// add() adds the cores from the given affinity to this affinity.
// This affinity is returned to allow for fluent calls.
MARL_EXPORT Affinity& add(const Affinity&);
// remove() removes the cores from the given affinity from this affinity.
// This affinity is returned to allow for fluent calls.
MARL_EXPORT Affinity& remove(const Affinity&);
private:
Affinity(const Affinity&) = delete;
containers::vector<Core, 32> cores;
};
MARL_EXPORT Thread() = default;
MARL_EXPORT Thread(Thread&&);
MARL_EXPORT Thread& operator=(Thread&&);
// Start a new thread using the given affinity that calls func.
MARL_EXPORT Thread(Affinity&& affinity, Func&& func);
MARL_EXPORT ~Thread();
// join() blocks until the thread completes.
MARL_EXPORT void join();
// setName() sets the name of the currently executing thread for displaying
// in a debugger.
MARL_EXPORT static void setName(const char* fmt, ...);
// numLogicalCPUs() returns the number of available logical CPU cores for
// the system.
MARL_EXPORT static unsigned int numLogicalCPUs();
private:
Thread(const Thread&) = delete;
Thread& operator=(const Thread&) = delete;
class Impl;
Impl* impl = nullptr;
};
////////////////////////////////////////////////////////////////////////////////
// Thread::Core
////////////////////////////////////////////////////////////////////////////////
// Comparison functions
bool Thread::Core::operator==(const Core& other) const {
return pthread.index == other.pthread.index;
}
bool Thread::Core::operator<(const Core& other) const {
return pthread.index < other.pthread.index;
}
} // namespace marl
#endif // marl_thread_h