]> code.delx.au - pulseaudio/blob - src/pulsecore/pdispatch.c
use a free list for allocating reply_info structs
[pulseaudio] / src / pulsecore / pdispatch.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2004-2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
8
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as
11 published by the Free Software Foundation; either version 2.1 of the
12 License, or (at your option) any later version.
13
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 USA.
23 ***/
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31
32 #include <pulse/timeval.h>
33 #include <pulse/xmalloc.h>
34
35 #include <pulsecore/native-common.h>
36 #include <pulsecore/llist.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/core-util.h>
39 #include <pulsecore/macro.h>
40 #include <pulsecore/refcnt.h>
41 #include <pulsecore/flist.h>
42
43 #include "pdispatch.h"
44
45 /*#define DEBUG_OPCODES */
46
47 #ifdef DEBUG_OPCODES
48
49 static const char *command_names[PA_COMMAND_MAX] = {
50 [PA_COMMAND_ERROR] = "ERROR",
51 [PA_COMMAND_TIMEOUT] = "TIMEOUT",
52 [PA_COMMAND_REPLY] = "REPLY",
53 [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
54 [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
55 [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
56 [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
57 [PA_COMMAND_AUTH] = "AUTH",
58 [PA_COMMAND_REQUEST] = "REQUEST",
59 [PA_COMMAND_EXIT] = "EXIT",
60 [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME",
61 [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
62 [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
63 [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
64 [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
65 [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
66 [PA_COMMAND_STAT] = "STAT",
67 [PA_COMMAND_GET_PLAYBACK_LATENCY] = "PLAYBACK_LATENCY",
68 [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
69 [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
70 [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM",
71 [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE",
72 [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE",
73 [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO",
74 [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO",
75 [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST",
76 [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO",
77 [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST",
78 [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO",
79 [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST",
80 [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO",
81 [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST",
82 [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO",
83 [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST",
84 [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO",
85 [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST",
86 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO",
87 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST",
88 [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE",
89 [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
90 [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
91 [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
92 [PA_COMMAND_SET_SOURCE_VOLUME] = "SET_SOURCE_VOLME",
93 [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
94 [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
95 [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
96 [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO",
97 [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST",
98 };
99
100 #endif
101
102 PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
103
104 struct reply_info {
105 pa_pdispatch *pdispatch;
106 PA_LLIST_FIELDS(struct reply_info);
107 pa_pdispatch_cb_t callback;
108 void *userdata;
109 pa_free_cb_t free_cb;
110 uint32_t tag;
111 pa_time_event *time_event;
112 };
113
114 struct pa_pdispatch {
115 PA_REFCNT_DECLARE;
116 pa_mainloop_api *mainloop;
117 const pa_pdispatch_cb_t *callback_table;
118 unsigned n_commands;
119 PA_LLIST_HEAD(struct reply_info, replies);
120 pa_pdispatch_drain_callback drain_callback;
121 void *drain_userdata;
122 const pa_creds *creds;
123 };
124
125 static void reply_info_free(struct reply_info *r) {
126 pa_assert(r);
127 pa_assert(r->pdispatch);
128 pa_assert(r->pdispatch->mainloop);
129
130 if (r->time_event)
131 r->pdispatch->mainloop->time_free(r->time_event);
132
133 PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
134
135 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
136 pa_xfree(r);
137 }
138
139 pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
140 pa_pdispatch *pd;
141 pa_assert(mainloop);
142
143 pa_assert((entries && table) || (!entries && !table));
144
145 pd = pa_xnew(pa_pdispatch, 1);
146 PA_REFCNT_INIT(pd);
147 pd->mainloop = mainloop;
148 pd->callback_table = table;
149 pd->n_commands = entries;
150 PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
151 pd->drain_callback = NULL;
152 pd->drain_userdata = NULL;
153 pd->creds = NULL;
154
155 return pd;
156 }
157
158 static void pdispatch_free(pa_pdispatch *pd) {
159 pa_assert(pd);
160
161 while (pd->replies) {
162 if (pd->replies->free_cb)
163 pd->replies->free_cb(pd->replies->userdata);
164
165 reply_info_free(pd->replies);
166 }
167
168 pa_xfree(pd);
169 }
170
171 static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
172 pa_pdispatch_cb_t callback;
173 void *userdata;
174 uint32_t tag;
175 pa_assert(r);
176
177 pa_pdispatch_ref(pd);
178
179 callback = r->callback;
180 userdata = r->userdata;
181 tag = r->tag;
182
183 reply_info_free(r);
184
185 callback(pd, command, tag, ts, userdata);
186
187 if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
188 pd->drain_callback(pd, pd->drain_userdata);
189
190 pa_pdispatch_unref(pd);
191 }
192
193 int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, void *userdata) {
194 uint32_t tag, command;
195 pa_tagstruct *ts = NULL;
196 int ret = -1;
197
198 pa_assert(pd);
199 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
200 pa_assert(packet);
201 pa_assert(PA_REFCNT_VALUE(packet) >= 1);
202 pa_assert(packet->data);
203
204 pa_pdispatch_ref(pd);
205
206 if (packet->length <= 8)
207 goto finish;
208
209 ts = pa_tagstruct_new(packet->data, packet->length);
210
211 if (pa_tagstruct_getu32(ts, &command) < 0 ||
212 pa_tagstruct_getu32(ts, &tag) < 0)
213 goto finish;
214
215 #ifdef DEBUG_OPCODES
216 {
217 char t[256];
218 char const *p;
219 if (!(p = command_names[command]))
220 pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
221
222 pa_log("Recieved opcode <%s>", p);
223 }
224 #endif
225
226 pd->creds = creds;
227
228 if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
229 struct reply_info *r;
230
231 for (r = pd->replies; r; r = r->next)
232 if (r->tag == tag)
233 break;
234
235 if (r)
236 run_action(pd, r, command, ts);
237
238 } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
239 const pa_pdispatch_cb_t *c = pd->callback_table+command;
240
241 (*c)(pd, command, tag, ts, userdata);
242 } else {
243 pa_log("Recieved unsupported command %u", command);
244 goto finish;
245 }
246
247 ret = 0;
248
249 finish:
250 pd->creds = NULL;
251
252 if (ts)
253 pa_tagstruct_free(ts);
254
255 pa_pdispatch_unref(pd);
256
257 return ret;
258 }
259
260 static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
261 struct reply_info*r = userdata;
262
263 pa_assert(r);
264 pa_assert(r->time_event == e);
265 pa_assert(r->pdispatch);
266 pa_assert(r->pdispatch->mainloop == m);
267 pa_assert(r->callback);
268
269 run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
270 }
271
272 void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {
273 struct reply_info *r;
274 struct timeval tv;
275
276 pa_assert(pd);
277 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
278 pa_assert(cb);
279
280 if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
281 r = pa_xnew(struct reply_info, 1);
282
283 r->pdispatch = pd;
284 r->callback = cb;
285 r->userdata = userdata;
286 r->free_cb = free_cb;
287 r->tag = tag;
288
289 pa_gettimeofday(&tv);
290 tv.tv_sec += timeout;
291
292 pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r));
293
294 PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
295 }
296
297 int pa_pdispatch_is_pending(pa_pdispatch *pd) {
298 pa_assert(pd);
299 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
300
301 return !!pd->replies;
302 }
303
304 void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
305 pa_assert(pd);
306 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
307 pa_assert(!cb || pa_pdispatch_is_pending(pd));
308
309 pd->drain_callback = cb;
310 pd->drain_userdata = userdata;
311 }
312
313 void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
314 struct reply_info *r, *n;
315
316 pa_assert(pd);
317 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
318
319 for (r = pd->replies; r; r = n) {
320 n = r->next;
321
322 if (r->userdata == userdata)
323 reply_info_free(r);
324 }
325 }
326
327 void pa_pdispatch_unref(pa_pdispatch *pd) {
328 pa_assert(pd);
329 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
330
331 if (PA_REFCNT_DEC(pd) <= 0)
332 pdispatch_free(pd);
333 }
334
335 pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
336 pa_assert(pd);
337 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
338
339 PA_REFCNT_INC(pd);
340 return pd;
341 }
342
343 const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
344 pa_assert(pd);
345 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
346
347 return pd->creds;
348 }