diff options
Diffstat (limited to 'zbar/processor/lock.c')
-rw-r--r-- | zbar/processor/lock.c | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/zbar/processor/lock.c b/zbar/processor/lock.c new file mode 100644 index 0000000..2668b4e --- /dev/null +++ b/zbar/processor/lock.c @@ -0,0 +1,228 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <assert.h> +#include "processor.h" + +/* the processor api lock is a recursive mutex with added capabilities + * to completely drop all lock levels before blocking and atomically + * unblock a waiting set. the lock is implemented using a variation + * of the "specific notification pattern" [cargill], which makes it + * easy to provide these features across platforms with consistent, + * predictable semantics. probably overkill, but additional overhead + * associated with this mechanism should fall in the noise, as locks + * are only exchanged O(frame/image) + * + * [cargill] + * http://www.profcon.com/profcon/cargill/jgf/9809/SpecificNotification.html + */ + +static inline proc_waiter_t *proc_waiter_queue(zbar_processor_t *proc) +{ + proc_waiter_t *waiter = proc->free_waiter; + if (waiter) { + proc->free_waiter = waiter->next; + waiter->events = 0; + } else { + waiter = calloc(1, sizeof(proc_waiter_t)); + _zbar_event_init(&waiter->notify); + } + + waiter->next = NULL; + waiter->requester = _zbar_thread_self(); + + if (proc->wait_head) + proc->wait_tail->next = waiter; + else + proc->wait_head = waiter; + proc->wait_tail = waiter; + return (waiter); +} + +static inline proc_waiter_t *proc_waiter_dequeue(zbar_processor_t *proc) +{ + proc_waiter_t *prev = proc->wait_next, *waiter; + if (prev) + waiter = prev->next; + else + waiter = proc->wait_head; + while (waiter && (waiter->events & EVENTS_PENDING)) { + prev = waiter; + proc->wait_next = waiter; + waiter = waiter->next; + } + + if (waiter) { + if (prev) + prev->next = waiter->next; + else + proc->wait_head = waiter->next; + if (!waiter->next) + proc->wait_tail = prev; + waiter->next = NULL; + + proc->lock_level = 1; + proc->lock_owner = waiter->requester; + } + return (waiter); +} + +static inline void proc_waiter_release(zbar_processor_t *proc, + proc_waiter_t *waiter) +{ + if (waiter) { + waiter->next = proc->free_waiter; + proc->free_waiter = waiter; + } +} + +int _zbar_processor_lock(zbar_processor_t *proc) +{ + proc_waiter_t *waiter; + if (!proc->lock_level) { + proc->lock_owner = _zbar_thread_self(); + proc->lock_level = 1; + return (0); + } + + if (_zbar_thread_is_self(proc->lock_owner)) { + proc->lock_level++; + return (0); + } + + waiter = proc_waiter_queue(proc); + _zbar_event_wait(&waiter->notify, &proc->mutex, NULL); + + assert(proc->lock_level == 1); + assert(_zbar_thread_is_self(proc->lock_owner)); + + proc_waiter_release(proc, waiter); + return (0); +} + +int _zbar_processor_unlock(zbar_processor_t *proc, int all) +{ + assert(proc->lock_level > 0); + assert(_zbar_thread_is_self(proc->lock_owner)); + + if (all) + proc->lock_level = 0; + else + proc->lock_level--; + + if (!proc->lock_level) { + proc_waiter_t *waiter = proc_waiter_dequeue(proc); + if (waiter) + _zbar_event_trigger(&waiter->notify); + } + return (0); +} + +void _zbar_processor_notify(zbar_processor_t *proc, unsigned events) +{ + proc_waiter_t *waiter; + proc->wait_next = NULL; + for (waiter = proc->wait_head; waiter; waiter = waiter->next) + waiter->events = + ((waiter->events & ~events) | (events & EVENT_CANCELED)); + + if (!proc->lock_level) { + waiter = proc_waiter_dequeue(proc); + if (waiter) + _zbar_event_trigger(&waiter->notify); + } +} + +static inline int proc_wait_unthreaded(zbar_processor_t *proc, + proc_waiter_t *waiter, + zbar_timer_t *timeout) +{ + int rc; + int blocking = proc->streaming && zbar_video_get_fd(proc->video) < 0; + _zbar_mutex_unlock(&proc->mutex); + + rc = 1; + while (rc > 0 && (waiter->events & EVENTS_PENDING)) { + int reltime; + /* FIXME lax w/the locking (though shouldn't matter...) */ + if (blocking) { + zbar_image_t *img = zbar_video_next_image(proc->video); + if (!img) { + rc = -1; + break; + } + + /* FIXME reacquire API lock! (refactor w/video thread?) */ + _zbar_mutex_lock(&proc->mutex); + _zbar_process_image(proc, img); + zbar_image_destroy(img); + _zbar_mutex_unlock(&proc->mutex); + } + reltime = _zbar_timer_check(timeout); + if (blocking && (reltime < 0 || reltime > MAX_INPUT_BLOCK)) + reltime = MAX_INPUT_BLOCK; + rc = _zbar_processor_input_wait(proc, NULL, reltime); + } + _zbar_mutex_lock(&proc->mutex); + return (rc); +} + +int _zbar_processor_wait(zbar_processor_t *proc, unsigned events, + zbar_timer_t *timeout) +{ + int save_level; + proc_waiter_t *waiter; + int rc; + + _zbar_mutex_lock(&proc->mutex); + save_level = proc->lock_level; + waiter = proc_waiter_queue(proc); + waiter->events = events & EVENTS_PENDING; + + _zbar_processor_unlock(proc, 1); + if (proc->threaded) + rc = _zbar_event_wait(&waiter->notify, &proc->mutex, timeout); + else + rc = proc_wait_unthreaded(proc, waiter, timeout); + + if (rc <= 0 || !proc->threaded) { + /* reacquire api lock */ + waiter->events &= EVENT_CANCELED; + proc->wait_next = NULL; + if (!proc->lock_level) { + proc_waiter_t *w = proc_waiter_dequeue(proc); + assert(w == waiter); + } else + _zbar_event_wait(&waiter->notify, &proc->mutex, NULL); + } + if (rc > 0 && (waiter->events & EVENT_CANCELED)) + rc = -1; + + assert(proc->lock_level == 1); + assert(_zbar_thread_is_self(proc->lock_owner)); + + proc->lock_level = save_level; + proc_waiter_release(proc, waiter); + _zbar_mutex_unlock(&proc->mutex); + return (rc); +} |