mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-21 05:45:19 +00:00
Add task-stopping functionality to thread.Pool
This commit is contained in:
@@ -44,6 +44,29 @@ Pool :: struct {
|
||||
tasks_done: [dynamic]Task,
|
||||
}
|
||||
|
||||
Pool_Thread_Data :: struct {
|
||||
pool: ^Pool,
|
||||
task: Task,
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
pool_thread_runner :: proc(t: ^Thread) {
|
||||
data := cast(^Pool_Thread_Data)t.data
|
||||
pool := data.pool
|
||||
|
||||
for intrinsics.atomic_load(&pool.is_running) {
|
||||
sync.wait(&pool.sem_available)
|
||||
|
||||
if task, ok := pool_pop_waiting(pool); ok {
|
||||
data.task = task
|
||||
pool_do_work(pool, task)
|
||||
data.task = {}
|
||||
}
|
||||
}
|
||||
|
||||
sync.post(&pool.sem_available, 1)
|
||||
}
|
||||
|
||||
// Once initialized, the pool's memory address is not allowed to change until
|
||||
// it is destroyed.
|
||||
//
|
||||
@@ -58,21 +81,11 @@ pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
|
||||
pool.is_running = true
|
||||
|
||||
for _, i in pool.threads {
|
||||
t := create(proc(t: ^Thread) {
|
||||
pool := (^Pool)(t.data)
|
||||
|
||||
for intrinsics.atomic_load(&pool.is_running) {
|
||||
sync.wait(&pool.sem_available)
|
||||
|
||||
if task, ok := pool_pop_waiting(pool); ok {
|
||||
pool_do_work(pool, task)
|
||||
}
|
||||
}
|
||||
|
||||
sync.post(&pool.sem_available, 1)
|
||||
})
|
||||
t := create(pool_thread_runner)
|
||||
data := new(Pool_Thread_Data)
|
||||
data.pool = pool
|
||||
t.user_index = i
|
||||
t.data = pool
|
||||
t.data = data
|
||||
pool.threads[i] = t
|
||||
}
|
||||
}
|
||||
@@ -82,6 +95,8 @@ pool_destroy :: proc(pool: ^Pool) {
|
||||
delete(pool.tasks_done)
|
||||
|
||||
for &t in pool.threads {
|
||||
data := cast(^Pool_Thread_Data)t.data
|
||||
free(data, pool.allocator)
|
||||
destroy(t)
|
||||
}
|
||||
|
||||
@@ -103,7 +118,7 @@ pool_join :: proc(pool: ^Pool) {
|
||||
|
||||
yield()
|
||||
|
||||
started_count: int
|
||||
started_count: int
|
||||
for started_count < len(pool.threads) {
|
||||
started_count = 0
|
||||
for t in pool.threads {
|
||||
@@ -138,6 +153,91 @@ pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Pro
|
||||
sync.post(&pool.sem_available, 1)
|
||||
}
|
||||
|
||||
// Forcibly stop a running task by its user index.
|
||||
//
|
||||
// This will terminate the underlying thread. Ideally, you should use some
|
||||
// means of communication to stop a task, as thread termination may leave
|
||||
// resources unclaimed.
|
||||
//
|
||||
// The thread will be restarted to accept new tasks.
|
||||
//
|
||||
// Returns true if the task was found and terminated.
|
||||
pool_stop_task :: proc(pool: ^Pool, user_index: int, exit_code: int = 1) -> bool {
|
||||
sync.guard(&pool.mutex)
|
||||
|
||||
for t, i in pool.threads {
|
||||
data := cast(^Pool_Thread_Data)t.data
|
||||
if data.task.user_index == user_index && data.task.procedure != nil {
|
||||
terminate(t, exit_code)
|
||||
|
||||
append(&pool.tasks_done, data.task)
|
||||
intrinsics.atomic_add(&pool.num_done, 1)
|
||||
intrinsics.atomic_sub(&pool.num_outstanding, 1)
|
||||
intrinsics.atomic_sub(&pool.num_in_processing, 1)
|
||||
|
||||
destroy(t)
|
||||
|
||||
replacement := create(pool_thread_runner)
|
||||
replacement.user_index = t.user_index
|
||||
replacement.data = data
|
||||
pool.threads[i] = replacement
|
||||
|
||||
start(replacement)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Forcibly stop all running tasks.
|
||||
//
|
||||
// The same notes from `pool_stop_task` apply here.
|
||||
pool_stop_all_tasks :: proc(pool: ^Pool, exit_code: int = 1) {
|
||||
sync.guard(&pool.mutex)
|
||||
|
||||
for t, i in pool.threads {
|
||||
data := cast(^Pool_Thread_Data)t.data
|
||||
if data.task.procedure != nil {
|
||||
terminate(t, exit_code)
|
||||
|
||||
append(&pool.tasks_done, data.task)
|
||||
intrinsics.atomic_add(&pool.num_done, 1)
|
||||
intrinsics.atomic_sub(&pool.num_outstanding, 1)
|
||||
intrinsics.atomic_sub(&pool.num_in_processing, 1)
|
||||
|
||||
destroy(t)
|
||||
|
||||
replacement := create(pool_thread_runner)
|
||||
replacement.user_index = t.user_index
|
||||
replacement.data = data
|
||||
pool.threads[i] = replacement
|
||||
|
||||
start(replacement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Force the pool to stop all of its threads and put it into a state where
|
||||
// it will no longer run any more tasks.
|
||||
//
|
||||
// The pool must still be destroyed after this.
|
||||
pool_shutdown :: proc(pool: ^Pool, exit_code: int = 1) {
|
||||
sync.guard(&pool.mutex)
|
||||
|
||||
for t in pool.threads {
|
||||
terminate(t, exit_code)
|
||||
|
||||
data := cast(^Pool_Thread_Data)t.data
|
||||
if data.task.procedure != nil {
|
||||
append(&pool.tasks_done, data.task)
|
||||
intrinsics.atomic_add(&pool.num_done, 1)
|
||||
intrinsics.atomic_sub(&pool.num_outstanding, 1)
|
||||
intrinsics.atomic_sub(&pool.num_in_processing, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Number of tasks waiting to be processed. Only informational, mostly for
|
||||
// debugging. Don't rely on this value being consistent with other num_*
|
||||
// values.
|
||||
|
||||
Reference in New Issue
Block a user