mirror of
https://github.com/NixOS/nix
synced 2025-07-05 16:31:47 +02:00
withFramedSink(): Don't use a thread to monitor the other side
Since withFramedSink() is now used a lot more than in the past (for every addToStore() variant), we were creating a lot of threads, e.g. nix flake show --no-eval-cache --all-systems github:NixOS/nix/afdd12be5e19c0001ff3297dea544301108d298 would create 46418 threads. While threads on Linux are cheap, this is still substantial overhead. So instead, just poll from FramedSink before every write whether there are pending messages from the daemon. This could slightly increase the latency on log messages from the daemon, but not on exceptions (which were only synchronously checked from FramedSink anyway). This speeds up the command above from 19.2s to 17.5s on my machine (a 9% speedup).
This commit is contained in:
parent
b0a7edb5ab
commit
39daa4a0d3
6 changed files with 61 additions and 52 deletions
|
@ -10,6 +10,8 @@
|
|||
#ifdef _WIN32
|
||||
# include <fileapi.h>
|
||||
# include "windows-error.hh"
|
||||
#else
|
||||
# include <poll.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -158,6 +160,24 @@ bool FdSource::good()
|
|||
}
|
||||
|
||||
|
||||
bool FdSource::hasData()
|
||||
{
|
||||
if (BufferedSource::hasData()) return true;
|
||||
|
||||
while (true) {
|
||||
struct pollfd fds[1];
|
||||
fds[0].fd = fd;
|
||||
fds[0].events = POLLIN;
|
||||
auto n = poll(fds, 1, 0);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
throw SysError("polling file descriptor");
|
||||
}
|
||||
return n == 1 && (fds[0].events & POLLIN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t StringSource::read(char * data, size_t len)
|
||||
{
|
||||
if (pos == s.size()) throw EndOfFile("end of string reached");
|
||||
|
|
|
@ -104,6 +104,9 @@ struct BufferedSource : Source
|
|||
|
||||
size_t read(char * data, size_t len) override;
|
||||
|
||||
/**
|
||||
* Return true if the buffer is not empty.
|
||||
*/
|
||||
bool hasData();
|
||||
|
||||
protected:
|
||||
|
@ -162,6 +165,13 @@ struct FdSource : BufferedSource
|
|||
FdSource & operator=(FdSource && s) = default;
|
||||
|
||||
bool good() override;
|
||||
|
||||
/**
|
||||
* Return true if the buffer is not empty after a non-blocking
|
||||
* read.
|
||||
*/
|
||||
bool hasData();
|
||||
|
||||
protected:
|
||||
size_t readUnbuffered(char * data, size_t len) override;
|
||||
private:
|
||||
|
@ -522,15 +532,16 @@ struct FramedSource : Source
|
|||
/**
|
||||
* Write as chunks in the format expected by FramedSource.
|
||||
*
|
||||
* The exception_ptr reference can be used to terminate the stream when you
|
||||
* detect that an error has occurred on the remote end.
|
||||
* The `checkError` function can be used to terminate the stream when you
|
||||
* detect that an error has occurred.
|
||||
*/
|
||||
struct FramedSink : nix::BufferedSink
|
||||
{
|
||||
BufferedSink & to;
|
||||
std::exception_ptr & ex;
|
||||
std::function<void()> checkError;
|
||||
|
||||
FramedSink(BufferedSink & to, std::exception_ptr & ex) : to(to), ex(ex)
|
||||
FramedSink(BufferedSink & to, std::function<void()> && checkError)
|
||||
: to(to), checkError(checkError)
|
||||
{ }
|
||||
|
||||
~FramedSink()
|
||||
|
@ -545,13 +556,9 @@ struct FramedSink : nix::BufferedSink
|
|||
|
||||
void writeUnbuffered(std::string_view data) override
|
||||
{
|
||||
/* Don't send more data if the remote has
|
||||
encountered an error. */
|
||||
if (ex) {
|
||||
auto ex2 = ex;
|
||||
ex = nullptr;
|
||||
std::rethrow_exception(ex2);
|
||||
}
|
||||
/* Don't send more data if an error has occured. */
|
||||
checkError();
|
||||
|
||||
to << data.size();
|
||||
to(data);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue