You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
6.2 KiB
147 lines
6.2 KiB
#include "worker.h" |
|
#include "../../lib/u_async.h" |
|
#include "../../lib/mem.h" |
|
#include "../../lib/debug_config.h" |
|
#include <pthread.h> |
|
|
|
struct mf_work { |
|
struct mf_work* next; |
|
struct mf_work* queued_next; |
|
struct mf_worker* owner; |
|
struct posted_task* completion; |
|
mf_work_fn work; |
|
mf_done_fn done; |
|
void* data; |
|
void* arg; |
|
int completed; |
|
}; |
|
struct mf_worker { |
|
struct UASYNC* ua; |
|
pthread_t thread; |
|
pthread_mutex_t mutex; |
|
pthread_cond_t condition; |
|
struct mf_work* jobs; |
|
struct mf_work* queue; |
|
struct mf_work* tail; |
|
unsigned count; |
|
long creator_pid; |
|
int stopping; |
|
}; |
|
|
|
static void work_finish(struct mf_work* j) { |
|
struct mf_worker* w = j->owner; |
|
struct mf_work** link = &w->jobs; |
|
while (*link && *link != j) link = &(*link)->next; |
|
if (*link) *link = j->next; |
|
w->count--; |
|
DEBUG_TRACE(DEBUG_CATEGORY_MEDIA, "member_files: worker callback owner=%p task=%p remaining=%u pid=%ld", |
|
(void*)w, (void*)j, w->count, (long)getpid()); |
|
mf_done_fn done = j->done; void* arg = j->arg; |
|
u_free(j); done(arg, 0); |
|
} |
|
|
|
static void work_completed(void* arg) { |
|
struct mf_work* j = arg; |
|
pthread_mutex_lock(&j->owner->mutex); |
|
j->completion = NULL; |
|
pthread_mutex_unlock(&j->owner->mutex); |
|
work_finish(j); |
|
} |
|
|
|
static void* disk_thread(void* arg) { |
|
struct mf_worker* w = arg; |
|
pthread_mutex_lock(&w->mutex); |
|
for (;;) { |
|
while (!w->queue && !w->stopping) pthread_cond_wait(&w->condition, &w->mutex); |
|
if (!w->queue && w->stopping) break; |
|
struct mf_work* j = w->queue; w->queue = j->queued_next; |
|
if (!w->queue) w->tail = NULL; |
|
pthread_mutex_unlock(&w->mutex); |
|
DEBUG_TRACE(DEBUG_CATEGORY_MEDIA, "member_files: worker execute owner=%p task=%p pid=%ld", (void*)w, (void*)j, (long)getpid()); |
|
j->work(j->data); |
|
pthread_mutex_lock(&w->mutex); |
|
j->completed = 1; |
|
DEBUG_TRACE(DEBUG_CATEGORY_MEDIA, "member_files: worker posting owner=%p task=%p pid=%ld", (void*)w, (void*)j, (long)getpid()); |
|
uasync_post_reserved(w->ua, j->completion); |
|
pthread_cond_broadcast(&w->condition); |
|
} |
|
pthread_mutex_unlock(&w->mutex); |
|
return NULL; |
|
} |
|
|
|
struct mf_worker* mf_worker_create(struct UASYNC* ua) { |
|
struct mf_worker* w = u_calloc(1, sizeof(*w)); |
|
if (!w) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files: disk worker allocation failed"); return NULL; } |
|
w->ua = ua; w->creator_pid = (long)getpid(); |
|
int rc = pthread_mutex_init(&w->mutex, NULL); |
|
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files: worker mutex failed rc=%d", rc); u_free(w); return NULL; } |
|
rc = pthread_cond_init(&w->condition, NULL); |
|
if (rc) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files: worker condition failed rc=%d", rc); |
|
pthread_mutex_destroy(&w->mutex); u_free(w); return NULL; |
|
} |
|
rc = pthread_create(&w->thread, NULL, disk_thread, w); |
|
if (rc) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files: disk thread failed rc=%d", rc); |
|
pthread_cond_destroy(&w->condition); pthread_mutex_destroy(&w->mutex); u_free(w); return NULL; |
|
} |
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "member_files: worker created owner=%p pid=%ld ua=%p limit=64", |
|
(void*)w, w->creator_pid, (void*)ua); |
|
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files: disk thread started queue_limit=64"); return w; |
|
} |
|
|
|
struct mf_work* mf_worker_reserve(void) { |
|
struct mf_work* j = u_calloc(1, sizeof(*j)); |
|
if (j) j->completion = u_calloc(1, sizeof(*j->completion)); |
|
if (!j || !j->completion) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files: work/completion reservation failed"); u_free(j); return NULL; } |
|
j->completion->callback = work_completed; j->completion->arg = j; |
|
return j; |
|
} |
|
|
|
void mf_worker_unreserve(struct mf_work* j) { if (j) { u_free(j->completion); u_free(j); } } |
|
|
|
void mf_worker_submit_reserved(struct mf_worker* w, struct mf_work* j, |
|
mf_work_fn work, void* data, mf_done_fn done, void* arg) { |
|
if (w->creator_pid != (long)getpid()) DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, |
|
"member_files: worker inherited after fork creator_pid=%ld current_pid=%ld; disk thread is missing", |
|
w->creator_pid, (long)getpid()); |
|
j->owner = w; j->work = work; j->data = data; j->done = done; j->arg = arg; |
|
j->next = w->jobs; w->jobs = j; w->count++; |
|
DEBUG_TRACE(DEBUG_CATEGORY_MEDIA, "member_files: worker queued owner=%p task=%p current_pid=%ld creator_pid=%ld active=%u", |
|
(void*)w, (void*)j, (long)getpid(), w->creator_pid, w->count); |
|
pthread_mutex_lock(&w->mutex); |
|
if (w->tail) w->tail->queued_next = j; |
|
else w->queue = j; |
|
w->tail = j; pthread_cond_signal(&w->condition); |
|
pthread_mutex_unlock(&w->mutex); |
|
} |
|
|
|
void mf_worker_submit(struct mf_worker* w, mf_work_fn work, void* data, mf_done_fn done, void* arg) { |
|
struct mf_work* j = w->count < 64 ? mf_worker_reserve() : NULL; |
|
if (!j) { DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: disk queue rejected active=%u", w->count); done(arg, -1); return; } |
|
mf_worker_submit_reserved(w, j, work, data, done, arg); |
|
} |
|
|
|
void mf_worker_drain(struct mf_worker* w) { |
|
if (!w) return; |
|
/* Не запускаем loop рекурсивно: завершаем только собственные callbacks. */ |
|
while (w->jobs) { |
|
struct mf_work* j = w->jobs; |
|
pthread_mutex_lock(&w->mutex); |
|
while (!j->completed) pthread_cond_wait(&w->condition, &w->mutex); |
|
pthread_mutex_unlock(&w->mutex); |
|
uasync_cancel_post(w->ua, j->completion); j->completion = NULL; |
|
work_finish(j); |
|
} |
|
} |
|
|
|
void mf_worker_destroy(struct mf_worker* w) { |
|
if (!w) return; |
|
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "member_files: worker stop owner=%p current_pid=%ld creator_pid=%ld active=%u", |
|
(void*)w, (long)getpid(), w->creator_pid, w->count); |
|
mf_worker_drain(w); |
|
pthread_mutex_lock(&w->mutex); w->stopping = 1; pthread_cond_signal(&w->condition); pthread_mutex_unlock(&w->mutex); |
|
pthread_join(w->thread, NULL); |
|
pthread_cond_destroy(&w->condition); pthread_mutex_destroy(&w->mutex); |
|
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files: disk thread stopped"); u_free(w); |
|
}
|
|
|