Core commit. Compile and run on the OpenPandora
[mupen64plus-pandora.git] / source / mupen64plus-core / src / main / workqueue.c
CommitLineData
451ab91e 1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus - util.h *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
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#include "api/callbacks.h"
24
25#include <SDL.h>
26#include <SDL_thread.h>
27
28#define WORKQUEUE_THREADS 1
29
30struct workqueue_mgmt_globals {
31 struct list_head work_queue;
32 struct list_head thread_queue;
33 struct list_head thread_list;
34 SDL_mutex *lock;
35};
36
37struct workqueue_thread {
38 SDL_Thread *thread;
39 SDL_cond *work_avail;
40 struct list_head list;
41 struct list_head list_mgmt;
42};
43
44static struct workqueue_mgmt_globals workqueue_mgmt;
45
46static void workqueue_dismiss(struct work_struct *work)
47{
48}
49
50static struct work_struct *workqueue_get_work(struct workqueue_thread *thread)
51{
52 int found = 0;
53 struct work_struct *work;
54
55 while (1) {
56 SDL_LockMutex(workqueue_mgmt.lock);
57 list_del_init(&thread->list);
58 if (!list_empty(&workqueue_mgmt.work_queue)) {
59 found = 1;
60 work = list_first_entry(&workqueue_mgmt.work_queue, struct work_struct, list);
61 list_del_init(&work->list);
62 } else {
63 list_add(&thread->list, &workqueue_mgmt.thread_queue);
64 SDL_CondWait(thread->work_avail, workqueue_mgmt.lock);
65 }
66 SDL_UnlockMutex(workqueue_mgmt.lock);
67
68 if (found)
69 break;
70 }
71
72 return work;
73}
74
75static int workqueue_thread_handler(void *data)
76{
77 struct workqueue_thread *thread = data;
78 struct work_struct *work;
79
80 while (1) {
81 work = workqueue_get_work(thread);
82 if (work->func == workqueue_dismiss) {
83 free(work);
84 break;
85 }
86
87 work->func(work);
88 }
89
90 return 0;
91}
92
93int workqueue_init(void)
94{
95 size_t i;
96 struct workqueue_thread *thread;
97
98 memset(&workqueue_mgmt, 0, sizeof(workqueue_mgmt));
99 INIT_LIST_HEAD(&workqueue_mgmt.work_queue);
100 INIT_LIST_HEAD(&workqueue_mgmt.thread_queue);
101 INIT_LIST_HEAD(&workqueue_mgmt.thread_list);
102
103 workqueue_mgmt.lock = SDL_CreateMutex();
104 if (!workqueue_mgmt.lock) {
105 DebugMessage(M64MSG_ERROR, "Could not create workqueue management");
106 return -1;
107 }
108
109 SDL_LockMutex(workqueue_mgmt.lock);
110 for (i = 0; i < WORKQUEUE_THREADS; i++) {
111 thread = malloc(sizeof(*thread));
112 if (!thread) {
113 DebugMessage(M64MSG_ERROR, "Could not create workqueue thread management data");
114 SDL_UnlockMutex(workqueue_mgmt.lock);
115 return -1;
116 }
117
118 memset(thread, 0, sizeof(*thread));
119 list_add(&thread->list_mgmt, &workqueue_mgmt.thread_list);
120 INIT_LIST_HEAD(&thread->list);
121 thread->work_avail = SDL_CreateCond();
122 if (!thread->work_avail) {
123 DebugMessage(M64MSG_ERROR, "Could not create workqueue thread work_avail condition");
124 SDL_UnlockMutex(workqueue_mgmt.lock);
125 return -1;
126 }
127
128#if SDL_VERSION_ATLEAST(2,0,0)
129 thread->thread = SDL_CreateThread(workqueue_thread_handler, "m64pwq", thread);
130#else
131 thread->thread = SDL_CreateThread(workqueue_thread_handler, thread);
132#endif
133 if (!thread->thread) {
134 DebugMessage(M64MSG_ERROR, "Could not create workqueue thread handler");
135 SDL_UnlockMutex(workqueue_mgmt.lock);
136 return -1;
137 }
138 }
139 SDL_UnlockMutex(workqueue_mgmt.lock);
140
141 return 0;
142}
143
144void workqueue_shutdown(void)
145{
146 size_t i;
147 int status;
148 struct work_struct *work;
149 struct workqueue_thread *thread, *safe;
150
151 for (i = 0; i < WORKQUEUE_THREADS; i++) {
152 work = malloc(sizeof(*work));
153 init_work(work, workqueue_dismiss);
154 queue_work(work);
155 }
156
157 list_for_each_entry_safe(thread, safe, &workqueue_mgmt.thread_list, struct workqueue_thread, list_mgmt) {
158 list_del(&thread->list_mgmt);
159 SDL_WaitThread(thread->thread, &status);
160 SDL_DestroyCond(thread->work_avail);
161 free(thread);
162 }
163
164 if (!list_empty(&workqueue_mgmt.work_queue))
165 DebugMessage(M64MSG_WARNING, "Stopped workqueue with work still pending");
166
167 SDL_DestroyMutex(workqueue_mgmt.lock);
168}
169
170int queue_work(struct work_struct *work)
171{
172 struct workqueue_thread *thread;
173
174 SDL_LockMutex(workqueue_mgmt.lock);
175 list_add_tail(&work->list, &workqueue_mgmt.work_queue);
176 if (!list_empty(&workqueue_mgmt.thread_queue)) {
177 thread = list_first_entry(&workqueue_mgmt.thread_queue, struct workqueue_thread, list);
178 list_del_init(&thread->list);
179
180 SDL_CondSignal(thread->work_avail);
181 }
182 SDL_UnlockMutex(workqueue_mgmt.lock);
183
184 return 0;
185}