Skip to content

Commit e396dbf

Browse files
committed
chore: ThreadPool
1 parent 9c0f220 commit e396dbf

File tree

6 files changed

+259
-29
lines changed

6 files changed

+259
-29
lines changed

include/mrdox/Support/Path.hpp

+7
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ forEachFile(
7171

7272
namespace files {
7373

74+
/** Return true if pathName is absolute.
75+
*/
76+
MRDOX_DECL
77+
bool
78+
isAbsolute(
79+
std::string_view pathName) noexcept;
80+
7481
/** Return true if pathName ends in a separator.
7582
*/
7683
MRDOX_DECL

include/mrdox/Support/ParallelFor.hpp renamed to include/mrdox/Support/Thread.hpp

+88-5
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,112 @@
99
// Official repository: https://github.com/cppalliance/mrdox
1010
//
1111

12-
#ifndef MRDOX_SUPPORT_PARALLELFOR_HPP
13-
#define MRDOX_SUPPORT_PARALLELFOR_HPP
12+
#ifndef MRDOX_SUPPORT_THREAD_HPP
13+
#define MRDOX_SUPPORT_THREAD_HPP
1414

1515
#include <mrdox/Platform.hpp>
1616
#include <mrdox/Support/Error.hpp>
1717
#include <iterator>
1818
#include <mutex>
1919
#include <thread>
20+
#include <utility>
2021
#include <vector>
2122

22-
#include <algorithm>
23-
2423
namespace clang {
2524
namespace mrdox {
2625

26+
//------------------------------------------------
27+
28+
/** A pool of threads for executing work concurrently.
29+
*/
30+
class MRDOX_VISIBLE
31+
ThreadPool
32+
{
33+
class Impl;
34+
35+
struct MRDOX_VISIBLE
36+
Work
37+
{
38+
virtual ~Work() = 0;
39+
virtual void operator()() = 0;
40+
};
41+
42+
std::unique_ptr<Impl> impl_;
43+
44+
public:
45+
/** Destructor.
46+
*/
47+
MRDOX_DECL
48+
~ThreadPool();
49+
50+
/** Constructor.
51+
*/
52+
MRDOX_DECL
53+
explicit
54+
ThreadPool(
55+
std::size_t concurrency);
56+
57+
/** Submit work to be executed.
58+
59+
The signature of the submitted function
60+
object should be `void(void)`.
61+
*/
62+
template<class F>
63+
void
64+
post(F&& f);
65+
66+
/** Block until all work has completed.
67+
*/
68+
MRDOX_DECL
69+
void
70+
wait();
71+
72+
private:
73+
MRDOX_DECL void do_post(std::unique_ptr<Work>);
74+
};
75+
76+
template<class F>
77+
void
78+
ThreadPool::
79+
post(F&& f)
80+
{
81+
if(! impl_)
82+
{
83+
// no threads
84+
f();
85+
return;
86+
}
87+
88+
struct WorkImpl : Work
89+
{
90+
F f;
91+
92+
~WorkImpl() = default;
93+
94+
explicit WorkImpl(F&& f_)
95+
: f(std::forward<F>(f_))
96+
{
97+
}
98+
99+
void operator()() override
100+
{
101+
f();
102+
}
103+
};
104+
105+
do_post(std::make_unique<WorkImpl>(std::forward<F>(f)));
106+
}
107+
108+
//------------------------------------------------
109+
27110
/** Visit all elements of a range concurrently.
28111
*/
29112
template<
30113
class Elements,
31114
class Workers,
32115
class... Args>
33116
Error
34-
parallelFor(
117+
forEach(
35118
Elements& elements,
36119
Workers& workers,
37120
Args&&... args)

source/ConfigImpl.cpp

+3-9
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ construct(
7777
namespace fs = llvm::sys::fs;
7878
namespace path = llvm::sys::path;
7979

80-
workingDir = workingDir_;
80+
if(! files::isAbsolute(workingDir_))
81+
return Error("path \"{}\" is not absolute", workingDir_);
82+
workingDir = files::makeDirsy(files::normalizePath(workingDir_));
8183
configYaml = configYaml_;
8284
extraYaml = extraYaml_;
8385

@@ -174,14 +176,6 @@ createConfigFromYAML(
174176
llvm::StringRef configYaml,
175177
llvm::StringRef extraYaml)
176178
{
177-
#if 0
178-
namespace fs = llvm::sys::fs;
179-
namespace path = llvm::sys::path;
180-
181-
SmallPathString temp(workingDir);
182-
path::remove_dots(temp, true, path::Style::native);
183-
#endif
184-
185179
auto config = std::make_shared<ConfigImpl>();
186180
if(auto err = config->construct(workingDir, configYaml, extraYaml))
187181
return err;

source/Support/Path.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,21 @@ forEachFile(
8080

8181
namespace files {
8282

83+
bool
84+
isAbsolute(
85+
std::string_view pathName) noexcept
86+
{
87+
namespace path = llvm::sys::path;
88+
89+
return path::is_absolute(pathName);
90+
}
91+
8392
bool
8493
isDirsy(
8594
std::string_view pathName) noexcept
8695
{
8796
namespace path = llvm::sys::path;
97+
8898
if(pathName.empty())
8999
return false;
90100
if(! path::is_separator(

source/Support/Thread.cpp

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//
2+
// Licensed under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com)
7+
//
8+
// Official repository: https://github.com/cppalliance/mrdox
9+
//
10+
11+
#include <mrdox/Support/Thread.hpp>
12+
#include <condition_variable>
13+
#include <cstdlib>
14+
#include <deque>
15+
#include <iterator>
16+
#include <memory>
17+
#include <mutex>
18+
#include <thread>
19+
#include <vector>
20+
21+
namespace clang {
22+
namespace mrdox {
23+
24+
ThreadPool::Work::~Work() = default;
25+
26+
class ThreadPool::Impl
27+
{
28+
std::vector<std::thread> threads_;
29+
std::condition_variable work_cv_;
30+
std::condition_variable join_cv_;
31+
std::mutex mutex_;
32+
33+
std::deque<std::unique_ptr<Work>> work_;
34+
std::size_t busy_ = 0;
35+
bool destroy_ = false;
36+
37+
void
38+
run()
39+
{
40+
std::unique_lock<std::mutex> lock(mutex_);
41+
++busy_;
42+
for(;;)
43+
{
44+
--busy_;
45+
work_cv_.wait(lock,
46+
[&]
47+
{
48+
return destroy_ || ! work_.empty();
49+
});
50+
++busy_;
51+
if(! work_.empty())
52+
{
53+
std::unique_ptr<Work> work =
54+
std::move(work_.front());
55+
work_.pop_front();
56+
lock.unlock();
57+
(*work)();
58+
continue;
59+
}
60+
if(destroy_)
61+
break;
62+
}
63+
--busy_;
64+
join_cv_.notify_all();
65+
}
66+
67+
public:
68+
explicit
69+
Impl(std::size_t concurrency)
70+
{
71+
threads_.resize(concurrency);
72+
for(auto& t : threads_)
73+
t = std::thread(&Impl::run, this);
74+
}
75+
76+
~Impl()
77+
{
78+
for(auto& t : threads_)
79+
t.join();
80+
}
81+
82+
void destroy() noexcept
83+
{
84+
std::unique_lock<std::mutex> lock(mutex_);
85+
destroy_ = true;
86+
work_cv_.notify_all();
87+
}
88+
89+
void post(std::unique_ptr<Work> work)
90+
{
91+
std::unique_lock<std::mutex> lock(mutex_);
92+
work_.emplace_back(std::move(work));
93+
work_cv_.notify_one();
94+
}
95+
96+
void wait()
97+
{
98+
std::unique_lock<std::mutex> lock(mutex_);
99+
join_cv_.wait(lock,
100+
[&]
101+
{
102+
return busy_ == 0;
103+
});
104+
}
105+
};
106+
107+
ThreadPool::
108+
~ThreadPool()
109+
{
110+
if(impl_)
111+
{
112+
wait();
113+
impl_->destroy();
114+
}
115+
}
116+
117+
ThreadPool::
118+
ThreadPool(
119+
std::size_t concurrency)
120+
{
121+
if( concurrency == 0)
122+
concurrency = std::thread::hardware_concurrency();
123+
if(concurrency > 1)
124+
impl_ = std::make_unique<Impl>(concurrency);
125+
}
126+
127+
void
128+
ThreadPool::
129+
wait()
130+
{
131+
if( impl_)
132+
impl_->wait();
133+
}
134+
135+
void
136+
ThreadPool::
137+
do_post(
138+
std::unique_ptr<Work> work)
139+
{
140+
impl_->post(std::move(work));
141+
}
142+
143+
} // mrdox
144+
} // clang

source/TestAction.cpp

+7-15
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <mrdox/Config.hpp>
1818
#include <mrdox/Generators.hpp>
1919
#include <mrdox/Support/Report.hpp>
20+
#include <mrdox/Support/Thread.hpp>
2021
#include <clang/Tooling/StandaloneExecution.h>
2122
#include <llvm/Support/CommandLine.h>
2223
#include <llvm/Support/FileSystem.h>
@@ -73,10 +74,9 @@ struct Results
7374

7475
class TestRunner
7576
{
77+
ThreadPool threadPool_;
7678
Results& results_;
7779
std::string extraYaml_;
78-
std::shared_ptr<Config const> config_;
79-
Config::WorkGroup wg_;
8080
llvm::ErrorOr<std::string> diff_;
8181
Generator const* xmlGen_;
8282
Generator const* adocGen_;
@@ -120,17 +120,9 @@ TestRunner::
120120
TestRunner(
121121
Results& results,
122122
llvm::StringRef extraYaml)
123-
: results_(results)
123+
: threadPool_(1)
124+
, results_(results)
124125
, extraYaml_(extraYaml)
125-
, config_([&extraYaml]
126-
{
127-
std::error_code ec;
128-
auto config = loadConfigString(
129-
"", extraYaml.str());
130-
Assert(config);
131-
return *config;
132-
}())
133-
, wg_(config_.get())
134126
, diff_(llvm::sys::findProgramByName("diff"))
135127
, xmlGen_(getGenerators().find("xml"))
136128
, adocGen_(getGenerators().find("adoc"))
@@ -342,7 +334,7 @@ handleDir(
342334
iter->type() == fs::file_type::regular_file &&
343335
path::extension(iter->path()).equals_insensitive(".cpp"))
344336
{
345-
wg_.post(
337+
threadPool_.post(
346338
[this, config, filePath = SmallString(iter->path())]
347339
{
348340
handleFile(filePath, config).operator bool();
@@ -388,7 +380,7 @@ checkPath(
388380

389381
auto config = makeConfig(workingDir);
390382
auto err = handleFile(inputPath, config);
391-
wg_.wait();
383+
threadPool_.wait();
392384
return err;
393385
}
394386

@@ -398,7 +390,7 @@ checkPath(
398390
SmallString dirPath(inputPath);
399391
path::remove_dots(dirPath, true);
400392
auto err = handleDir(dirPath);
401-
wg_.wait();
393+
threadPool_.wait();
402394
return err;
403395
}
404396

0 commit comments

Comments
 (0)