diff options
Diffstat (limited to 'gpgme/wait.c')
| -rw-r--r-- | gpgme/wait.c | 558 |
1 files changed, 241 insertions, 317 deletions
diff --git a/gpgme/wait.c b/gpgme/wait.c index 6292bde8..9f849fdf 100644 --- a/gpgme/wait.c +++ b/gpgme/wait.c @@ -35,112 +35,179 @@ #include "io.h" #include "engine.h" -struct wait_item_s; -struct proc_s; +struct fd_table fdt_global; -static struct proc_s *proc_queue; -DEFINE_STATIC_LOCK (proc_queue_lock); - -static int fd_table_size; -static struct io_select_fd_s *fd_table; -DEFINE_STATIC_LOCK (fd_table_lock); +static GpgmeCtx *ctx_done_list; +static int ctx_done_list_size; +static int ctx_done_list_length; +DEFINE_STATIC_LOCK (ctx_done_list_lock); static GpgmeIdleFunc idle_function; - -struct proc_s +struct wait_item_s { - struct proc_s *next; - int pid; - GpgmeCtx ctx; - struct wait_item_s *handler_list; - /* Non-zero if the process has been completed. */ - int done; - /* Non-zero if the status for this process has been returned - already. */ - int reported; -}; - -struct wait_item_s { - struct wait_item_s *next; - int (*handler)(void*,int,int); - void *handler_value; - int inbound; /* this is an inbound data handler fd */ - struct proc_s *proc; /* backlink */ - int done; - int frozen; /* copy of the frozen flag from the fd_table */ + struct wait_item_s *next; + GpgmeIOCb handler; + void *handler_value; + int dir; }; - - -static int do_select ( void ); static void run_idle (void); + +void +_gpgme_fd_table_init (fd_table_t fdt) +{ + INIT_LOCK (fdt->lock); + fdt->fds = NULL; + fdt->size = 0; +} -/* only to be called with a locked proc_queue */ -static int -count_running_fds (struct proc_s *proc) +void +_gpgme_fd_table_deinit (fd_table_t fdt) { - struct wait_item_s *q; - int count = 0; + DESTROY_LOCK (fdt->lock); + if (fdt->fds) + xfree (fdt->fds); +} - for (q = proc->handler_list; q; q=q->next) +/* XXX We should keep a marker and roll over for speed. */ +GpgmeError +_gpgme_fd_table_put (fd_table_t fdt, int fd, int dir, void *opaque, int *idx) +{ + int i, j; + struct io_select_fd_s *new_fds; + + LOCK (fdt->lock); + for (i = 0; i < fdt->size; i++) + { + if (fdt->fds[i].fd == -1) + break; + } + if (i == fdt->size) { - if (!q->frozen && !q->done) - count++; +#define FDT_ALLOCSIZE 10 + new_fds = xtryrealloc (fdt->fds, (fdt->size + FDT_ALLOCSIZE) + * sizeof (*new_fds)); + if (!new_fds) + { + UNLOCK (fdt->lock); + return mk_error (Out_Of_Core); + } + + fdt->fds = new_fds; + fdt->size += FDT_ALLOCSIZE; + for (j = 0; j < FDT_ALLOCSIZE; j++) + fdt->fds[i + j].fd = -1; } - return count; + + fdt->fds[i].fd = fd; + fdt->fds[i].for_read = (dir == 1); + fdt->fds[i].for_write = (dir == 0); + fdt->fds[i].frozen = 0; + fdt->fds[i].signaled = 0; + fdt->fds[i].opaque = opaque; + UNLOCK (fdt->lock); + *idx = i; + return 0; +} + + +/** + * gpgme_register_idle: + * @fnc: Callers idle function + * + * Register a function with GPGME called by GPGME whenever it feels + * that is is idle. NULL may be used to remove this function. + * + * Return value: The idle function pointer that was passed to the + * function at the last time it was invoked, or NULL if the function + * is invoked the first time. + **/ +GpgmeIdleFunc +gpgme_register_idle (GpgmeIdleFunc idle) +{ + GpgmeIdleFunc old_idle = idle_function; + + idle_function = idle; + return old_idle; } -/* only to be called with a locked proc_queue */ static void -set_process_done (struct proc_s *proc) +run_idle () { - struct wait_item_s *q, *q2; - int i; + _gpgme_engine_housecleaning (); + if (idle_function) + idle_function (); +} - assert (proc); - DEBUG2 ("set_process_done(%p) pid=%d", proc, proc->pid); - LOCK (fd_table_lock); - for (q = proc->handler_list; q; q=q2) + +/* Wait on all file descriptors listed in FDT and process them using + the registered callbacks. Returns 0 if nothing to run and 1 if it + did run something. */ +static int +do_select (fd_table_t fdt) +{ + int i, n; + int any = 0; + + LOCK (fdt->lock); + n = _gpgme_io_select (fdt->fds, fdt->size); + + if (n <= 0) { - q2 = q->next; - for (i = 0; i < fd_table_size; i++) + UNLOCK (fdt->lock); + return 0; /* Error or timeout. */ + } + + for (i = 0; i < fdt->size && n; i++) + { + if (fdt->fds[i].fd != -1 && fdt->fds[i].signaled) { - if (fd_table[i].fd != -1 && q == fd_table[i].opaque) - { - fd_table[i].opaque = NULL; - fd_table[i].fd = -1; - } + struct wait_item_s *item; + + assert (n); + n--; + + item = (struct wait_item_s *) fdt->fds[i].opaque; + assert (item); + any = 1; + + fdt->fds[i].signaled = 0; + UNLOCK (fdt->lock); + item->handler (item->handler_value, fdt->fds[i].fd); + LOCK (fdt->lock); } - xfree (q); } - UNLOCK (fd_table_lock); - proc->handler_list = NULL; - proc->done = 1; + UNLOCK (fdt->lock); + + return any; } + + void -_gpgme_remove_proc_from_wait_queue (int pid) +_gpgme_wait_event_cb (void *data, GpgmeEventIO type, void *type_data) { - struct proc_s *proc, *last; + if (type != GPGME_EVENT_DONE) + return; - DEBUG1 ("removing process %d", pid); - LOCK (proc_queue_lock); - for (last = NULL, proc = proc_queue; proc; last = proc, proc = proc->next) - { - if (proc->pid == pid) - { - set_process_done (proc); - if (!last) - proc_queue = proc->next; - else - last->next = proc->next; - xfree (proc); - break; - } - } - UNLOCK (proc_queue_lock); + if (ctx_done_list_size == ctx_done_list_length) + { +#define CTX_DONE_LIST_SIZE_INITIAL 8 + int new_size = ctx_done_list_size ? 2 * ctx_done_list_size + : CTX_DONE_LIST_SIZE_INITIAL; + GpgmeCtx *new_list = xtryrealloc (ctx_done_list, + new_size * sizeof (GpgmeCtx *)); + assert (new_list); +#if 0 + if (!new_list) + return mk_error (Out_Of_Core); +#endif + ctx_done_list = new_list; + ctx_done_list_size = new_size; + } + ctx_done_list[ctx_done_list_length++] = (GpgmeCtx) data; } @@ -155,65 +222,73 @@ _gpgme_remove_proc_from_wait_queue (int pid) * it will return immediately when there is no pending finished request. * * Return value: Context of the finished request or NULL if @hang is false - * and no (or the given) request has finished. + * and no (or not the given) request has finished. **/ GpgmeCtx gpgme_wait (GpgmeCtx ctx, GpgmeError *status, int hang) { - GpgmeCtx retctx = _gpgme_wait_on_condition (ctx, hang, NULL); - if (status) - *status = retctx->error; - return retctx; + ctx = _gpgme_wait_on_condition (ctx, hang, NULL); + if (ctx && status) + *status = ctx->error; + return ctx; +} + +GpgmeError +_gpgme_wait_one (GpgmeCtx ctx) +{ + int hang = 1; + DEBUG1 ("waiting... ctx=%p", ctx); + do + { + if (! do_select (&ctx->fdt)) + hang = 0; + } + while (hang && !ctx->cancel); + if (ctx->cancel) + { + /* FIXME: Paranoia? */ + ctx->cancel = 0; + ctx->pending = 0; + ctx->error = mk_error (Canceled); + } + return ctx->error; } + GpgmeCtx _gpgme_wait_on_condition (GpgmeCtx ctx, int hang, volatile int *cond) { DEBUG3 ("waiting... ctx=%p hang=%d cond=%p", ctx, hang, cond); do { - int any = 0; - struct proc_s *proc; - - do_select (); + if (! do_select (&fdt_global)) + hang = 0; if (cond && *cond) hang = 0; else { - LOCK (proc_queue_lock); - for (proc = proc_queue; proc; proc = proc->next) + int i; + + LOCK (ctx_done_list_lock); + /* A process that is done is eligible for election if it is + the requested context or if it was not yet reported. */ + for (i = 0; i < ctx_done_list_length; i++) + if (!ctx || ctx == ctx_done_list[i]) + break; + if (i < ctx_done_list_length) { - /* A process is done if it has completed voluntarily, or - if the context it lived in was canceled. */ - if (!proc->done && !count_running_fds (proc)) - set_process_done (proc); - else if (!proc->done && proc->ctx->cancel) - { - set_process_done (proc); - proc->ctx->cancel = 0; - proc->ctx->error = mk_error (Canceled); - } - /* A process that is done is eligible for election if it - is in the requested context or if it was not yet - reported. */ - if (proc->done && (proc->ctx == ctx || (!ctx && !proc->reported))) - { - if (!ctx) - ctx = proc->ctx; - hang = 0; - ctx->pending = 0; - proc->reported = 1; - } - if (!proc->done) - any = 1; - } - UNLOCK (proc_queue_lock); - if (!any) - hang = 0; + if (!ctx) + ctx = ctx_done_list[i]; + hang = 0; + ctx->pending = 0; + if (--ctx_done_list_length) + memcpy (&ctx_done_list[i], + &ctx_done_list[i + 1], + (ctx_done_list_length - i) * sizeof (GpgmeCtx *)); + } + UNLOCK (ctx_done_list_lock); } - /* fixme: We should check here for hanging processes. */ - if (hang) run_idle (); } @@ -228,221 +303,70 @@ _gpgme_wait_on_condition (GpgmeCtx ctx, int hang, volatile int *cond) return ctx; } - -/* - * We use this function to do the select stuff for all running - * gpgs. A future version might provide a facility to delegate - * those selects to the GDK select stuff. - * This function must be called only by one thread!! - * Returns: 0 = nothing to run - * 1 = did run something - */ - -static int -do_select (void) + +struct tag { - int i, n; - int any = 0; - - n = _gpgme_io_select (fd_table, fd_table_size); - if (n <= 0) - return 0; /* error or timeout */ - - for (i = 0; i < fd_table_size && n; i++) - { - if (fd_table[i].fd != -1 && fd_table[i].signaled - && !fd_table[i].frozen) - { - struct wait_item_s *q; - - assert (n); - n--; - - q = fd_table[i].opaque; - assert (q); - assert (q->proc); - assert (!q->done); - any = 1; - if (q->handler (q->handler_value, - q->proc->pid, fd_table[i].fd)) - { - DEBUG2 ("setting fd %d (q=%p) done", fd_table[i].fd, q); - q->done = 1; - /* Free the table entry. */ - LOCK (fd_table_lock); - fd_table[i].for_read = 0; - fd_table[i].for_write = 0; - fd_table[i].fd = -1; - fd_table[i].opaque = NULL; - UNLOCK (fd_table_lock); - } - } - } - - return any; -} - + fd_table_t fdt; + int idx; +}; -/* - * called by rungpg.c to register something for select() - */ -GpgmeError -_gpgme_register_pipe_handler (void *opaque, - int (*handler)(void*,int,int), - void *handler_value, - int pid, int fd, int inbound) +void * +_gpgme_add_io_cb (void *data, int fd, int dir, + GpgmeIOCb fnc, void *fnc_data) { - GpgmeCtx ctx = opaque; - struct wait_item_s *q; - struct proc_s *proc; - int i; + GpgmeError err; + fd_table_t fdt = (fd_table_t) (data ? data : &fdt_global); + struct wait_item_s *item; + struct tag *tag; - assert (opaque); - assert (handler); + assert (fdt); + assert (fnc); - /* Allocate a structure to hold info about the handler. */ - q = xtrycalloc (1, sizeof *q); - if (!q) - return mk_error (Out_Of_Core); - q->inbound = inbound; - q->handler = handler; - q->handler_value = handler_value; + tag = xtrymalloc (sizeof *tag); + if (!tag) + return NULL; + tag->fdt = fdt; - /* Put this into the process queue. */ - LOCK (proc_queue_lock); - for (proc = proc_queue; proc && proc->pid != pid; proc = proc->next) - ; - if (!proc) - { - /* A new process. */ - proc = xtrycalloc (1, sizeof *proc); - if (!proc) - { - UNLOCK (proc_queue_lock); - return mk_error (Out_Of_Core); - } - proc->pid = pid; - proc->ctx = ctx; - proc->next = proc_queue; - proc_queue = proc; - } - assert (proc->ctx == ctx); - q->proc = proc; - q->next = proc->handler_list; - proc->handler_list = q; - UNLOCK (proc_queue_lock); - - LOCK (fd_table_lock); - again: - for (i=0; i < fd_table_size; i++) + /* Allocate a structure to hold info about the handler. */ + item = xtrycalloc (1, sizeof *item); + if (!item) { - if (fd_table[i].fd == -1) - { - fd_table[i].fd = fd; - fd_table[i].for_read = inbound; - fd_table[i].for_write = !inbound; - fd_table[i].signaled = 0; - fd_table[i].frozen = 0; - fd_table[i].opaque = q; - UNLOCK (fd_table_lock); - return 0; - } + xfree (tag); + return NULL; } - if ( fd_table_size < 50 ) { - /* FIXME: We have to wait until there are no other readers of the - * table, i.e that the io_select is not active in another thread */ - struct io_select_fd_s *tmp; - - tmp = xtryrealloc (fd_table, (fd_table_size + 10) * sizeof *tmp); - if (tmp) - { - for (i = 0; i < 10; i++) - tmp[fd_table_size+i].fd = -1; - fd_table_size += i; - fd_table = tmp; - goto again; - } - } + item->dir = dir; + item->handler = fnc; + item->handler_value = fnc_data; - UNLOCK (fd_table_lock); - xfree (q); - /* FIXME: Remove the proc table entry. */ - return mk_error (Too_Many_Procs); -} - - -void -_gpgme_freeze_fd (int fd) -{ - int i; - - LOCK (fd_table_lock); - for (i = 0; i < fd_table_size; i++) + err = _gpgme_fd_table_put (fdt, fd, dir, item, &tag->idx); + if (err) { - if (fd_table[i].fd == fd) - { - struct wait_item_s *q; - - fd_table[i].frozen = 1; - q = fd_table[i].opaque; - if (q) - q->frozen = 1; - DEBUG2 ("fd %d frozen (q=%p)", fd, q); - break; - } + xfree (tag); + xfree (item); + errno = ENOMEM; + return 0; } - UNLOCK (fd_table_lock); + + return tag; } void -_gpgme_thaw_fd (int fd) +_gpgme_remove_io_cb (void *data) { - int i; + struct tag *tag = data; + fd_table_t fdt = tag->fdt; + int idx = tag->idx; - LOCK (fd_table_lock); - for (i = 0; i < fd_table_size; i++) - { - if (fd_table[i].fd == fd) - { - struct wait_item_s *q; + LOCK (fdt->lock); + DEBUG2 ("setting fd %d (item=%p) done", fdt->fds[idx].fd, + fdt->fds[idx].opaque); + xfree (fdt->fds[idx].opaque); + xfree (tag); - fd_table[i].frozen = 0; - q = fd_table[i].opaque; - if (q) - q->frozen = 0; - DEBUG2 ("fd %d thawed (q=%p)", fd, q); - break; - } - } - UNLOCK (fd_table_lock); + /* Free the table entry. */ + fdt->fds[idx].fd = -1; + fdt->fds[idx].for_read = 0; + fdt->fds[idx].for_write = 0; + fdt->fds[idx].opaque = NULL; } - -/** - * gpgme_register_idle: - * @fnc: Callers idle function - * - * Register a function with GPGME called by GPGME whenever it feels - * that is is idle. NULL may be used to remove this function. - * - * Return value: The idle function pointer that was passed to the - * function at the last time it was invoked, or NULL if the function - * is invoked the first time. - **/ -GpgmeIdleFunc -gpgme_register_idle (GpgmeIdleFunc idle) -{ - GpgmeIdleFunc old_idle = idle_function; - - idle_function = idle; - return old_idle; -} - - -static void -run_idle () -{ - _gpgme_engine_housecleaning (); - if (idle_function) - idle_function (); -} |
