Core commit. Compile and run on the OpenPandora
[mupen64plus-pandora.git] / source / mupen64plus-core / src / main / workqueue.c
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
30 struct 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
37 struct workqueue_thread {
38     SDL_Thread *thread;
39     SDL_cond *work_avail;
40     struct list_head list;
41     struct list_head list_mgmt;
42 };
43
44 static struct workqueue_mgmt_globals workqueue_mgmt;
45
46 static void workqueue_dismiss(struct work_struct *work)
47 {
48 }
49
50 static 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
75 static 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
93 int 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
144 void 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
170 int 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 }