1 /*------------------------------------------------------------------------
2 * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
3 *
4 * This file is part of the ZBar Bar Code Reader.
5 *
6 * The ZBar Bar Code Reader is free software; you can redistribute it
7 * and/or modify it under the terms of the GNU Lesser Public License as
8 * published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * The ZBar Bar Code Reader is distributed in the hope that it will be
12 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser Public License
17 * along with the ZBar Bar Code Reader; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
20 *
21 * http://sourceforge.net/projects/zbar
22 *------------------------------------------------------------------------*/
23
24 #include "processor.h"
25 #include <assert.h>
26
27 /* the processor api lock is a recursive mutex with added capabilities
28 * to completely drop all lock levels before blocking and atomically
29 * unblock a waiting set. the lock is implemented using a variation
30 * of the "specific notification pattern" [cargill], which makes it
31 * easy to provide these features across platforms with consistent,
32 * predictable semantics. probably overkill, but additional overhead
33 * associated with this mechanism should fall in the noise, as locks
34 * are only exchanged O(frame/image)
35 *
36 * [cargill]
37 * http://www.profcon.com/profcon/cargill/jgf/9809/SpecificNotification.html
38 */
39
proc_waiter_queue(zbar_processor_t * proc)40 static inline proc_waiter_t *proc_waiter_queue (zbar_processor_t *proc)
41 {
42 proc_waiter_t *waiter = proc->free_waiter;
43 if(waiter) {
44 proc->free_waiter = waiter->next;
45 waiter->events = 0;
46 }
47 else {
48 waiter = calloc(1, sizeof(proc_waiter_t));
49 _zbar_event_init(&waiter->notify);
50 }
51
52 waiter->next = NULL;
53 waiter->requester = _zbar_thread_self();
54
55 if(proc->wait_head)
56 proc->wait_tail->next = waiter;
57 else
58 proc->wait_head = waiter;
59 proc->wait_tail = waiter;
60 return(waiter);
61 }
62
proc_waiter_dequeue(zbar_processor_t * proc)63 static inline proc_waiter_t *proc_waiter_dequeue (zbar_processor_t *proc)
64 {
65 proc_waiter_t *prev = proc->wait_next, *waiter;
66 if(prev)
67 waiter = prev->next;
68 else
69 waiter = proc->wait_head;
70 while(waiter && (waiter->events & EVENTS_PENDING)) {
71 prev = waiter;
72 proc->wait_next = waiter;
73 waiter = waiter->next;
74 }
75
76 if(waiter) {
77 if(prev)
78 prev->next = waiter->next;
79 else
80 proc->wait_head = waiter->next;
81 if(!waiter->next)
82 proc->wait_tail = prev;
83 waiter->next = NULL;
84
85 proc->lock_level = 1;
86 proc->lock_owner = waiter->requester;
87 }
88 return(waiter);
89 }
90
proc_waiter_release(zbar_processor_t * proc,proc_waiter_t * waiter)91 static inline void proc_waiter_release (zbar_processor_t *proc,
92 proc_waiter_t *waiter)
93 {
94 if(waiter) {
95 waiter->next = proc->free_waiter;
96 proc->free_waiter = waiter;
97 }
98 }
99
_zbar_processor_lock(zbar_processor_t * proc)100 int _zbar_processor_lock (zbar_processor_t *proc)
101 {
102 if(!proc->lock_level) {
103 proc->lock_owner = _zbar_thread_self();
104 proc->lock_level = 1;
105 return(0);
106 }
107
108 if(_zbar_thread_is_self(proc->lock_owner)) {
109 proc->lock_level++;
110 return(0);
111 }
112
113 proc_waiter_t *waiter = proc_waiter_queue(proc);
114 _zbar_event_wait(&waiter->notify, &proc->mutex, NULL);
115
116 assert(proc->lock_level == 1);
117 assert(_zbar_thread_is_self(proc->lock_owner));
118
119 proc_waiter_release(proc, waiter);
120 return(0);
121 }
122
_zbar_processor_unlock(zbar_processor_t * proc,int all)123 int _zbar_processor_unlock (zbar_processor_t *proc,
124 int all)
125 {
126 assert(proc->lock_level > 0);
127 assert(_zbar_thread_is_self(proc->lock_owner));
128
129 if(all)
130 proc->lock_level = 0;
131 else
132 proc->lock_level--;
133
134 if(!proc->lock_level) {
135 proc_waiter_t *waiter = proc_waiter_dequeue(proc);
136 if(waiter)
137 _zbar_event_trigger(&waiter->notify);
138 }
139 return(0);
140 }
141
_zbar_processor_notify(zbar_processor_t * proc,unsigned events)142 void _zbar_processor_notify (zbar_processor_t *proc,
143 unsigned events)
144 {
145 proc->wait_next = NULL;
146 proc_waiter_t *waiter;
147 for(waiter = proc->wait_head; waiter; waiter = waiter->next)
148 waiter->events = ((waiter->events & ~events) |
149 (events & EVENT_CANCELED));
150
151 if(!proc->lock_level) {
152 waiter = proc_waiter_dequeue(proc);
153 if(waiter)
154 _zbar_event_trigger(&waiter->notify);
155 }
156 }
157
proc_wait_unthreaded(zbar_processor_t * proc,proc_waiter_t * waiter,zbar_timer_t * timeout)158 static inline int proc_wait_unthreaded (zbar_processor_t *proc,
159 proc_waiter_t *waiter,
160 zbar_timer_t *timeout)
161 {
162 int blocking = proc->streaming && zbar_video_get_fd(proc->video) < 0;
163 _zbar_mutex_unlock(&proc->mutex);
164
165 int rc = 1;
166 while(rc > 0 && (waiter->events & EVENTS_PENDING)) {
167 /* FIXME lax w/the locking (though shouldn't matter...) */
168 if(blocking) {
169 zbar_image_t *img = zbar_video_next_image(proc->video);
170 if(!img) {
171 rc = -1;
172 break;
173 }
174
175 /* FIXME reacquire API lock! (refactor w/video thread?) */
176 _zbar_mutex_lock(&proc->mutex);
177 _zbar_process_image(proc, img);
178 zbar_image_destroy(img);
179 _zbar_mutex_unlock(&proc->mutex);
180 }
181 int reltime = _zbar_timer_check(timeout);
182 if(blocking && (reltime < 0 || reltime > MAX_INPUT_BLOCK))
183 reltime = MAX_INPUT_BLOCK;
184 rc = _zbar_processor_input_wait(proc, NULL, reltime);
185 }
186 _zbar_mutex_lock(&proc->mutex);
187 return(rc);
188 }
189
_zbar_processor_wait(zbar_processor_t * proc,unsigned events,zbar_timer_t * timeout)190 int _zbar_processor_wait (zbar_processor_t *proc,
191 unsigned events,
192 zbar_timer_t *timeout)
193 {
194 _zbar_mutex_lock(&proc->mutex);
195 int save_level = proc->lock_level;
196 proc_waiter_t *waiter = proc_waiter_queue(proc);
197 waiter->events = events & EVENTS_PENDING;
198
199 _zbar_processor_unlock(proc, 1);
200 int rc;
201 if(proc->threaded)
202 rc = _zbar_event_wait(&waiter->notify, &proc->mutex, timeout);
203 else
204 rc = proc_wait_unthreaded(proc, waiter, timeout);
205
206 if(rc <= 0 || !proc->threaded) {
207 /* reacquire api lock */
208 waiter->events &= EVENT_CANCELED;
209 proc->wait_next = NULL;
210 if(!proc->lock_level) {
211 proc_waiter_t *w = proc_waiter_dequeue(proc);
212 assert(w == waiter);
213 }
214 else
215 _zbar_event_wait(&waiter->notify, &proc->mutex, NULL);
216 }
217 if(rc > 0 && (waiter->events & EVENT_CANCELED))
218 rc = -1;
219
220 assert(proc->lock_level == 1);
221 assert(_zbar_thread_is_self(proc->lock_owner));
222
223 proc->lock_level = save_level;
224 proc_waiter_release(proc, waiter);
225 _zbar_mutex_unlock(&proc->mutex);
226 return(rc);
227 }
228