From b4b6e8df7ba147ac6a76101f35309b6b39977313 Mon Sep 17 00:00:00 2001 From: Daniel Wennberg Date: Mon, 31 Mar 2025 18:36:19 -0700 Subject: [PATCH 1/4] Avoid closure in @safe_* macros --- src/utils.jl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/utils.jl b/src/utils.jl index cc031225..46515fa6 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -69,8 +69,17 @@ for level in [:debug, :info, :warn, :error] # they may expect Logging.shouldlog() getting called, so we use # the global_logger()'s min level which is more likely to be usable. min_level = _invoked_min_enabled_level(global_logger()) - with_logger(Logging.ConsoleLogger(io, min_level)) do + safe_logger = Logging.ConsoleLogger(io, min_level) + # cannot use with_logger in generated functions because it requires a closure + # copy with_logstate implementation instead + safe_logstate = Base.CoreLogging.LogState(safe_logger) + t = current_task() + old_logstate = t.logstate + try + t.logstate = safe_logstate $(esc(macrocall)) + finally + t.logstate = old_logstate end end end From 0972a4af3e8ba2261ac4ab028cc6e057ceea82f2 Mon Sep 17 00:00:00 2001 From: Daniel Wennberg Date: Fri, 11 Apr 2025 09:11:07 -0700 Subject: [PATCH 2/4] Fix >=1.11 compatibility --- src/utils.jl | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 46515fa6..3a4b8fcd 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -54,6 +54,24 @@ function _invoked_min_enabled_level(@nospecialize(logger)) return invoke(Logging.min_enabled_level, Tuple{typeof(logger)}, logger)::LogLevel end +# execute an expression with a custom logstate without creating a closure +macro safe_with_logstate(logstate, expr) + @static if VERSION < v"1.11-" + quote + t = current_task() + old_logstate = t.logstate + try + t.logstate = $logstate + $expr + finally + t.logstate = old_logstate + end + end + else + :(Base.ScopedValues.@with(Base.CoreLogging.CURRENT_LOGSTATE => $logstate, $expr)) + end +end + # define safe loggers for use in generated functions (where task switches are not allowed) for level in [:debug, :info, :warn, :error] @eval begin @@ -70,17 +88,8 @@ for level in [:debug, :info, :warn, :error] # the global_logger()'s min level which is more likely to be usable. min_level = _invoked_min_enabled_level(global_logger()) safe_logger = Logging.ConsoleLogger(io, min_level) - # cannot use with_logger in generated functions because it requires a closure - # copy with_logstate implementation instead safe_logstate = Base.CoreLogging.LogState(safe_logger) - t = current_task() - old_logstate = t.logstate - try - t.logstate = safe_logstate - $(esc(macrocall)) - finally - t.logstate = old_logstate - end + @safe_with_logstate safe_logstate $(esc(macrocall)) end end end From 5021b79431b2e60ce55f8740d88209140149f4fe Mon Sep 17 00:00:00 2001 From: Daniel Wennberg Date: Fri, 11 Apr 2025 09:20:01 -0700 Subject: [PATCH 3/4] Formatting --- src/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.jl b/src/utils.jl index 3a4b8fcd..f7d80e12 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -56,7 +56,7 @@ end # execute an expression with a custom logstate without creating a closure macro safe_with_logstate(logstate, expr) - @static if VERSION < v"1.11-" + return @static if VERSION < v"1.11-" quote t = current_task() old_logstate = t.logstate From 104a6c7c61a0916d80f5a0a7d9bc8af5851e9485 Mon Sep 17 00:00:00 2001 From: Daniel Wennberg Date: Sun, 13 Apr 2025 09:16:23 -0700 Subject: [PATCH 4/4] Remove extra macro --- src/utils.jl | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index f7d80e12..04995428 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -54,24 +54,6 @@ function _invoked_min_enabled_level(@nospecialize(logger)) return invoke(Logging.min_enabled_level, Tuple{typeof(logger)}, logger)::LogLevel end -# execute an expression with a custom logstate without creating a closure -macro safe_with_logstate(logstate, expr) - return @static if VERSION < v"1.11-" - quote - t = current_task() - old_logstate = t.logstate - try - t.logstate = $logstate - $expr - finally - t.logstate = old_logstate - end - end - else - :(Base.ScopedValues.@with(Base.CoreLogging.CURRENT_LOGSTATE => $logstate, $expr)) - end -end - # define safe loggers for use in generated functions (where task switches are not allowed) for level in [:debug, :info, :warn, :error] @eval begin @@ -88,8 +70,23 @@ for level in [:debug, :info, :warn, :error] # the global_logger()'s min level which is more likely to be usable. min_level = _invoked_min_enabled_level(global_logger()) safe_logger = Logging.ConsoleLogger(io, min_level) + # using with_logger would create a closure, which is incompatible with + # generated functions, so instead we reproduce its implementation here safe_logstate = Base.CoreLogging.LogState(safe_logger) - @safe_with_logstate safe_logstate $(esc(macrocall)) + @static if VERSION < v"1.11-" + t = current_task() + old_logstate = t.logstate + try + t.logstate = safe_logstate + $(esc(macrocall)) + finally + t.logstate = old_logstate + end + else + Base.ScopedValues.@with( + Base.CoreLogging.CURRENT_LOGSTATE => safe_logstate, $(esc(macrocall)) + ) + end end end end