mirror of
https://github.com/YosysHQ/yosys
synced 2025-09-15 22:21:30 +00:00
Add thread support code
Provides very simple ConcurrentQueue and ThreadPool classes that build even when threading is disabled. Also provides a `DeferredLogs` class that captures log output to be replayed on the main thread later.
This commit is contained in:
parent
012ddc2f1e
commit
9f0d5d1aca
7 changed files with 254 additions and 2 deletions
14
Makefile
14
Makefile
|
@ -44,7 +44,12 @@ LINK_ABC := 0
|
||||||
# Needed for environments that can't run executables (i.e. emscripten, wasm)
|
# Needed for environments that can't run executables (i.e. emscripten, wasm)
|
||||||
DISABLE_SPAWN := 0
|
DISABLE_SPAWN := 0
|
||||||
# Needed for environments that don't have proper thread support (i.e. emscripten, wasm--for now)
|
# Needed for environments that don't have proper thread support (i.e. emscripten, wasm--for now)
|
||||||
|
ENABLE_THREADS := 1
|
||||||
|
ifeq ($(ENABLE_THREADS),1)
|
||||||
DISABLE_ABC_THREADS := 0
|
DISABLE_ABC_THREADS := 0
|
||||||
|
else
|
||||||
|
DISABLE_ABC_THREADS := 1
|
||||||
|
endif
|
||||||
|
|
||||||
# clang sanitizers
|
# clang sanitizers
|
||||||
SANITIZER =
|
SANITIZER =
|
||||||
|
@ -300,6 +305,7 @@ DISABLE_SPAWN := 1
|
||||||
|
|
||||||
ifeq ($(ENABLE_ABC),1)
|
ifeq ($(ENABLE_ABC),1)
|
||||||
LINK_ABC := 1
|
LINK_ABC := 1
|
||||||
|
ENABLE_THREADS := 0
|
||||||
DISABLE_ABC_THREADS := 1
|
DISABLE_ABC_THREADS := 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -457,6 +463,11 @@ CXXFLAGS := -Og -DDEBUG $(filter-out $(OPT_LEVEL),$(CXXFLAGS))
|
||||||
STRIP :=
|
STRIP :=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(ENABLE_THREADS),1)
|
||||||
|
CXXFLAGS += -DYOSYS_ENABLE_THREADS
|
||||||
|
LIBS += -lpthread
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(ENABLE_ABC),1)
|
ifeq ($(ENABLE_ABC),1)
|
||||||
CXXFLAGS += -DYOSYS_ENABLE_ABC
|
CXXFLAGS += -DYOSYS_ENABLE_ABC
|
||||||
ifeq ($(LINK_ABC),1)
|
ifeq ($(LINK_ABC),1)
|
||||||
|
@ -612,6 +623,7 @@ $(eval $(call add_include_file,kernel/satgen.h))
|
||||||
$(eval $(call add_include_file,kernel/scopeinfo.h))
|
$(eval $(call add_include_file,kernel/scopeinfo.h))
|
||||||
$(eval $(call add_include_file,kernel/sexpr.h))
|
$(eval $(call add_include_file,kernel/sexpr.h))
|
||||||
$(eval $(call add_include_file,kernel/sigtools.h))
|
$(eval $(call add_include_file,kernel/sigtools.h))
|
||||||
|
$(eval $(call add_include_file,kernel/threading.h))
|
||||||
$(eval $(call add_include_file,kernel/timinginfo.h))
|
$(eval $(call add_include_file,kernel/timinginfo.h))
|
||||||
$(eval $(call add_include_file,kernel/utils.h))
|
$(eval $(call add_include_file,kernel/utils.h))
|
||||||
$(eval $(call add_include_file,kernel/yosys.h))
|
$(eval $(call add_include_file,kernel/yosys.h))
|
||||||
|
@ -635,7 +647,7 @@ OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/cal
|
||||||
OBJS += kernel/log_help.o
|
OBJS += kernel/log_help.o
|
||||||
OBJS += kernel/binding.o kernel/tclapi.o
|
OBJS += kernel/binding.o kernel/tclapi.o
|
||||||
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o kernel/sexpr.o
|
OBJS += kernel/cellaigs.o kernel/celledges.o kernel/cost.o kernel/satgen.o kernel/scopeinfo.o kernel/qcsat.o kernel/mem.o kernel/ffmerge.o kernel/ff.o kernel/yw.o kernel/json.o kernel/fmt.o kernel/sexpr.o
|
||||||
OBJS += kernel/drivertools.o kernel/functional.o
|
OBJS += kernel/drivertools.o kernel/functional.o kernel/threading.o
|
||||||
ifeq ($(ENABLE_ZLIB),1)
|
ifeq ($(ENABLE_ZLIB),1)
|
||||||
OBJS += kernel/fstdata.o
|
OBJS += kernel/fstdata.o
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -639,6 +639,13 @@ void log_dump_val_worker(RTLIL::State v) {
|
||||||
log("%s", log_signal(v));
|
log("%s", log_signal(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string signal_str(const RTLIL::SigSpec &sig, bool autoint)
|
||||||
|
{
|
||||||
|
std::stringstream buf;
|
||||||
|
RTLIL_BACKEND::dump_sigspec(buf, sig, autoint);
|
||||||
|
return buf.str();
|
||||||
|
}
|
||||||
|
|
||||||
const char *log_signal(const RTLIL::SigSpec &sig, bool autoint)
|
const char *log_signal(const RTLIL::SigSpec &sig, bool autoint)
|
||||||
{
|
{
|
||||||
std::stringstream buf;
|
std::stringstream buf;
|
||||||
|
|
|
@ -204,6 +204,7 @@ extern dict<std::string, LogExpectedItem> log_expect_log, log_expect_warning, lo
|
||||||
extern dict<std::string, LogExpectedItem> log_expect_prefix_log, log_expect_prefix_warning, log_expect_prefix_error;
|
extern dict<std::string, LogExpectedItem> log_expect_prefix_log, log_expect_prefix_warning, log_expect_prefix_error;
|
||||||
void log_check_expected();
|
void log_check_expected();
|
||||||
|
|
||||||
|
std::string signal_str(const RTLIL::SigSpec &sig, bool autoint = true);
|
||||||
const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true);
|
const char *log_signal(const RTLIL::SigSpec &sig, bool autoint = true);
|
||||||
const char *log_const(const RTLIL::Const &value, bool autoint = true);
|
const char *log_const(const RTLIL::Const &value, bool autoint = true);
|
||||||
const char *log_id(const RTLIL::IdString &id);
|
const char *log_id(const RTLIL::IdString &id);
|
||||||
|
|
45
kernel/threading.cc
Normal file
45
kernel/threading.cc
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "kernel/yosys_common.h"
|
||||||
|
#include "kernel/threading.h"
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
void DeferredLogs::flush()
|
||||||
|
{
|
||||||
|
for (auto &m : logs)
|
||||||
|
if (m.error)
|
||||||
|
YOSYS_NAMESPACE_PREFIX log_error("%s", m.text.c_str());
|
||||||
|
else
|
||||||
|
YOSYS_NAMESPACE_PREFIX log("%s", m.text.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int ThreadPool::pool_size(int reserved_cores, int max_threads)
|
||||||
|
{
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
int num_threads = std::min<int>(std::thread::hardware_concurrency() - reserved_cores, max_threads);
|
||||||
|
return std::max(0, num_threads);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadPool::ThreadPool(int pool_size, std::function<void(int)> b)
|
||||||
|
: body(std::move(b))
|
||||||
|
{
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
threads.reserve(pool_size);
|
||||||
|
for (int i = 0; i < pool_size; i++)
|
||||||
|
threads.emplace_back([i, this]{ body(i); });
|
||||||
|
#else
|
||||||
|
log_assert(pool_size == 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadPool::~ThreadPool()
|
||||||
|
{
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
for (auto &t : threads)
|
||||||
|
t.join();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_END
|
186
kernel/threading.h
Normal file
186
kernel/threading.h
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "kernel/yosys_common.h"
|
||||||
|
#include "kernel/log.h"
|
||||||
|
|
||||||
|
#ifndef YOSYS_THREADING_H
|
||||||
|
#define YOSYS_THREADING_H
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Concurrent queue implementation. Not fast, but simple.
|
||||||
|
// Multi-producer, multi-consumer, optionally bounded.
|
||||||
|
// When YOSYS_ENABLE_THREADS is not defined, this is just a non-thread-safe non-blocking deque.
|
||||||
|
template <typename T>
|
||||||
|
class ConcurrentQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConcurrentQueue(int capacity = INT_MAX)
|
||||||
|
: capacity(capacity) {}
|
||||||
|
// Push an element into the queue. If it's at capacity, block until there is room.
|
||||||
|
void push_back(T t)
|
||||||
|
{
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
not_full_condition.wait(lock, [this] { return static_cast<int>(contents.size()) < capacity; });
|
||||||
|
if (contents.empty())
|
||||||
|
not_empty_condition.notify_one();
|
||||||
|
#endif
|
||||||
|
log_assert(!closed);
|
||||||
|
contents.push_back(std::move(t));
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
if (static_cast<int>(contents.size()) < capacity)
|
||||||
|
not_full_condition.notify_one();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
// Signal that no more elements will be produced. `pop_front()` will return nullopt.
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
not_empty_condition.notify_all();
|
||||||
|
#endif
|
||||||
|
closed = true;
|
||||||
|
}
|
||||||
|
// Pop an element from the queue. Blocks until an element is available
|
||||||
|
// or the queue is closed and empty.
|
||||||
|
std::optional<T> pop_front()
|
||||||
|
{
|
||||||
|
return pop_front_internal(true);
|
||||||
|
}
|
||||||
|
// Pop an element from the queue. Does not block, just returns nullopt if the
|
||||||
|
// queue is empty.
|
||||||
|
std::optional<T> try_pop_front()
|
||||||
|
{
|
||||||
|
return pop_front_internal(false);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
std::optional<T> pop_front_internal(bool wait)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
if (wait) {
|
||||||
|
not_empty_condition.wait(lock, [this] { return !contents.empty() || closed; });
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
std::optional<T> pop_front_internal(bool)
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
if (contents.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
if (static_cast<int>(contents.size()) == capacity)
|
||||||
|
not_full_condition.notify_one();
|
||||||
|
#endif
|
||||||
|
T result = std::move(contents.front());
|
||||||
|
contents.pop_front();
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
if (!contents.empty())
|
||||||
|
not_empty_condition.notify_one();
|
||||||
|
#endif
|
||||||
|
return std::move(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
std::mutex mutex;
|
||||||
|
// Signals one waiter thread when the queue changes and is not full.
|
||||||
|
std::condition_variable not_full_condition;
|
||||||
|
// Signals one waiter thread when the queue changes and is not empty.
|
||||||
|
std::condition_variable not_empty_condition;
|
||||||
|
#endif
|
||||||
|
std::deque<T> contents;
|
||||||
|
int capacity;
|
||||||
|
bool closed = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeferredLogs
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename... Args>
|
||||||
|
void log(FmtString<TypeIdentity<Args>...> fmt, Args... args)
|
||||||
|
{
|
||||||
|
logs.push_back({fmt.format(args...), false});
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
void log_error(FmtString<TypeIdentity<Args>...> fmt, Args... args)
|
||||||
|
{
|
||||||
|
logs.push_back({fmt.format(args...), true});
|
||||||
|
}
|
||||||
|
void flush();
|
||||||
|
private:
|
||||||
|
struct Message
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
bool error;
|
||||||
|
};
|
||||||
|
std::vector<Message> logs;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThreadPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Computes the number of worker threads to use.
|
||||||
|
// `reserved_cores` cores are set aside for other threads (e.g. work on the main thread).
|
||||||
|
// `max_threads` --- don't return more workers than this.
|
||||||
|
// The result may be 0.
|
||||||
|
static int pool_size(int reserved_cores, int max_threads);
|
||||||
|
|
||||||
|
// Create a pool of threads running the given closure (parameterized by thread number).
|
||||||
|
// `pool_size` must be the result of a `pool_size()` call.
|
||||||
|
ThreadPool(int pool_size, std::function<void(int)> b);
|
||||||
|
ThreadPool(ThreadPool &&other) = delete;
|
||||||
|
// Waits for all threads to terminate. Make sure those closures return!
|
||||||
|
~ThreadPool();
|
||||||
|
|
||||||
|
// Return the number of threads in the pool.
|
||||||
|
int num_threads() const
|
||||||
|
{
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
return threads.size();
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::function<void(int)> body;
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class ConcurrentStack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void push_back(T &&t) {
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
#endif
|
||||||
|
contents.push_back(std::move(t));
|
||||||
|
}
|
||||||
|
std::optional<T> try_pop_back() {
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
#endif
|
||||||
|
if (contents.empty())
|
||||||
|
return std::nullopt;
|
||||||
|
T result = std::move(contents.back());
|
||||||
|
contents.pop_back();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
#ifdef YOSYS_ENABLE_THREADS
|
||||||
|
std::mutex mutex;
|
||||||
|
#endif
|
||||||
|
std::vector<T> contents;
|
||||||
|
};
|
||||||
|
|
||||||
|
YOSYS_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // YOSYS_THREADING_H
|
|
@ -177,7 +177,7 @@ int run_command(const std::string &command, std::function<void(const std::string
|
||||||
|
|
||||||
int ret = pclose(f);
|
int ret = pclose(f);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -2;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return ret;
|
return ret;
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -38,6 +38,7 @@ popd
|
||||||
} > "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
|
} > "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
|
||||||
|
|
||||||
sed -i 's,</AdditionalIncludeDirectories>,</AdditionalIncludeDirectories>\n <LanguageStandard>stdcpp17</LanguageStandard>\n <AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
|
sed -i 's,</AdditionalIncludeDirectories>,</AdditionalIncludeDirectories>\n <LanguageStandard>stdcpp17</LanguageStandard>\n <AdditionalOptions>/Zc:__cplusplus %(AdditionalOptions)</AdditionalOptions>,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
|
||||||
|
sed -i 's,<PreprocessorDefinitions>,<PreprocessorDefinitions>YOSYS_ENABLE_THREADS;,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
|
||||||
if [ -f "/usr/include/FlexLexer.h" ] ; then
|
if [ -f "/usr/include/FlexLexer.h" ] ; then
|
||||||
sed -i 's,</AdditionalIncludeDirectories>,;..\\yosys\\libs\\flex</AdditionalIncludeDirectories>,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
|
sed -i 's,</AdditionalIncludeDirectories>,;..\\yosys\\libs\\flex</AdditionalIncludeDirectories>,g' "$vcxsrc"/YosysVS/YosysVS.vcxproj.new
|
||||||
fi
|
fi
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue