Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/libexpr-tests/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ class CaptureLogger : public Logger

class CaptureLogging
{
std::unique_ptr<Logger> oldLogger;
Logger * oldLogger;
public:
CaptureLogging()
{
oldLogger = std::move(logger);
logger = std::make_unique<CaptureLogger>();
oldLogger = logger;
logger = new CaptureLogger();
}

~CaptureLogging()
{
logger = std::move(oldLogger);
logger = oldLogger;
}
};

Expand Down Expand Up @@ -144,7 +144,7 @@ TEST_F(PrimOpTest, trace)
CaptureLogging l;
auto v = eval("builtins.trace \"test string 123\" 123");
ASSERT_THAT(v, IsIntEq(123));
auto text = (dynamic_cast<CaptureLogger *>(logger.get()))->get();
auto text = (dynamic_cast<CaptureLogger *>(logger))->get();
ASSERT_NE(text.find("test string 123"), std::string::npos);
}

Expand Down
2 changes: 1 addition & 1 deletion src/libmain/loggers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void setLogFormat(const std::string & logFormatStr)
void setLogFormat(const LogFormat & logFormat)
{
defaultLogFormat = logFormat;
logger = makeDefaultLogger();
logger = makeDefaultLogger().release();
}

} // namespace nix
9 changes: 3 additions & 6 deletions src/libstore/daemon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1074,14 +1074,11 @@ void processConnection(ref<Store> store, FdSource && from, FdSink && to, Trusted
conn.to = std::move(to);
conn.from = std::move(from);

auto tunnelLogger_ = std::make_unique<TunnelLogger>(conn.to, conn.protoVersion);
auto tunnelLogger = tunnelLogger_.get();
std::unique_ptr<Logger> prevLogger_;
auto prevLogger = logger.get();
auto tunnelLogger = new TunnelLogger(conn.to, conn.protoVersion);
auto prevLogger = logger;
// FIXME
if (!recursive) {
prevLogger_ = std::move(logger);
logger = std::move(tunnelLogger_);
logger = tunnelLogger;
applyJSONLogger();
}

Expand Down
2 changes: 1 addition & 1 deletion src/libstore/unix/build/child.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace nix {

void commonChildInit()
{
logger = makeSimpleLogger();
logger = makeSimpleLogger().release();

const static std::string pathNullDevice = "/dev/null";
restoreProcessContext(false);
Expand Down
2 changes: 1 addition & 1 deletion src/libstore/unix/build/derivation-builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1408,7 +1408,7 @@ void DerivationBuilderImpl::runChild(RunChildArgs args)
/* If this is a builtin builder, call it now. This should not return. */
if (drv.isBuiltin()) {
try {
logger = makeJSONLogger(getStandardError());
logger = makeJSONLogger(getStandardError()).release();

for (auto & e : drv.outputs)
ctx.outputs.insert_or_assign(e.first, store.printStorePath(scratchOutputs.at(e.first)));
Expand Down
2 changes: 1 addition & 1 deletion src/libutil-c/nix_api_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ nix_err nix_set_logger(nix_c_context * context, const nix_logger * vtable, void
if (vtable == nullptr)
return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "logger vtable is null");
try {
nix::logger = std::make_unique<CallbackLogger>(*vtable, userdata);
nix::logger = new CallbackLogger(*vtable, userdata);
}
Comment thread
edolstra marked this conversation as resolved.
NIXC_CATCH_ERRS
}
Expand Down
12 changes: 6 additions & 6 deletions src/libutil-tests/nix_api_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ TEST_F(nix_api_util_context, nix_set_logger_routes_log_calls)
LogCapture capture;
// Restore the default logger before `capture` goes out of scope so
// the destroy callback runs while `capture` is still alive.
Finally restoreLogger([] { nix::logger = nix::makeSimpleLogger(); });
Finally restoreLogger([] { nix::logger = nix::makeSimpleLogger().release(); });

ASSERT_EQ(nix_set_logger(ctx, &captureLoggerVtable, &capture), NIX_OK);

Expand All @@ -163,7 +163,7 @@ TEST_F(nix_api_util_context, nix_set_logger_routes_log_calls)
TEST_F(nix_api_util_context, nix_set_logger_routes_activities_and_results)
{
LogCapture capture;
Finally restoreLogger([] { nix::logger = nix::makeSimpleLogger(); });
Finally restoreLogger([] { nix::logger = nix::makeSimpleLogger().release(); });

ASSERT_EQ(nix_set_logger(ctx, &captureLoggerVtable, &capture), NIX_OK);

Expand Down Expand Up @@ -191,20 +191,20 @@ TEST_F(nix_api_util_context, nix_set_logger_routes_activities_and_results)
EXPECT_EQ(capture.stops[0], actId);
}

TEST_F(nix_api_util_context, nix_set_logger_invokes_destroy_when_replaced)
TEST_F(nix_api_util_context, nix_set_logger_does_not_invoke_destroy_when_replaced)
{
// Both captures must outlive the Finally so that the logger held
// by `nix::logger` at teardown can call destroy on a live capture.
Comment on lines 196 to 197

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Comment contradicts test behavior regarding destroy lifecycle.

The comment states that "the logger held by nix::logger at teardown can call destroy on a live capture", suggesting destroy will eventually be called. However:

  • Line 207 verifies that destroy is NOT called when replacing the logger
  • The leaked-logger model (using raw pointers) means loggers are intentionally not destroyed
  • This contradicts the comment's implication that destroy will fire at teardown

Consider clarifying whether destroy is expected to be called at any point in the test lifecycle, or remove the reference to destroy being called.

📝 Suggested clarification
-    // Both captures must outlive the Finally so that the logger held
-    // by `nix::logger` at teardown can call destroy on a live capture.
+    // Both captures must outlive the Finally block.
+    // Note: destroy is not called when replacing loggers.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Both captures must outlive the Finally so that the logger held
// by `nix::logger` at teardown can call destroy on a live capture.
// Both captures must outlive the Finally block.
// Note: destroy is not called when replacing loggers.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/libutil-tests/nix_api_util.cc` around lines 196 - 197, The comment near
the Finally in nix_api_util.cc incorrectly implies that nix::logger's teardown
will call destroy on a live capture; update the comment to match test behavior
by either removing the reference to destroy being called at teardown or
explicitly stating that destroy is not expected to be called (e.g., because the
leaked-logger model with raw pointers intentionally avoids destruction and line
207 verifies destroy is NOT called when replacing the logger). Mention the
relevant symbols Finally and nix::logger/destroy so future readers see the
intended lifecycle.

LogCapture capture;
LogCapture capture2;
Finally restoreLogger([] { nix::logger = nix::makeSimpleLogger(); });
Finally restoreLogger([] { nix::logger = nix::makeSimpleLogger().release(); });

ASSERT_EQ(nix_set_logger(ctx, &captureLoggerVtable, &capture), NIX_OK);
EXPECT_FALSE(capture.destroyed);

// Replacing the logger must fire destroy on the old userdata.
// Replacing the logger won't destroy the old logger.
ASSERT_EQ(nix_set_logger(ctx, &captureLoggerVtable, &capture2), NIX_OK);
EXPECT_TRUE(capture.destroyed);
EXPECT_FALSE(capture.destroyed);
EXPECT_FALSE(capture2.destroyed);
}

Expand Down
2 changes: 1 addition & 1 deletion src/libutil/include/nix/util/logging.hh
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ struct PushActivity
}
};

extern std::unique_ptr<Logger> logger;
extern Logger * logger;

std::unique_ptr<Logger> makeSimpleLogger(bool printBuildLogs = true);

Expand Down
8 changes: 6 additions & 2 deletions src/libutil/logging.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ void setCurActivity(const ActivityId activityId)
curActivity = activityId;
}

std::unique_ptr<Logger> logger = makeSimpleLogger(true);
/**
* This is a raw pointer to allow it to leak.
* Avoids races in activity teardown.
*/
Logger * logger = makeSimpleLogger(true).release();

void Logger::warn(const std::string & msg)
{
Expand Down Expand Up @@ -403,7 +407,7 @@ void applyJSONLogger()
std::vector<std::unique_ptr<Logger>> loggers;
loggers.push_back(makeJSONLogger(*opt, false));
try {
logger = makeTeeLogger(std::move(logger), std::move(loggers));
logger = makeTeeLogger(std::unique_ptr<Logger>(logger), std::move(loggers)).release();
} catch (...) {
// `logger` is now gone so give up.
abort();
Expand Down
7 changes: 3 additions & 4 deletions src/libutil/unix/processes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -210,16 +210,15 @@ static int childEntry(void * arg)

pid_t startProcess(fun<void()> processMain, const ProcessOptions & options)
{
auto newLogger = makeSimpleLogger();
auto newLogger = makeSimpleLogger().release();
ChildWrapperFunction wrapper = [&] {
if (!options.allowVfork) {
/* Set a simple logger, while releasing (not destroying)
/* Set a simple logger, while leaking (not destroying)
the parent logger. We don't want to run the parent
logger's destructor since that will crash (e.g. when
~ProgressBar() tries to join a thread that doesn't
exist. */
logger.release();
logger = std::move(newLogger);
logger = newLogger;
}
try {
#ifdef __linux__
Expand Down
2 changes: 1 addition & 1 deletion src/nix/build-remote/build-remote.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ static bool allSupportedLocally(Store & store, const StringSet & requiredFeature
static int main_build_remote(int argc, char ** argv)
{
{
logger = makeJSONLogger(getStandardError());
logger = makeJSONLogger(getStandardError()).release();

/* Ensure we don't get any SSH passphrase or host key popups. */
unsetenv("DISPLAY");
Expand Down
Loading