mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-14 02:06:16 +02:00
Merge pull request #10678 from nix-windows/windows-substitution-goal
Start building the scheduler for Windows
This commit is contained in:
commit
d0c7da131f
25 changed files with 285 additions and 94 deletions
|
@ -228,18 +228,18 @@
|
||||||
''^src/libstore/store-dir-config\.hh$''
|
''^src/libstore/store-dir-config\.hh$''
|
||||||
''^src/libstore/unix/build/derivation-goal\.cc$''
|
''^src/libstore/unix/build/derivation-goal\.cc$''
|
||||||
''^src/libstore/unix/build/derivation-goal\.hh$''
|
''^src/libstore/unix/build/derivation-goal\.hh$''
|
||||||
''^src/libstore/unix/build/drv-output-substitution-goal\.cc$''
|
''^src/libstore/build/drv-output-substitution-goal\.cc$''
|
||||||
''^src/libstore/unix/build/drv-output-substitution-goal\.hh$''
|
''^src/libstore/build/drv-output-substitution-goal\.hh$''
|
||||||
''^src/libstore/unix/build/entry-points\.cc$''
|
''^src/libstore/build/entry-points\.cc$''
|
||||||
''^src/libstore/unix/build/goal\.cc$''
|
''^src/libstore/build/goal\.cc$''
|
||||||
''^src/libstore/unix/build/goal\.hh$''
|
''^src/libstore/build/goal\.hh$''
|
||||||
''^src/libstore/unix/build/hook-instance\.cc$''
|
''^src/libstore/unix/build/hook-instance\.cc$''
|
||||||
''^src/libstore/unix/build/local-derivation-goal\.cc$''
|
''^src/libstore/unix/build/local-derivation-goal\.cc$''
|
||||||
''^src/libstore/unix/build/local-derivation-goal\.hh$''
|
''^src/libstore/unix/build/local-derivation-goal\.hh$''
|
||||||
''^src/libstore/unix/build/substitution-goal\.cc$''
|
''^src/libstore/build/substitution-goal\.cc$''
|
||||||
''^src/libstore/unix/build/substitution-goal\.hh$''
|
''^src/libstore/build/substitution-goal\.hh$''
|
||||||
''^src/libstore/unix/build/worker\.cc$''
|
''^src/libstore/build/worker\.cc$''
|
||||||
''^src/libstore/unix/build/worker\.hh$''
|
''^src/libstore/build/worker\.hh$''
|
||||||
''^src/libstore/unix/builtins/fetchurl\.cc$''
|
''^src/libstore/unix/builtins/fetchurl\.cc$''
|
||||||
''^src/libstore/unix/builtins/unpack-channel\.cc$''
|
''^src/libstore/unix/builtins/unpack-channel\.cc$''
|
||||||
''^src/libstore/gc\.cc$''
|
''^src/libstore/gc\.cc$''
|
||||||
|
@ -247,8 +247,8 @@
|
||||||
''^src/libstore/unix/local-overlay-store\.hh$''
|
''^src/libstore/unix/local-overlay-store\.hh$''
|
||||||
''^src/libstore/local-store\.cc$''
|
''^src/libstore/local-store\.cc$''
|
||||||
''^src/libstore/local-store\.hh$''
|
''^src/libstore/local-store\.hh$''
|
||||||
''^src/libstore/unix/lock\.cc$''
|
''^src/libstore/unix/user-lock\.cc$''
|
||||||
''^src/libstore/unix/lock\.hh$''
|
''^src/libstore/unix/user-lock\.hh$''
|
||||||
''^src/libstore/optimise-store\.cc$''
|
''^src/libstore/optimise-store\.cc$''
|
||||||
''^src/libstore/unix/pathlocks\.cc$''
|
''^src/libstore/unix/pathlocks\.cc$''
|
||||||
''^src/libstore/posix-fs-canonicalise\.cc$''
|
''^src/libstore/posix-fs-canonicalise\.cc$''
|
||||||
|
|
|
@ -66,7 +66,11 @@ void DrvOutputSubstitutionGoal::tryNext()
|
||||||
some other error occurs), so it must not touch `this`. So put
|
some other error occurs), so it must not touch `this`. So put
|
||||||
the shared state in a separate refcounted object. */
|
the shared state in a separate refcounted object. */
|
||||||
downloadState = std::make_shared<DownloadState>();
|
downloadState = std::make_shared<DownloadState>();
|
||||||
|
#ifndef _WIN32
|
||||||
downloadState->outPipe.create();
|
downloadState->outPipe.create();
|
||||||
|
#else
|
||||||
|
downloadState->outPipe.createAsyncPipe(worker.ioport.get());
|
||||||
|
#endif
|
||||||
|
|
||||||
sub->queryRealisation(
|
sub->queryRealisation(
|
||||||
id,
|
id,
|
||||||
|
@ -79,7 +83,13 @@ void DrvOutputSubstitutionGoal::tryNext()
|
||||||
}
|
}
|
||||||
} });
|
} });
|
||||||
|
|
||||||
worker.childStarted(shared_from_this(), {downloadState->outPipe.readSide.get()}, true, false);
|
worker.childStarted(shared_from_this(), {
|
||||||
|
#ifndef _WIN32
|
||||||
|
downloadState->outPipe.readSide.get()
|
||||||
|
#else
|
||||||
|
&downloadState->outPipe
|
||||||
|
#endif
|
||||||
|
}, true, false);
|
||||||
|
|
||||||
state = &DrvOutputSubstitutionGoal::realisationFetched;
|
state = &DrvOutputSubstitutionGoal::realisationFetched;
|
||||||
}
|
}
|
||||||
|
@ -158,7 +168,7 @@ void DrvOutputSubstitutionGoal::work()
|
||||||
(this->*state)();
|
(this->*state)();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrvOutputSubstitutionGoal::handleEOF(int fd)
|
void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd)
|
||||||
{
|
{
|
||||||
if (fd == downloadState->outPipe.readSide.get()) worker.wakeUp(shared_from_this());
|
if (fd == downloadState->outPipe.readSide.get()) worker.wakeUp(shared_from_this());
|
||||||
}
|
}
|
|
@ -1,11 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "goal.hh"
|
#include "goal.hh"
|
||||||
#include "realisation.hh"
|
#include "realisation.hh"
|
||||||
#include <thread>
|
|
||||||
#include <future>
|
#ifdef _WIN32
|
||||||
|
# include "windows-async-pipe.hh"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -43,7 +48,11 @@ class DrvOutputSubstitutionGoal : public Goal {
|
||||||
|
|
||||||
struct DownloadState
|
struct DownloadState
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
Pipe outPipe;
|
Pipe outPipe;
|
||||||
|
#else
|
||||||
|
windows::AsyncPipe outPipe;
|
||||||
|
#endif
|
||||||
std::promise<std::shared_ptr<const Realisation>> promise;
|
std::promise<std::shared_ptr<const Realisation>> promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -71,7 +80,7 @@ public:
|
||||||
std::string key() override;
|
std::string key() override;
|
||||||
|
|
||||||
void work() override;
|
void work() override;
|
||||||
void handleEOF(int fd) override;
|
void handleEOF(Descriptor fd) override;
|
||||||
|
|
||||||
JobCategory jobCategory() const override {
|
JobCategory jobCategory() const override {
|
||||||
return JobCategory::Substitution;
|
return JobCategory::Substitution;
|
|
@ -1,6 +1,8 @@
|
||||||
#include "worker.hh"
|
#include "worker.hh"
|
||||||
#include "substitution-goal.hh"
|
#include "substitution-goal.hh"
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
# include "derivation-goal.hh"
|
# include "derivation-goal.hh"
|
||||||
|
#endif
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
@ -25,9 +27,12 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
|
||||||
ex = std::move(i->ex);
|
ex = std::move(i->ex);
|
||||||
}
|
}
|
||||||
if (i->exitCode != Goal::ecSuccess) {
|
if (i->exitCode != Goal::ecSuccess) {
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get()))
|
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get()))
|
||||||
failed.insert(printStorePath(i2->drvPath));
|
failed.insert(printStorePath(i2->drvPath));
|
||||||
else if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))
|
else
|
||||||
|
#endif
|
||||||
|
if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))
|
||||||
failed.insert(printStorePath(i2->storePath));
|
failed.insert(printStorePath(i2->storePath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +79,12 @@ BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivat
|
||||||
BuildMode buildMode)
|
BuildMode buildMode)
|
||||||
{
|
{
|
||||||
Worker worker(*this, *this);
|
Worker worker(*this, *this);
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, OutputsSpec::All {}, buildMode);
|
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, OutputsSpec::All {}, buildMode);
|
||||||
|
#else
|
||||||
|
std::shared_ptr<Goal> goal;
|
||||||
|
throw UnimplementedError("Building derivations not yet implemented on windows.");
|
||||||
|
#endif
|
||||||
|
|
||||||
try {
|
try {
|
||||||
worker.run(Goals{goal});
|
worker.run(Goals{goal});
|
|
@ -138,12 +138,12 @@ public:
|
||||||
|
|
||||||
virtual void waiteeDone(GoalPtr waitee, ExitCode result);
|
virtual void waiteeDone(GoalPtr waitee, ExitCode result);
|
||||||
|
|
||||||
virtual void handleChildOutput(int fd, std::string_view data)
|
virtual void handleChildOutput(Descriptor fd, std::string_view data)
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void handleEOF(int fd)
|
virtual void handleEOF(Descriptor fd)
|
||||||
{
|
{
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
|
@ -212,7 +212,11 @@ void PathSubstitutionGoal::tryToRun()
|
||||||
maintainRunningSubstitutions = std::make_unique<MaintainCount<uint64_t>>(worker.runningSubstitutions);
|
maintainRunningSubstitutions = std::make_unique<MaintainCount<uint64_t>>(worker.runningSubstitutions);
|
||||||
worker.updateProgress();
|
worker.updateProgress();
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
outPipe.create();
|
outPipe.create();
|
||||||
|
#else
|
||||||
|
outPipe.createAsyncPipe(worker.ioport.get());
|
||||||
|
#endif
|
||||||
|
|
||||||
promise = std::promise<void>();
|
promise = std::promise<void>();
|
||||||
|
|
||||||
|
@ -235,7 +239,13 @@ void PathSubstitutionGoal::tryToRun()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, false);
|
worker.childStarted(shared_from_this(), {
|
||||||
|
#ifndef _WIN32
|
||||||
|
outPipe.readSide.get()
|
||||||
|
#else
|
||||||
|
&outPipe
|
||||||
|
#endif
|
||||||
|
}, true, false);
|
||||||
|
|
||||||
state = &PathSubstitutionGoal::finished;
|
state = &PathSubstitutionGoal::finished;
|
||||||
}
|
}
|
||||||
|
@ -294,12 +304,12 @@ void PathSubstitutionGoal::finished()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PathSubstitutionGoal::handleChildOutput(int fd, std::string_view data)
|
void PathSubstitutionGoal::handleChildOutput(Descriptor fd, std::string_view data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PathSubstitutionGoal::handleEOF(int fd)
|
void PathSubstitutionGoal::handleEOF(Descriptor fd)
|
||||||
{
|
{
|
||||||
if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this());
|
if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this());
|
||||||
}
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
#include "lock.hh"
|
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "goal.hh"
|
#include "goal.hh"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include "windows-async-pipe.hh"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
class Worker;
|
class Worker;
|
||||||
|
@ -45,7 +48,11 @@ struct PathSubstitutionGoal : public Goal
|
||||||
/**
|
/**
|
||||||
* Pipe for the substituter's standard output.
|
* Pipe for the substituter's standard output.
|
||||||
*/
|
*/
|
||||||
|
#ifndef _WIN32
|
||||||
Pipe outPipe;
|
Pipe outPipe;
|
||||||
|
#else
|
||||||
|
windows::AsyncPipe outPipe;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The substituter thread.
|
* The substituter thread.
|
||||||
|
@ -111,8 +118,8 @@ public:
|
||||||
/**
|
/**
|
||||||
* Callback used by the worker to write to the log.
|
* Callback used by the worker to write to the log.
|
||||||
*/
|
*/
|
||||||
void handleChildOutput(int fd, std::string_view data) override;
|
void handleChildOutput(Descriptor fd, std::string_view data) override;
|
||||||
void handleEOF(int fd) override;
|
void handleEOF(Descriptor fd) override;
|
||||||
|
|
||||||
/* Called by destructor, can't be overridden */
|
/* Called by destructor, can't be overridden */
|
||||||
void cleanup() override final;
|
void cleanup() override final;
|
|
@ -1,12 +1,20 @@
|
||||||
|
#include "local-store.hh"
|
||||||
#include "machines.hh"
|
#include "machines.hh"
|
||||||
#include "worker.hh"
|
#include "worker.hh"
|
||||||
#include "substitution-goal.hh"
|
#include "substitution-goal.hh"
|
||||||
#include "drv-output-substitution-goal.hh"
|
#include "drv-output-substitution-goal.hh"
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
# include "local-derivation-goal.hh"
|
# include "local-derivation-goal.hh"
|
||||||
# include "hook-instance.hh"
|
# include "hook-instance.hh"
|
||||||
|
#endif
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
# include <poll.h>
|
# include <poll.h>
|
||||||
|
#else
|
||||||
|
# include <ioapiset.h>
|
||||||
|
# include "windows-error.hh"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -41,6 +49,7 @@ Worker::~Worker()
|
||||||
assert(expectedNarSize == 0);
|
assert(expectedNarSize == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
|
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath,
|
||||||
|
@ -70,7 +79,6 @@ std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drv
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath & drvPath,
|
std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath & drvPath,
|
||||||
const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode)
|
const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
|
@ -81,6 +89,8 @@ std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<PathSubstitutionGoal> Worker::makePathSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional<ContentAddress> ca)
|
std::shared_ptr<PathSubstitutionGoal> Worker::makePathSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional<ContentAddress> ca)
|
||||||
{
|
{
|
||||||
|
@ -112,10 +122,14 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[&](const DerivedPath::Built & bfd) -> GoalPtr {
|
[&](const DerivedPath::Built & bfd) -> GoalPtr {
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
if (auto bop = std::get_if<DerivedPath::Opaque>(&*bfd.drvPath))
|
if (auto bop = std::get_if<DerivedPath::Opaque>(&*bfd.drvPath))
|
||||||
return makeDerivationGoal(bop->path, bfd.outputs, buildMode);
|
return makeDerivationGoal(bop->path, bfd.outputs, buildMode);
|
||||||
else
|
else
|
||||||
throw UnimplementedError("Building dynamic derivations in one shot is not yet implemented.");
|
throw UnimplementedError("Building dynamic derivations in one shot is not yet implemented.");
|
||||||
|
#else
|
||||||
|
throw UnimplementedError("Building derivations not yet implemented on Windows");
|
||||||
|
#endif
|
||||||
},
|
},
|
||||||
[&](const DerivedPath::Opaque & bo) -> GoalPtr {
|
[&](const DerivedPath::Opaque & bo) -> GoalPtr {
|
||||||
return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair);
|
return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair);
|
||||||
|
@ -141,9 +155,12 @@ static void removeGoal(std::shared_ptr<G> goal, std::map<K, std::weak_ptr<G>> &
|
||||||
|
|
||||||
void Worker::removeGoal(GoalPtr goal)
|
void Worker::removeGoal(GoalPtr goal)
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
|
if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
|
||||||
nix::removeGoal(drvGoal, derivationGoals);
|
nix::removeGoal(drvGoal, derivationGoals);
|
||||||
else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
|
else
|
||||||
|
#endif
|
||||||
|
if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
|
||||||
nix::removeGoal(subGoal, substitutionGoals);
|
nix::removeGoal(subGoal, substitutionGoals);
|
||||||
else if (auto subGoal = std::dynamic_pointer_cast<DrvOutputSubstitutionGoal>(goal))
|
else if (auto subGoal = std::dynamic_pointer_cast<DrvOutputSubstitutionGoal>(goal))
|
||||||
nix::removeGoal(subGoal, drvOutputSubstitutionGoals);
|
nix::removeGoal(subGoal, drvOutputSubstitutionGoals);
|
||||||
|
@ -187,13 +204,13 @@ unsigned Worker::getNrSubstitutions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Worker::childStarted(GoalPtr goal, const std::set<int> & fds,
|
void Worker::childStarted(GoalPtr goal, const std::set<Child::CommChannel> & channels,
|
||||||
bool inBuildSlot, bool respectTimeouts)
|
bool inBuildSlot, bool respectTimeouts)
|
||||||
{
|
{
|
||||||
Child child;
|
Child child;
|
||||||
child.goal = goal;
|
child.goal = goal;
|
||||||
child.goal2 = goal.get();
|
child.goal2 = goal.get();
|
||||||
child.fds = fds;
|
child.channels = channels;
|
||||||
child.timeStarted = child.lastOutput = steady_time_point::clock::now();
|
child.timeStarted = child.lastOutput = steady_time_point::clock::now();
|
||||||
child.inBuildSlot = inBuildSlot;
|
child.inBuildSlot = inBuildSlot;
|
||||||
child.respectTimeouts = respectTimeouts;
|
child.respectTimeouts = respectTimeouts;
|
||||||
|
@ -281,12 +298,15 @@ void Worker::run(const Goals & _topGoals)
|
||||||
|
|
||||||
for (auto & i : _topGoals) {
|
for (auto & i : _topGoals) {
|
||||||
topGoals.insert(i);
|
topGoals.insert(i);
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
if (auto goal = dynamic_cast<DerivationGoal *>(i.get())) {
|
if (auto goal = dynamic_cast<DerivationGoal *>(i.get())) {
|
||||||
topPaths.push_back(DerivedPath::Built {
|
topPaths.push_back(DerivedPath::Built {
|
||||||
.drvPath = makeConstantStorePathRef(goal->drvPath),
|
.drvPath = makeConstantStorePathRef(goal->drvPath),
|
||||||
.outputs = goal->wantedOutputs,
|
.outputs = goal->wantedOutputs,
|
||||||
});
|
});
|
||||||
} else if (auto goal = dynamic_cast<PathSubstitutionGoal *>(i.get())) {
|
} else
|
||||||
|
#endif
|
||||||
|
if (auto goal = dynamic_cast<PathSubstitutionGoal *>(i.get())) {
|
||||||
topPaths.push_back(DerivedPath::Opaque{goal->storePath});
|
topPaths.push_back(DerivedPath::Opaque{goal->storePath});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,13 +428,14 @@ void Worker::waitForInput()
|
||||||
if (useTimeout)
|
if (useTimeout)
|
||||||
vomit("sleeping %d seconds", timeout);
|
vomit("sleeping %d seconds", timeout);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
/* Use select() to wait for the input side of any logger pipe to
|
/* Use select() to wait for the input side of any logger pipe to
|
||||||
become `available'. Note that `available' (i.e., non-blocking)
|
become `available'. Note that `available' (i.e., non-blocking)
|
||||||
includes EOF. */
|
includes EOF. */
|
||||||
std::vector<struct pollfd> pollStatus;
|
std::vector<struct pollfd> pollStatus;
|
||||||
std::map<int, size_t> fdToPollStatus;
|
std::map<int, size_t> fdToPollStatus;
|
||||||
for (auto & i : children) {
|
for (auto & i : children) {
|
||||||
for (auto & j : i.fds) {
|
for (auto & j : i.channels) {
|
||||||
pollStatus.push_back((struct pollfd) { .fd = j, .events = POLLIN });
|
pollStatus.push_back((struct pollfd) { .fd = j, .events = POLLIN });
|
||||||
fdToPollStatus[j] = pollStatus.size() - 1;
|
fdToPollStatus[j] = pollStatus.size() - 1;
|
||||||
}
|
}
|
||||||
|
@ -425,6 +446,28 @@ void Worker::waitForInput()
|
||||||
if (errno == EINTR) return;
|
if (errno == EINTR) return;
|
||||||
throw SysError("waiting for input");
|
throw SysError("waiting for input");
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
OVERLAPPED_ENTRY oentries[0x20] = {0};
|
||||||
|
ULONG removed;
|
||||||
|
bool gotEOF = false;
|
||||||
|
|
||||||
|
// we are on at least Windows Vista / Server 2008 and can get many (countof(oentries)) statuses in one API call
|
||||||
|
if (!GetQueuedCompletionStatusEx(
|
||||||
|
ioport.get(),
|
||||||
|
oentries,
|
||||||
|
sizeof(oentries) / sizeof(*oentries),
|
||||||
|
&removed,
|
||||||
|
useTimeout ? timeout * 1000 : INFINITE,
|
||||||
|
false))
|
||||||
|
{
|
||||||
|
windows::WinError winError("GetQueuedCompletionStatusEx");
|
||||||
|
if (winError.lastError != WAIT_TIMEOUT)
|
||||||
|
throw winError;
|
||||||
|
assert(removed == 0);
|
||||||
|
} else {
|
||||||
|
assert(0 < removed && removed <= sizeof(oentries)/sizeof(*oentries));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
auto after = steady_time_point::clock::now();
|
auto after = steady_time_point::clock::now();
|
||||||
|
|
||||||
|
@ -439,20 +482,21 @@ void Worker::waitForInput()
|
||||||
GoalPtr goal = j->goal.lock();
|
GoalPtr goal = j->goal.lock();
|
||||||
assert(goal);
|
assert(goal);
|
||||||
|
|
||||||
std::set<int> fds2(j->fds);
|
#ifndef _WIN32
|
||||||
|
std::set<Descriptor> fds2(j->channels);
|
||||||
std::vector<unsigned char> buffer(4096);
|
std::vector<unsigned char> buffer(4096);
|
||||||
for (auto & k : fds2) {
|
for (auto & k : fds2) {
|
||||||
const auto fdPollStatusId = get(fdToPollStatus, k);
|
const auto fdPollStatusId = get(fdToPollStatus, k);
|
||||||
assert(fdPollStatusId);
|
assert(fdPollStatusId);
|
||||||
assert(*fdPollStatusId < pollStatus.size());
|
assert(*fdPollStatusId < pollStatus.size());
|
||||||
if (pollStatus.at(*fdPollStatusId).revents) {
|
if (pollStatus.at(*fdPollStatusId).revents) {
|
||||||
ssize_t rd = ::read(k, buffer.data(), buffer.size());
|
ssize_t rd = ::read(fromDescriptorReadOnly(k), buffer.data(), buffer.size());
|
||||||
// FIXME: is there a cleaner way to handle pt close
|
// FIXME: is there a cleaner way to handle pt close
|
||||||
// than EIO? Is this even standard?
|
// than EIO? Is this even standard?
|
||||||
if (rd == 0 || (rd == -1 && errno == EIO)) {
|
if (rd == 0 || (rd == -1 && errno == EIO)) {
|
||||||
debug("%1%: got EOF", goal->getName());
|
debug("%1%: got EOF", goal->getName());
|
||||||
goal->handleEOF(k);
|
goal->handleEOF(k);
|
||||||
j->fds.erase(k);
|
j->channels.erase(k);
|
||||||
} else if (rd == -1) {
|
} else if (rd == -1) {
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
throw SysError("%s: read failed", goal->getName());
|
throw SysError("%s: read failed", goal->getName());
|
||||||
|
@ -465,6 +509,48 @@ void Worker::waitForInput()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
decltype(j->channels)::iterator p = j->channels.begin();
|
||||||
|
while (p != j->channels.end()) {
|
||||||
|
decltype(p) nextp = p;
|
||||||
|
++nextp;
|
||||||
|
for (ULONG i = 0; i < removed; i++) {
|
||||||
|
if (oentries[i].lpCompletionKey == ((ULONG_PTR)((*p)->readSide.get()) ^ 0x5555)) {
|
||||||
|
printMsg(lvlVomit, "%s: read %s bytes", goal->getName(), oentries[i].dwNumberOfBytesTransferred);
|
||||||
|
if (oentries[i].dwNumberOfBytesTransferred > 0) {
|
||||||
|
std::string data {
|
||||||
|
(char *) (*p)->buffer.data(),
|
||||||
|
oentries[i].dwNumberOfBytesTransferred,
|
||||||
|
};
|
||||||
|
//std::cerr << "read [" << data << "]" << std::endl;
|
||||||
|
j->lastOutput = after;
|
||||||
|
goal->handleChildOutput((*p)->readSide.get(), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gotEOF) {
|
||||||
|
debug("%s: got EOF", goal->getName());
|
||||||
|
goal->handleEOF((*p)->readSide.get());
|
||||||
|
nextp = j->channels.erase(p); // no need to maintain `j->channels` ?
|
||||||
|
} else {
|
||||||
|
BOOL rc = ReadFile((*p)->readSide.get(), (*p)->buffer.data(), (*p)->buffer.size(), &(*p)->got, &(*p)->overlapped);
|
||||||
|
if (rc) {
|
||||||
|
// here is possible (but not obligatory) to call `goal->handleChildOutput` and repeat ReadFile immediately
|
||||||
|
} else {
|
||||||
|
windows::WinError winError("ReadFile(%s, ..)", (*p)->readSide.get());
|
||||||
|
if (winError.lastError == ERROR_BROKEN_PIPE) {
|
||||||
|
debug("%s: got EOF", goal->getName());
|
||||||
|
goal->handleEOF((*p)->readSide.get());
|
||||||
|
nextp = j->channels.erase(p); // no need to maintain `j->channels` ?
|
||||||
|
} else if (winError.lastError != ERROR_IO_PENDING)
|
||||||
|
throw winError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = nextp;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (goal->exitCode == Goal::ecBusy &&
|
if (goal->exitCode == Goal::ecBusy &&
|
||||||
0 != settings.maxSilentTime &&
|
0 != settings.maxSilentTime &&
|
|
@ -2,18 +2,23 @@
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "lock.hh"
|
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "goal.hh"
|
#include "goal.hh"
|
||||||
#include "realisation.hh"
|
#include "realisation.hh"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include "windows-async-pipe.hh"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
/* Forward definition. */
|
/* Forward definition. */
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
struct DerivationGoal;
|
struct DerivationGoal;
|
||||||
|
#endif
|
||||||
struct PathSubstitutionGoal;
|
struct PathSubstitutionGoal;
|
||||||
class DrvOutputSubstitutionGoal;
|
class DrvOutputSubstitutionGoal;
|
||||||
|
|
||||||
|
@ -36,14 +41,22 @@ typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping used to remember for each child process to what goal it
|
* A mapping used to remember for each child process to what goal it
|
||||||
* belongs, and file descriptors for receiving log data and output
|
* belongs, and comm channels for receiving log data and output
|
||||||
* path creation commands.
|
* path creation commands.
|
||||||
*/
|
*/
|
||||||
struct Child
|
struct Child
|
||||||
{
|
{
|
||||||
|
using CommChannel =
|
||||||
|
#ifndef _WIN32
|
||||||
|
Descriptor
|
||||||
|
#else
|
||||||
|
windows::AsyncPipe *
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
WeakGoalPtr goal;
|
WeakGoalPtr goal;
|
||||||
Goal * goal2; // ugly hackery
|
Goal * goal2; // ugly hackery
|
||||||
std::set<int> fds;
|
std::set<CommChannel> channels;
|
||||||
bool respectTimeouts;
|
bool respectTimeouts;
|
||||||
bool inBuildSlot;
|
bool inBuildSlot;
|
||||||
/**
|
/**
|
||||||
|
@ -53,8 +66,10 @@ struct Child
|
||||||
steady_time_point timeStarted;
|
steady_time_point timeStarted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
/* Forward definition. */
|
/* Forward definition. */
|
||||||
struct HookInstance;
|
struct HookInstance;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The worker class.
|
* The worker class.
|
||||||
|
@ -101,7 +116,9 @@ private:
|
||||||
* Maps used to prevent multiple instantiations of a goal for the
|
* Maps used to prevent multiple instantiations of a goal for the
|
||||||
* same derivation / path.
|
* same derivation / path.
|
||||||
*/
|
*/
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
std::map<StorePath, std::weak_ptr<DerivationGoal>> derivationGoals;
|
std::map<StorePath, std::weak_ptr<DerivationGoal>> derivationGoals;
|
||||||
|
#endif
|
||||||
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
|
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
|
||||||
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
||||||
|
|
||||||
|
@ -152,10 +169,16 @@ public:
|
||||||
*/
|
*/
|
||||||
bool checkMismatch;
|
bool checkMismatch;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
AutoCloseFD ioport;
|
||||||
|
#endif
|
||||||
|
|
||||||
Store & store;
|
Store & store;
|
||||||
Store & evalStore;
|
Store & evalStore;
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
std::unique_ptr<HookInstance> hook;
|
std::unique_ptr<HookInstance> hook;
|
||||||
|
#endif
|
||||||
|
|
||||||
uint64_t expectedBuilds = 0;
|
uint64_t expectedBuilds = 0;
|
||||||
uint64_t doneBuilds = 0;
|
uint64_t doneBuilds = 0;
|
||||||
|
@ -184,6 +207,7 @@ public:
|
||||||
* Make a goal (with caching).
|
* Make a goal (with caching).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
/**
|
/**
|
||||||
* @ref DerivationGoal "derivation goal"
|
* @ref DerivationGoal "derivation goal"
|
||||||
*/
|
*/
|
||||||
|
@ -198,6 +222,7 @@ public:
|
||||||
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
|
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
|
||||||
const StorePath & drvPath, const BasicDerivation & drv,
|
const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
|
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ref SubstitutionGoal "substitution goal"
|
* @ref SubstitutionGoal "substitution goal"
|
||||||
|
@ -238,7 +263,7 @@ public:
|
||||||
* Registers a running child process. `inBuildSlot` means that
|
* Registers a running child process. `inBuildSlot` means that
|
||||||
* the process counts towards the jobs limit.
|
* the process counts towards the jobs limit.
|
||||||
*/
|
*/
|
||||||
void childStarted(GoalPtr goal, const std::set<int> & fds,
|
void childStarted(GoalPtr goal, const std::set<Child::CommChannel> & channels,
|
||||||
bool inBuildSlot, bool respectTimeouts);
|
bool inBuildSlot, bool respectTimeouts);
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -4,7 +4,7 @@ libstore_NAME = libnixstore
|
||||||
|
|
||||||
libstore_DIR := $(d)
|
libstore_DIR := $(d)
|
||||||
|
|
||||||
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc)
|
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc)
|
||||||
ifdef HOST_UNIX
|
ifdef HOST_UNIX
|
||||||
libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc)
|
libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc)
|
||||||
endif
|
endif
|
||||||
|
@ -43,7 +43,7 @@ endif
|
||||||
|
|
||||||
INCLUDE_libstore := -I $(d) -I $(d)/build
|
INCLUDE_libstore := -I $(d) -I $(d)/build
|
||||||
ifdef HOST_UNIX
|
ifdef HOST_UNIX
|
||||||
INCLUDE_libstore += -I $(d)/unix
|
INCLUDE_libstore += -I $(d)/unix -I $(d)/unix/build
|
||||||
endif
|
endif
|
||||||
ifdef HOST_LINUX
|
ifdef HOST_LINUX
|
||||||
INCLUDE_libstore += -I $(d)/linux
|
INCLUDE_libstore += -I $(d)/linux
|
||||||
|
|
|
@ -1280,7 +1280,7 @@ bool DerivationGoal::isReadDesc(int fd)
|
||||||
return fd == hook->builderOut.readSide.get();
|
return fd == hook->builderOut.readSide.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DerivationGoal::handleChildOutput(int fd, std::string_view data)
|
void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data)
|
||||||
{
|
{
|
||||||
// local & `ssh://`-builds are dealt with here.
|
// local & `ssh://`-builds are dealt with here.
|
||||||
auto isWrittenToLog = isReadDesc(fd);
|
auto isWrittenToLog = isReadDesc(fd);
|
||||||
|
@ -1347,7 +1347,7 @@ void DerivationGoal::handleChildOutput(int fd, std::string_view data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::handleEOF(int fd)
|
void DerivationGoal::handleEOF(Descriptor fd)
|
||||||
{
|
{
|
||||||
if (!currentLogLine.empty()) flushLine();
|
if (!currentLogLine.empty()) flushLine();
|
||||||
worker.wakeUp(shared_from_this());
|
worker.wakeUp(shared_from_this());
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
#include "parsed-derivations.hh"
|
#include "parsed-derivations.hh"
|
||||||
#include "lock.hh"
|
#include "user-lock.hh"
|
||||||
#include "outputs-spec.hh"
|
#include "outputs-spec.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
||||||
|
@ -292,8 +292,8 @@ struct DerivationGoal : public Goal
|
||||||
/**
|
/**
|
||||||
* Callback used by the worker to write to the log.
|
* Callback used by the worker to write to the log.
|
||||||
*/
|
*/
|
||||||
void handleChildOutput(int fd, std::string_view data) override;
|
void handleChildOutput(Descriptor fd, std::string_view data) override;
|
||||||
void handleEOF(int fd) override;
|
void handleEOF(Descriptor fd) override;
|
||||||
void flushLine();
|
void flushLine();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "lock.hh"
|
#include "user-lock.hh"
|
||||||
#include "file-system.hh"
|
#include "file-system.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
|
@ -1,37 +0,0 @@
|
||||||
#include "store-api.hh"
|
|
||||||
#include "build-result.hh"
|
|
||||||
|
|
||||||
namespace nix {
|
|
||||||
|
|
||||||
void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode, std::shared_ptr<Store> evalStore)
|
|
||||||
{
|
|
||||||
unsupported("buildPaths");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<KeyedBuildResult> Store::buildPathsWithResults(
|
|
||||||
const std::vector<DerivedPath> & reqs,
|
|
||||||
BuildMode buildMode,
|
|
||||||
std::shared_ptr<Store> evalStore)
|
|
||||||
{
|
|
||||||
unsupported("buildPathsWithResults");
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
|
||||||
BuildMode buildMode)
|
|
||||||
{
|
|
||||||
unsupported("buildDerivation");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Store::ensurePath(const StorePath & path)
|
|
||||||
{
|
|
||||||
unsupported("ensurePath");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Store::repairPath(const StorePath & path)
|
|
||||||
{
|
|
||||||
unsupported("repairPath");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::windows;
|
||||||
|
|
||||||
void deleteLockFile(const Path & path, Descriptor desc)
|
void deleteLockFile(const Path & path, Descriptor desc)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -206,11 +206,11 @@ MakeError(SystemError, Error);
|
||||||
*
|
*
|
||||||
* Throw this, but prefer not to catch this, and catch `SystemError`
|
* Throw this, but prefer not to catch this, and catch `SystemError`
|
||||||
* instead. This allows implementations to freely switch between this
|
* instead. This allows implementations to freely switch between this
|
||||||
* and `WinError` without breaking catch blocks.
|
* and `windows::WinError` without breaking catch blocks.
|
||||||
*
|
*
|
||||||
* However, it is permissible to catch this and rethrow so long as
|
* However, it is permissible to catch this and rethrow so long as
|
||||||
* certain conditions are not met (e.g. to catch only if `errNo =
|
* certain conditions are not met (e.g. to catch only if `errNo =
|
||||||
* EFooBar`). In that case, try to also catch the equivalent `WinError`
|
* EFooBar`). In that case, try to also catch the equivalent `windows::WinError`
|
||||||
* code.
|
* code.
|
||||||
*
|
*
|
||||||
* @todo Rename this to `PosixError` or similar. At this point Windows
|
* @todo Rename this to `PosixError` or similar. At this point Windows
|
||||||
|
@ -248,7 +248,9 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
namespace windows {
|
||||||
class WinError;
|
class WinError;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -258,7 +260,7 @@ class WinError;
|
||||||
*/
|
*/
|
||||||
using NativeSysError =
|
using NativeSysError =
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
WinError
|
windows::WinError
|
||||||
#else
|
#else
|
||||||
SysError
|
SysError
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -136,7 +136,7 @@ size_t FdSource::readUnbuffered(char * data, size_t len)
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
if (!::ReadFile(fd, data, len, &n, NULL)) {
|
if (!::ReadFile(fd, data, len, &n, NULL)) {
|
||||||
_good = false;
|
_good = false;
|
||||||
throw WinError("ReadFile when FdSource::readUnbuffered");
|
throw windows::WinError("ReadFile when FdSource::readUnbuffered");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::windows;
|
||||||
|
|
||||||
std::string readFile(HANDLE handle)
|
std::string readFile(HANDLE handle)
|
||||||
{
|
{
|
||||||
LARGE_INTEGER li;
|
LARGE_INTEGER li;
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
using namespace nix::windows;
|
||||||
|
|
||||||
std::string getUserName()
|
std::string getUserName()
|
||||||
{
|
{
|
||||||
// Get the required buffer size
|
// Get the required buffer size
|
||||||
|
|
43
src/libutil/windows/windows-async-pipe.cc
Normal file
43
src/libutil/windows/windows-async-pipe.cc
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#include "windows-async-pipe.hh"
|
||||||
|
#include "windows-error.hh"
|
||||||
|
|
||||||
|
namespace nix::windows {
|
||||||
|
|
||||||
|
void AsyncPipe::createAsyncPipe(HANDLE iocp)
|
||||||
|
{
|
||||||
|
// std::cerr << (format("-----AsyncPipe::createAsyncPipe(%x)") % iocp) << std::endl;
|
||||||
|
|
||||||
|
buffer.resize(0x1000);
|
||||||
|
memset(&overlapped, 0, sizeof(overlapped));
|
||||||
|
|
||||||
|
std::string pipeName = fmt("\\\\.\\pipe\\nix-%d-%p", GetCurrentProcessId(), (void *) this);
|
||||||
|
|
||||||
|
readSide = CreateNamedPipeA(
|
||||||
|
pipeName.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 0, 0,
|
||||||
|
INFINITE, NULL);
|
||||||
|
if (!readSide)
|
||||||
|
throw WinError("CreateNamedPipeA(%s)", pipeName);
|
||||||
|
|
||||||
|
HANDLE hIocp = CreateIoCompletionPort(readSide.get(), iocp, (ULONG_PTR) (readSide.get()) ^ 0x5555, 0);
|
||||||
|
if (hIocp != iocp)
|
||||||
|
throw WinError("CreateIoCompletionPort(%x[%s], %x, ...) returned %x", readSide.get(), pipeName, iocp, hIocp);
|
||||||
|
|
||||||
|
if (!ConnectNamedPipe(readSide.get(), &overlapped) && GetLastError() != ERROR_IO_PENDING)
|
||||||
|
throw WinError("ConnectNamedPipe(%s)", pipeName);
|
||||||
|
|
||||||
|
SECURITY_ATTRIBUTES psa2 = {0};
|
||||||
|
psa2.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
|
psa2.bInheritHandle = TRUE;
|
||||||
|
|
||||||
|
writeSide = CreateFileA(pipeName.c_str(), GENERIC_WRITE, 0, &psa2, OPEN_EXISTING, 0, NULL);
|
||||||
|
if (!readSide)
|
||||||
|
throw WinError("CreateFileA(%s)", pipeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncPipe::close()
|
||||||
|
{
|
||||||
|
readSide.close();
|
||||||
|
writeSide.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
src/libutil/windows/windows-async-pipe.hh
Normal file
20
src/libutil/windows/windows-async-pipe.hh
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include "file-descriptor.hh"
|
||||||
|
|
||||||
|
namespace nix::windows {
|
||||||
|
|
||||||
|
class AsyncPipe
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AutoCloseFD writeSide, readSide;
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
DWORD got;
|
||||||
|
std::vector<unsigned char> buffer;
|
||||||
|
|
||||||
|
void createAsyncPipe(HANDLE iocp);
|
||||||
|
void close();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix::windows {
|
||||||
|
|
||||||
std::string WinError::renderError(DWORD lastError)
|
std::string WinError::renderError(DWORD lastError)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "error.hh"
|
#include "error.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix::windows {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Windows Error type.
|
* Windows Error type.
|
||||||
|
|
Loading…
Reference in a new issue