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

Provide a julia alternative of pthread_once #54042

Closed
KristofferC opened this issue Apr 11, 2024 · 3 comments
Closed

Provide a julia alternative of pthread_once #54042

KristofferC opened this issue Apr 11, 2024 · 3 comments
Labels
feature Indicates new feature / enhancement requests multithreading Base.Threads and related functionality

Comments

@KristofferC
Copy link
Member

pthread_once is a way to execute a function only once (https://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_once.html) and assure that the function has run after it returns. This would probably be useful to provide in Julia.

@KristofferC KristofferC added the multithreading Base.Threads and related functionality label Apr 11, 2024
@KristofferC
Copy link
Member Author

I attempted an implementation of this but my multithreading skills are way too shaky to dare make it into a proper PR. I don't really know what the best behavior is when executing the init functions throws. The code below just let's another thread try the initialization but maybe that is a bad idea.

using Base.Threads

const EXECUTE_ONCE_NOT_INITIALIZED = 0
const EXECUTE_ONCE_IN_PROGRESS = 1
const EXECUTE_ONCE_DONE = 2

mutable struct OnceControl
    state::Atomic{Int}
    condition::Threads.Condition
    OnceControl() = new(Atomic{Int}(EXECUTE_ONCE_NOT_INITIALIZED), Threads.Condition())
end

function execute_once!(once_control::OnceControl, @nospecialize(init_func::Function))
    if once_control.state[] != EXECUTE_ONCE_DONE
        execute_once_init!(once_control, init_func)
    end
end

function execute_once_init!(once_control::OnceControl, @nospecialize(init_func::Function))
    while true
        lock(once_control.condition)
        try
            val = once_control.state[]
            if val == EXECUTE_ONCE_DONE
                return
            elseif val == EXECUTE_ONCE_IN_PROGRESS
                wait(once_control.condition)
                continue
            end

            if atomic_cas!(once_control.state, EXECUTE_ONCE_NOT_INITIALIZED, EXECUTE_ONCE_IN_PROGRESS) == EXECUTE_ONCE_NOT_INITIALIZED
                try
                    init_func()  # Perform the initialization
                    once_control.state[] = EXECUTE_ONCE_DONE
                    notify(once_control.condition)
                catch ex
                    once_control.state[] = EXECUTE_ONCE_NOT_INITIALIZED
                    notify(once_control.condition)
                    rethrow()
                end
                break
            end
        finally
            unlock(once_control.condition)
        end
    end
end

function example_init()
    println("Example initialization executed by $(Threads.threadid()).")
    error()
end

const once_control = OnceControl()
@sync for i in 1:10
    Threads.@spawn execute_once!(once_control, example_init)
end

@quinnj
Copy link
Member

quinnj commented Apr 20, 2024

Maybe this could be simplified as:

mutable struct OnceControl
    @atomic set
    OnceControl() = new()
end

function execute_once!(oc::OnceControl, f)
    if @atomiconce(oc.set = true)
        f()
    end
end

function example_init()
    println("Example initialization executed by $(Threads.threadid()).")
    error()
end

const once_control = OnceControl()
@sync for i in 1:10
    Threads.@spawn execute_once!(once_control, example_init)
end

I don't personally love the idea of just retrying on a different thread on failure. I think if it fails, it fails, with no retry. Unless you have some kind of specific failure mode in mind that would warrant a retry? Or is there some behavior from pthread_once you're trying to emulate?

@nsajko nsajko added the feature Indicates new feature / enhancement requests label Aug 9, 2024
@maleadt
Copy link
Member

maleadt commented Nov 6, 2024

Implemented by #55793

@maleadt maleadt closed this as completed Nov 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Indicates new feature / enhancement requests multithreading Base.Threads and related functionality
Projects
None yet
Development

No branches or pull requests

4 participants