Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request for clarification on the thread-safety (or lack thereof) of the Julia logging system #57376

Open
simsurace opened this issue Feb 12, 2025 · 6 comments
Labels
logging The logging framework multithreading Base.Threads and related functionality

Comments

@simsurace
Copy link
Contributor

I did not find any (recent) issues tracking this, so feel free to mark as a duplicate if there is one.

There were some recent messages across various Slack channels that implied that logging is somehow inherently thread-unsafe. I believe that a lot of people were confused/surprised by this. I repeatedly asked for clarification on Slack. I don't want to put further pressure on anyone in particular, but I feel like this should be clarified by someone qualified to do so.

@Seelengrab Seelengrab added the logging The logging framework label Feb 12, 2025
@nsajko nsajko added the multithreading Base.Threads and related functionality label Feb 12, 2025
@Moelf
Copy link
Contributor

Moelf commented Feb 14, 2025

I think one of the more surprising thing is described by @vtjnash as:

Yes, the lowest level does, but at the slightly higher level, having any @log-type statements present in packages that might run in parallel (such as Pkg since the REPL now runs it using threads) may corrupt random memory and segfault (or merely corrupt) the program

@IanButterworth
Copy link
Member

Attempts to add tests that demonstrate the issue here #57448

@IanButterworth
Copy link
Member

We discussed this in the multithreading community call (link) and identified three potential threadsafety issues:

ConsoleLogger

  1. maxlog (if specified) is implemented within handle_message using a Dict logger.message_limits without a lock to get/store counts.
    remaining = get!(logger.message_limits, id, Int(maxlog)::Int)
  2. logger.stream isn't locked
    write(stream, take!(buf))

Global logger

  1. Changing the global logger is done without a lock. A lock that calls to handle_message etc. should take.
    global _global_logstate::LogState
    """
    global_logger()
    Return the global logger, used to receive messages when no specific logger
    exists for the current task.
    global_logger(logger)
    Set the global logger to `logger`, and return the previous global logger.
    """
    global_logger() = _global_logstate.logger
    function global_logger(logger::AbstractLogger)
    prev = _global_logstate.logger
    global _global_logstate = LogState(logger)
    prev
    end

@JamesWrigley
Copy link
Contributor

Am I missing something or won't all of these lead to regular exceptions rather than segfaults?

@vtjnash
Copy link
Member

vtjnash commented Feb 20, 2025

Data races will lead to arbitrary UB, which will probably do pretty bad things (the best of which is to segfault)

@IanButterworth
Copy link
Member

@vtjnash are there any other issues you're aware of?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
logging The logging framework multithreading Base.Threads and related functionality
Projects
None yet
Development

No branches or pull requests

7 participants