diff options
Diffstat (limited to 'zbar/processor/posix.c')
-rw-r--r-- | zbar/processor/posix.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/zbar/processor/posix.c b/zbar/processor/posix.c new file mode 100644 index 0000000..06b8d9a --- /dev/null +++ b/zbar/processor/posix.c @@ -0,0 +1,325 @@ +/*------------------------------------------------------------------------ + * 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 "posix.h" +#include <assert.h> +#include <errno.h> +#include <unistd.h> +#include "processor.h" + +static inline int proc_sleep(int timeout) +{ + assert(timeout > 0); + struct timespec sleepns, remns; + sleepns.tv_sec = timeout / 1000; + sleepns.tv_nsec = (timeout % 1000) * 1000000; + while (nanosleep(&sleepns, &remns) && errno == EINTR) + sleepns = remns; + return (1); +} + +int _zbar_event_init(zbar_event_t *event) +{ + event->state = 0; + event->pollfd = -1; +#ifdef HAVE_LIBPTHREAD + pthread_cond_init(&event->cond, NULL); +#endif + return (0); +} + +void _zbar_event_destroy(zbar_event_t *event) +{ + event->state = -1; + event->pollfd = -1; +#ifdef HAVE_LIBPTHREAD + pthread_cond_destroy(&event->cond); +#endif +} + +/* lock must be held */ +void _zbar_event_trigger(zbar_event_t *event) +{ + event->state = 1; +#ifdef HAVE_LIBPTHREAD + pthread_cond_broadcast(&event->cond); +#endif + if (event->pollfd >= 0) { + unsigned i = 0; /* unused */ + if (write(event->pollfd, &i, sizeof(unsigned)) < 0) + perror(""); + event->pollfd = -1; + } +} + +#ifdef HAVE_LIBPTHREAD + +/* lock must be held */ +int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock, + zbar_timer_t *timeout) +{ + int rc = 0; + while (!rc && !event->state) { + if (!timeout) + rc = pthread_cond_wait(&event->cond, lock); + else { + struct timespec *timer; +#if _POSIX_TIMERS > 0 + timer = timeout; +#else + struct timespec tmp; + tmp.tv_sec = timeout->tv_sec; + tmp.tv_nsec = timeout->tv_usec * 1000; + timer = &tmp; +#endif + rc = pthread_cond_timedwait(&event->cond, lock, timer); + } + } + + /* consume/reset event */ + event->state = 0; + + if (!rc) + return (1); /* got event */ + if (rc == ETIMEDOUT) + return (0); /* timed out */ + return (-1); /* error (FIXME save info) */ +} + +int _zbar_thread_start(zbar_thread_t *thr, zbar_thread_proc_t *proc, void *arg, + zbar_mutex_t *lock) +{ + if (thr->started || thr->running) + return (-1 /*FIXME*/); + thr->started = 1; + _zbar_event_init(&thr->notify); + _zbar_event_init(&thr->activity); + + int rc = 0; + _zbar_mutex_lock(lock); + if (pthread_create(&thr->tid, NULL, proc, arg) || + _zbar_event_wait(&thr->activity, lock, NULL) < 0 || !thr->running) { + thr->started = 0; + _zbar_event_destroy(&thr->notify); + _zbar_event_destroy(&thr->activity); + /*rc = err_capture_num(proc, SEV_ERROR, ZBAR_ERR_SYSTEM, + __func__, "spawning thread", rc);*/ + rc = -1 /*FIXME*/; + } + _zbar_mutex_unlock(lock); + return (rc); +} + +int _zbar_thread_stop(zbar_thread_t *thr, zbar_mutex_t *lock) +{ + if (thr->started) { + thr->started = 0; + _zbar_event_trigger(&thr->notify); + while (thr->running) + /* FIXME time out and abandon? */ + _zbar_event_wait(&thr->activity, lock, NULL); + pthread_join(thr->tid, NULL); + _zbar_event_destroy(&thr->notify); + _zbar_event_destroy(&thr->activity); + } + return (0); +} + +#else + +int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock, + zbar_timer_t *timeout) +{ + int rc = !event->state; + if (rc) { + if (!timeout) + /* FIXME was that error or hang? */ + return (-1); + + int sleep = _zbar_timer_check(timeout); + if (sleep) + proc_sleep(sleep); + } + + rc = !event->state; + + /* consume/reset event */ + event->state = 0; + + return (rc); +} + +#endif + +/* used by poll interface. lock is already held */ +static int proc_video_handler(zbar_processor_t *proc, int i) +{ + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_lock(proc); + _zbar_mutex_unlock(&proc->mutex); + + zbar_image_t *img = NULL; + if (proc->streaming) { + /* not expected to block */ + img = zbar_video_next_image(proc->video); + if (img) + _zbar_process_image(proc, img); + } + + _zbar_mutex_lock(&proc->mutex); + _zbar_processor_unlock(proc, 0); + _zbar_mutex_unlock(&proc->mutex); + if (img) + zbar_image_destroy(img); + return (0); +} + +static inline void proc_cache_polling(processor_state_t *state) +{ + /* make a thread-local copy of polling data */ + int n = state->thr_polling.num = state->polling.num; + alloc_polls(&state->thr_polling); + memcpy(state->thr_polling.fds, state->polling.fds, + n * sizeof(struct pollfd)); + memcpy(state->thr_polling.handlers, state->polling.handlers, + n * sizeof(poll_handler_t *)); +} + +static int proc_kick_handler(zbar_processor_t *proc, int i) +{ + processor_state_t *state = proc->state; + zprintf(5, "kicking %d fds\n", state->polling.num); + + unsigned junk[2]; + int rc = read(state->kick_fds[0], junk, 2 * sizeof(unsigned)); + + assert(proc->threaded); + _zbar_mutex_lock(&proc->mutex); + proc_cache_polling(proc->state); + _zbar_mutex_unlock(&proc->mutex); + return (rc); +} + +static inline int proc_poll_inputs(zbar_processor_t *proc, int timeout) +{ + processor_state_t *state = proc->state; + if (state->pre_poll_handler) + state->pre_poll_handler(proc, -1); + + poll_desc_t *p = &state->thr_polling; + assert(p->num); + int rc = poll(p->fds, p->num, timeout); + if (rc <= 0) + /* FIXME detect and handle fatal errors (somehow) */ + return (rc); + int i; + for (i = p->num - 1; i >= 0; i--) + if (p->fds[i].revents) { + if (p->handlers[i]) + p->handlers[i](proc, i); + p->fds[i].revents = 0; /* debug */ + rc--; + } + assert(!rc); + return (1); +} + +int _zbar_processor_input_wait(zbar_processor_t *proc, zbar_event_t *event, + int timeout) +{ + processor_state_t *state = proc->state; + if (state->thr_polling.num) { + if (event) { + _zbar_mutex_lock(&proc->mutex); + event->pollfd = state->kick_fds[1]; + _zbar_mutex_unlock(&proc->mutex); + } + return (proc_poll_inputs(proc, timeout)); + } else if (timeout) + return (proc_sleep(timeout)); + return (-1); +} + +int _zbar_processor_init(zbar_processor_t *proc) +{ + processor_state_t *state = proc->state = + calloc(1, sizeof(processor_state_t)); + state->kick_fds[0] = state->kick_fds[1] = -1; + + if (proc->threaded) { + /* FIXME check errors */ + if (pipe(state->kick_fds)) + return (err_capture(proc, SEV_FATAL, ZBAR_ERR_SYSTEM, __func__, + "failed to open pipe")); + add_poll(proc, state->kick_fds[0], proc_kick_handler); + proc_cache_polling(proc->state); + } + return (0); +} + +int _zbar_processor_cleanup(zbar_processor_t *proc) +{ + processor_state_t *state = proc->state; + if (proc->threaded) { + close(state->kick_fds[0]); + close(state->kick_fds[1]); + state->kick_fds[0] = state->kick_fds[1] = -1; + } + if (state->polling.fds) { + free(state->polling.fds); + state->polling.fds = NULL; + if (!proc->threaded) + state->thr_polling.fds = NULL; + } + if (state->polling.handlers) { + free(state->polling.handlers); + state->polling.handlers = NULL; + if (!proc->threaded) + state->thr_polling.handlers = NULL; + } + if (state->thr_polling.fds) { + free(state->thr_polling.fds); + state->thr_polling.fds = NULL; + } + if (state->thr_polling.handlers) { + free(state->thr_polling.handlers); + state->thr_polling.handlers = NULL; + } + free(proc->state); + proc->state = NULL; + return (0); +} + +int _zbar_processor_enable(zbar_processor_t *proc) +{ + int vid_fd = zbar_video_get_fd(proc->video); + if (vid_fd < 0) + return (0); + + if (proc->streaming) + add_poll(proc, vid_fd, proc_video_handler); + else + remove_poll(proc, vid_fd); + /* FIXME failure recovery? */ + return (0); +} |