I’m trying to create a worker who will pick up tasks from a global list and execute them without blocking the repl (i.e. the worker executes in the background). In the following MWE, I’d expect that adding tasks to the worker would return nothing (or possibly printing if the function prints), and calling tasks would show a decreasing number of tasks until it all tasks are executed. From there, the tasker should wait for a new task to be pushed to the list, and pushing a new task would cause it to be executed by the worker.
So far, either I hit a concurrency violation, the tasks are never executed, or the program blocks the repl.
Here is my MWE:
using Base.Threads
# Global task list, locking mechanism, and condition variable
global tasks = []
global tasks_lock = ReentrantLock() 
global new_task_condition = Threads.Condition()
# Function to add tasks to the global list
function add_task(new_task)
    lock(tasks_lock) do
        push!(tasks, new_task)
   end
   notify(new_task_condition)
end
# Function to define a worker
function worker()
    while true
        local task = nothing
        lock(tasks_lock) do
            while isempty(tasks)
                wait(new_task_condition)
            end
            task = popfirst!(tasks)
        end
        task()
    end
end
# Start a worker in the background
Threads.@spawn worker()
# Add tasks to the queue
add_task(() -> println("Hello, world!"))
add_task(() -> println("Goodbye, world!"))
add_task(() -> println(sum(1:1000)))
add_task(() -> sleep(2); println("Task 2 seconds"))
This snippet gives a concurrency violation.