Use threads if more than one input file is searched.

Use 75% of the available threads (rounded up).

Closes: #4
This commit is contained in:
tastytea 2021-05-26 17:23:53 +02:00
parent 554caebcef
commit fc0aa02bc9
Signed by: tastytea
GPG Key ID: CFC39497F1B26E07
3 changed files with 137 additions and 67 deletions

View File

@ -38,6 +38,7 @@ if(NOT termcolor_FOUND)
message(FATAL_ERROR "Termcolor was not found.")
endif()
endif()
find_package(Threads REQUIRED)
add_subdirectory(src)

View File

@ -22,7 +22,9 @@ target_link_libraries(${PROJECT_NAME}_lib
Boost::regex
std::filesystem
fmt::fmt
termcolor::termcolor)
termcolor::termcolor
Threads::Threads
m)
if(${CMAKE_VERSION} VERSION_LESS 3.17)
target_link_libraries(${PROJECT_NAME}_lib

View File

@ -21,16 +21,21 @@
#include <boost/locale/message.hpp>
#include <boost/program_options/errors.hpp>
#include <boost/program_options/variables_map.hpp>
#include <fmt/format.h>
#include <fmt/ostream.h> // For compatibility with fmt 4.
#include <termcolor/termcolor.hpp>
#include <clocale>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <exception>
#include <future>
#include <iostream>
#include <locale>
#include <string>
#include <typeinfo>
#include <string_view>
#include <thread>
#include <vector>
int main(int argc, char *argv[])
@ -39,8 +44,11 @@ int main(int argc, char *argv[])
using namespace epubgrep;
using boost::locale::translate;
using fmt::format;
using std::cerr;
using std::cout;
using std::string;
using std::vector;
// locale_generator("").name.c_str() returns "*" instead of "". That's why
// the global C locale isn't changed. So we have to set it additionally.
@ -74,84 +82,143 @@ int main(int argc, char *argv[])
{
cout << "NO INPUT FILE\n";
// TODO: Read data from stdin.
return EXIT_FAILURE;
}
else
{
search::options opts;
if (vm.count("basic-regexp") > 0)
{
opts.regex = search::regex_kind::basic;
}
if (vm.count("extended-regexp") > 0)
{
opts.regex = search::regex_kind::extended;
}
if (vm.count("perl-regexp") > 0)
{
opts.regex = search::regex_kind::perl;
}
if (vm.count("grep") > 0)
{
opts.grep_like = true;
}
if (vm.count("ignore-case") > 0)
{
opts.ignore_case = true;
}
if (vm.count("raw") > 0)
{
opts.raw = true;
}
opts.context = vm["context"].as<std::uint64_t>();
for (const auto &filepath :
vm["input-file"].as<std::vector<std::string>>())
int return_code{EXIT_SUCCESS};
search::options opts;
if (vm.count("basic-regexp") > 0)
{
opts.regex = search::regex_kind::basic;
}
if (vm.count("extended-regexp") > 0)
{
opts.regex = search::regex_kind::extended;
}
if (vm.count("perl-regexp") > 0)
{
opts.regex = search::regex_kind::perl;
}
if (vm.count("grep") > 0)
{
opts.grep_like = true;
}
if (vm.count("ignore-case") > 0)
{
opts.ignore_case = true;
}
if (vm.count("raw") > 0)
{
opts.raw = true;
}
opts.context = vm["context"].as<std::uint64_t>();
vector<vector<search::match>> matches_all;
vector<std::future<int>> futurepool;
auto search_file{
[&vm, &matches_all, &opts](std::string_view filepath)
{
for (const auto &regex :
vm["regexp"].as<std::vector<std::string>>())
for (const auto &regex : vm["regexp"].as<vector<string>>())
{
try
{
for (const auto &match :
search::search(filepath, regex, opts))
{
if (vm.count("no-filename") == 0)
{
cout << match.filepath;
}
if (!match.headline.empty())
{
if (vm.count("no-filename") == 0)
{
cout << ", ";
}
cout << match.headline;
}
if (!match.page.empty())
{
cout << ", page " << match.page;
}
cout << ": " << match.context.first;
if (vm.count("nocolor") == 0)
{
cout << termcolor::bright_magenta << match.text
<< termcolor::reset;
}
else
{
cout << match.text;
}
cout << match.context.second << '\n';
}
matches_all.emplace_back(
search::search(filepath, regex, opts));
}
catch (const std::exception &e)
{ // Unknown errors.
cerr << '\n' << translate("ERROR: ") << e.what() << '\n';
cerr << translate("Error while searching.") << '\n';
// NOTE: Maybe we should continue with the next regex/file?
cerr << format(translate("Error while searching {0:s}.")
.str()
.data(),
filepath)
<< '\n';
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}};
auto futures_cleanup{
[&futurepool, &return_code](const bool wait = false)
{
using namespace std::chrono_literals;
for (auto it{futurepool.begin()}; it != futurepool.end();)
{
if (!wait && it->wait_for(100ms) != std::future_status::ready)
{
++it;
continue;
}
if (int ret{}; (ret = it->get()) != EXIT_SUCCESS)
{
cerr << "ERROR\n";
return_code = ret;
}
futurepool.erase(it);
cerr << "ERASED\n";
}
return EXIT_SUCCESS;
}};
const auto max_threads{
[]
{
auto n{static_cast<double>(std::thread::hardware_concurrency())};
return static_cast<std::uint32_t>(std::ceil(n / 2 + n / 4));
}()};
for (const auto &filepath : vm["input-file"].as<vector<string>>())
{
if (futurepool.size() >= max_threads)
{
futures_cleanup();
}
futurepool.emplace_back(
std::async(std::launch::async, search_file, filepath));
cerr << "EMPLACED\n";
}
futures_cleanup(true);
for (const auto &matches_file : matches_all)
{
for (const auto &match : matches_file)
{
if (vm.count("no-filename") == 0)
{
cout << match.filepath;
}
if (!match.headline.empty())
{
if (vm.count("no-filename") == 0)
{
cout << ", ";
}
cout << match.headline;
}
if (!match.page.empty())
{
cout << ", page " << match.page;
}
cout << ": " << match.context.first;
if (vm.count("nocolor") == 0)
{
cout << termcolor::bright_magenta << match.text
<< termcolor::reset;
}
else
{
cout << match.text;
}
cout << match.context.second << '\n';
}
}
return return_code;
}