1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus - util.h *
3 * Mupen64Plus homepage: https://mupen64plus.org/ *
4 * Copyright (C) 2012 Mupen64plus development team *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
20 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21
22#include "workqueue.h"
23
24#include <SDL.h>
25#include <SDL_thread.h>
26#include <stddef.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include "api/callbacks.h"
31#include "api/m64p_types.h"
32#include "main/list.h"
33
34#define WORKQUEUE_THREADS 1
35
36struct workqueue_mgmt_globals {
37 struct list_head work_queue;
38 struct list_head thread_queue;
39 struct list_head thread_list;
40 SDL_mutex *lock;
41};
42
43struct workqueue_thread {
44 SDL_Thread *thread;
45 SDL_cond *work_avail;
46 struct list_head list;
47 struct list_head list_mgmt;
48};
49
50static struct workqueue_mgmt_globals workqueue_mgmt;
51
52static void workqueue_dismiss(struct work_struct *work)
53{
54}
55
56static struct work_struct *workqueue_get_work(struct workqueue_thread *thread)
57{
58 int found = 0;
59 struct work_struct *work;
60
61 for (;;) {
62 SDL_LockMutex(workqueue_mgmt.lock);
63 list_del_init(&thread->list);
64 if (!list_empty(&workqueue_mgmt.work_queue)) {
65 found = 1;
66 work = list_first_entry(&workqueue_mgmt.work_queue, struct work_struct, list);
67 list_del_init(&work->list);
68 } else {
69 list_add(&thread->list, &workqueue_mgmt.thread_queue);
70 SDL_CondWait(thread->work_avail, workqueue_mgmt.lock);
71 }
72 SDL_UnlockMutex(workqueue_mgmt.lock);
73
74 if (found)
75 break;
76 }
77
78 return work;
79}
80
81static int workqueue_thread_handler(void *data)
82{
83 struct workqueue_thread *thread = data;
84 struct work_struct *work;
85
86 for (;;) {
87 work = workqueue_get_work(thread);
88 if (work->func == workqueue_dismiss) {
89 free(work);
90 break;
91 }
92
93 work->func(work);
94 }
95
96 return 0;
97}
98
99int workqueue_init(void)
100{
101 size_t i;
102 struct workqueue_thread *thread;
103
104 memset(&workqueue_mgmt, 0, sizeof(workqueue_mgmt));
105 INIT_LIST_HEAD(&workqueue_mgmt.work_queue);
106 INIT_LIST_HEAD(&workqueue_mgmt.thread_queue);
107 INIT_LIST_HEAD(&workqueue_mgmt.thread_list);
108
109 workqueue_mgmt.lock = SDL_CreateMutex();
110 if (!workqueue_mgmt.lock) {
111 DebugMessage(M64MSG_ERROR, "Could not create workqueue management");
112 return -1;
113 }
114
115 SDL_LockMutex(workqueue_mgmt.lock);
116 for (i = 0; i < WORKQUEUE_THREADS; i++) {
117 thread = malloc(sizeof(*thread));
118 if (!thread) {
119 DebugMessage(M64MSG_ERROR, "Could not create workqueue thread management data");
120 SDL_UnlockMutex(workqueue_mgmt.lock);
121 return -1;
122 }
123
124 memset(thread, 0, sizeof(*thread));
125 list_add(&thread->list_mgmt, &workqueue_mgmt.thread_list);
126 INIT_LIST_HEAD(&thread->list);
127 thread->work_avail = SDL_CreateCond();
128 if (!thread->work_avail) {
129 DebugMessage(M64MSG_ERROR, "Could not create workqueue thread work_avail condition");
130 SDL_UnlockMutex(workqueue_mgmt.lock);
131 return -1;
132 }
133
134#if SDL_VERSION_ATLEAST(2,0,0)
135 thread->thread = SDL_CreateThread(workqueue_thread_handler, "m64pwq", thread);
136#else
137 thread->thread = SDL_CreateThread(workqueue_thread_handler, thread);
138#endif
139 if (!thread->thread) {
140 DebugMessage(M64MSG_ERROR, "Could not create workqueue thread handler");
141 SDL_UnlockMutex(workqueue_mgmt.lock);
142 return -1;
143 }
144 }
145 SDL_UnlockMutex(workqueue_mgmt.lock);
146
147 return 0;
148}
149
150void workqueue_shutdown(void)
151{
152 size_t i;
153 int status;
154 struct work_struct *work;
155 struct workqueue_thread *thread, *safe;
156
157 for (i = 0; i < WORKQUEUE_THREADS; i++) {
158 work = malloc(sizeof(*work));
159 init_work(work, workqueue_dismiss);
160 queue_work(work);
161 }
162
163 list_for_each_entry_safe_t(thread, safe, &workqueue_mgmt.thread_list, struct workqueue_thread, list_mgmt) {
164 list_del(&thread->list_mgmt);
165 SDL_WaitThread(thread->thread, &status);
166 SDL_DestroyCond(thread->work_avail);
167 free(thread);
168 }
169
170 if (!list_empty(&workqueue_mgmt.work_queue))
171 DebugMessage(M64MSG_WARNING, "Stopped workqueue with work still pending");
172
173 SDL_DestroyMutex(workqueue_mgmt.lock);
174}
175
176int queue_work(struct work_struct *work)
177{
178 struct workqueue_thread *thread;
179
180 SDL_LockMutex(workqueue_mgmt.lock);
181 list_add_tail(&work->list, &workqueue_mgmt.work_queue);
182 if (!list_empty(&workqueue_mgmt.thread_queue)) {
183 thread = list_first_entry(&workqueue_mgmt.thread_queue, struct workqueue_thread, list);
184 list_del_init(&thread->list);
185
186 SDL_CondSignal(thread->work_avail);
187 }
188 SDL_UnlockMutex(workqueue_mgmt.lock);
189
190 return 0;
191}
192