-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Comments
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 |
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? |
Implemented by #55793 |
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.The text was updated successfully, but these errors were encountered: