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