1 /*
2   This file is part of Deadbeef Player source code
3   http://deadbeef.sourceforge.net
4 
5   message pump implementation
6 
7   Copyright (C) 2009-2013 Alexey Yakovenko
8 
9   This software is provided 'as-is', without any express or implied
10   warranty.  In no event will the authors be held liable for any damages
11   arising from the use of this software.
12 
13   Permission is granted to anyone to use this software for any purpose,
14   including commercial applications, and to alter it and redistribute it
15   freely, subject to the following restrictions:
16 
17   1. The origin of this software must not be misrepresented; you must not
18      claim that you wrote the original software. If you use this software
19      in a product, an acknowledgment in the product documentation would be
20      appreciated but is not required.
21   2. Altered source versions must be plainly marked as such, and must not be
22      misrepresented as being the original software.
23   3. This notice may not be removed or altered from any source distribution.
24 
25   Alexey Yakovenko waker@users.sourceforge.net
26 */
27 #include <stdio.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <stdlib.h>
31 #include "messagepump.h"
32 #include "threading.h"
33 #include "playlist.h"
34 
35 typedef struct message_s {
36     uint32_t id;
37     uintptr_t ctx;
38     uint32_t p1;
39     uint32_t p2;
40     struct message_s *next;
41 } message_t;
42 
43 enum { MAX_MESSAGES = 100 };
44 static message_t pool[MAX_MESSAGES];
45 static message_t *mfree;
46 static message_t *mqueue;
47 static message_t *mqtail;
48 static uintptr_t mutex;
49 static uintptr_t cond;
50 
51 static void
52 messagepump_reset (void);
53 
54 int
messagepump_init(void)55 messagepump_init (void) {
56     messagepump_reset ();
57     mutex = mutex_create ();
58     cond = cond_create ();
59     return 0;
60 }
61 
62 void
messagepump_free()63 messagepump_free () {
64     mutex_lock (mutex);
65     messagepump_reset ();
66     mutex_unlock (mutex);
67     mutex_free (mutex);
68     cond_free (cond);
69 }
70 
71 static void
messagepump_reset(void)72 messagepump_reset (void) {
73     mqueue = NULL;
74     mfree = NULL;
75     mqtail = NULL;
76     memset (pool, 0, sizeof (pool));
77     for (int i = 0; i < MAX_MESSAGES; i++) {
78         pool[i].next = mfree;
79         mfree = &pool[i];
80     }
81 }
82 
83 int
messagepump_push(uint32_t id,uintptr_t ctx,uint32_t p1,uint32_t p2)84 messagepump_push (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) {
85     if (!mfree) {
86         //fprintf (stderr, "WARNING: message queue is full! message ignored (%d %p %d %d)\n", id, (void*)ctx, p1, p2);
87         if (id >= DB_EV_FIRST && ctx) {
88             messagepump_event_free ((ddb_event_t *)ctx);
89         }
90         return -1;
91     }
92     mutex_lock (mutex);
93     message_t *msg = mfree;
94     mfree = mfree->next;
95     if (mqtail) {
96         mqtail->next = msg;
97     }
98     mqtail = msg;
99     if (!mqueue) {
100         mqueue = msg;
101     }
102 
103     msg->next = NULL;
104     msg->id = id;
105     msg->ctx = ctx;
106     msg->p1 = p1;
107     msg->p2 = p2;
108     mutex_unlock (mutex);
109     cond_signal (cond);
110     return 0;
111 }
112 
113 void
messagepump_wait(void)114 messagepump_wait (void) {
115     cond_wait (cond, mutex);
116     mutex_unlock (mutex);
117 }
118 
119 int
messagepump_pop(uint32_t * id,uintptr_t * ctx,uint32_t * p1,uint32_t * p2)120 messagepump_pop (uint32_t *id, uintptr_t *ctx, uint32_t *p1, uint32_t *p2) {
121     mutex_lock (mutex);
122     if (!mqueue) {
123         mutex_unlock (mutex);
124         return -1;
125     }
126     *id = mqueue->id;
127     *ctx = mqueue->ctx;
128     *p1 = mqueue->p1;
129     *p2 = mqueue->p2;
130     message_t *next = mqueue->next;
131     mqueue->next = mfree;
132     mfree = mqueue;
133     mqueue = next;
134     if (!mqueue) {
135         mqtail = NULL;
136     }
137     mutex_unlock (mutex);
138     return 0;
139 }
140 
141 int
messagepump_hasmessages(void)142 messagepump_hasmessages (void) {
143     return mqueue ? 1 : 0;
144 }
145 
146 ddb_event_t *
messagepump_event_alloc(uint32_t id)147 messagepump_event_alloc (uint32_t id) {
148     int sz = 0;
149     ddb_event_t *ev;
150     switch (id) {
151     case DB_EV_SONGCHANGED:
152         sz = sizeof (ddb_event_trackchange_t);
153         break;
154     case DB_EV_SONGSTARTED:
155     case DB_EV_SONGFINISHED:
156     case DB_EV_TRACKINFOCHANGED:
157         sz = sizeof (ddb_event_track_t);
158         break;
159     case DB_EV_SEEKED:
160         sz = sizeof (ddb_event_playpos_t);
161         break;
162     }
163     assert (("Invalid event %d to use with messagepump_event_alloc, use sendmessage instead\n", id));
164     ev = malloc (sz);
165     memset (ev, 0, sz);
166     ev->event = id;
167     ev->size = sz;
168     return ev;
169 }
170 
171 void
messagepump_event_free(ddb_event_t * ev)172 messagepump_event_free (ddb_event_t *ev) {
173     switch (ev->event) {
174     case DB_EV_SONGCHANGED:
175         {
176             ddb_event_trackchange_t *tc = (ddb_event_trackchange_t*)ev;
177             if (tc->from) {
178                 pl_item_unref ((playItem_t *)tc->from);
179             }
180             if (tc->to) {
181                 pl_item_unref ((playItem_t *)tc->to);
182             }
183         }
184         break;
185     case DB_EV_SONGSTARTED:
186     case DB_EV_SONGFINISHED:
187     case DB_EV_TRACKINFOCHANGED:
188         {
189             ddb_event_track_t *tc = (ddb_event_track_t*)ev;
190             if (tc->track) {
191                 pl_item_unref ((playItem_t *)tc->track);
192             }
193         }
194         break;
195     case DB_EV_SEEKED:
196         {
197             ddb_event_playpos_t *tc = (ddb_event_playpos_t*)ev;
198             if (tc->track) {
199                 pl_item_unref ((playItem_t *)tc->track);
200             }
201         }
202         break;
203     }
204     free (ev);
205 }
206 
207 int
messagepump_push_event(ddb_event_t * ev,uint32_t p1,uint32_t p2)208 messagepump_push_event (ddb_event_t *ev, uint32_t p1, uint32_t p2) {
209     return messagepump_push (ev->event, (uintptr_t)ev, p1, p2);
210 }
211 
212