From a4a40e9024fe0bc9bb5dd860995a6821a0314ffd Mon Sep 17 00:00:00 2001 From: Marko Kreen Date: Fri, 25 Mar 2011 09:13:56 +0200 Subject: [PATCH] netdb: non-blocking compat getaddrinfo_a() Lauch single thread that does lookups. It must not multi-thread as thread-safety of libc getaddrinfo() is uncertain. --- usual/netdb.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 174 insertions(+), 14 deletions(-) diff --git a/usual/netdb.c b/usual/netdb.c index 7a719cc..d7bb4a2 100644 --- a/usual/netdb.c +++ b/usual/netdb.c @@ -20,39 +20,199 @@ #include +/* is compat function needed? */ #ifndef HAVE_GETADDRINFO_A -int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp) +/* full compat if threads are available */ +#ifdef HAVE_PTHREAD_H + +#include +#include + +/* + * Basic blocking lookup + */ + +static void gaia_lookup(pthread_t origin, struct gaicb *list[], int nitems, struct sigevent *sevp) { struct gaicb *g; int i, res; - if (nitems <= 0) - return 0; - if (mode != GAI_WAIT && mode != GAI_NOWAIT) - goto einval; - for (i = 0; i < nitems; i++) { g = list[i]; res = getaddrinfo(g->ar_name, g->ar_service, g->ar_request, &g->ar_result); g->_state = res; } - if (!sevp || sevp->sigev_notify == SIGEV_NONE) - return 0; - - if (sevp->sigev_notify == SIGEV_SIGNAL) { - raise(sevp->sigev_signo); + if (!sevp || sevp->sigev_notify == SIGEV_NONE) { + /* do nothing */ + } else if (sevp->sigev_notify == SIGEV_SIGNAL) { + /* send signal */ + pthread_kill(origin, sevp->sigev_signo); } else if (sevp->sigev_notify == SIGEV_THREAD) { + /* call function */ union sigval sv; sevp->sigev_notify_function(sv); - } else - goto einval; + } +} + +/* + * Thread to run blocking lookup in + */ + +struct GAIAContext { + struct List req_list; + pthread_cond_t cond; + pthread_mutex_t lock; + pthread_t thread; +}; + +struct GAIARequest { + struct List node; + pthread_t origin; + int nitems; + struct sigevent sev; + struct gaicb *list[FLEX_ARRAY]; +}; + +#define RQ_SIZE(n) (offsetof(struct GAIARequest,list) + (n)*(sizeof(struct gaicb *))) + +static void gaia_lock_reqs(struct GAIAContext *ctx) +{ + pthread_mutex_lock(&ctx->lock); +} + +static void gaia_unlock_reqs(struct GAIAContext *ctx) +{ + pthread_mutex_unlock(&ctx->lock); +} + +static void *gaia_lookup_thread(void *arg) +{ + struct GAIAContext *ctx = arg; + struct GAIARequest *rq; + struct List *el; + + gaia_lock_reqs(ctx); +loop: + while (1) { + el = list_pop(&ctx->req_list); + if (!el) + break; + gaia_unlock_reqs(ctx); + + rq = container_of(el, struct GAIARequest, node); + gaia_lookup(rq->origin, rq->list, rq->nitems, &rq->sev); + free(rq); + + gaia_lock_reqs(ctx); + } + pthread_cond_wait(&ctx->cond, &ctx->lock); + goto loop; + + return NULL; +} + +/* + * Functions run in user thread + */ + +static int gaia_post_request(struct GAIAContext *ctx, struct gaicb *list[], int nitems, struct sigevent *sevp) +{ + struct GAIARequest *rq; + + rq = malloc(RQ_SIZE(nitems)); + if (!rq) + return EAI_MEMORY; + + list_init(&rq->node); + rq->origin = pthread_self(); + rq->nitems = nitems; + if (sevp) + rq->sev = *sevp; + else + rq->sev.sigev_notify = SIGEV_NONE; + memcpy(rq->list, list, sizeof(struct gaicb *)); + + gaia_lock_reqs(ctx); + list_append(&ctx->req_list, &rq->node); + gaia_unlock_reqs(ctx); + + pthread_cond_signal(&ctx->cond); + return 0; +} + +static struct GAIAContext *gaia_create_context(void) +{ + struct GAIAContext *ctx; + int err; + + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + list_init(&ctx->req_list); + err = pthread_cond_init(&ctx->cond, NULL); + if (err) + goto failed; + + err = pthread_mutex_init(&ctx->lock, NULL); + if (err) + goto failed; + + err = pthread_create(&ctx->thread, NULL, gaia_lookup_thread, ctx); + if (err) + goto failed; + + return ctx; + +failed: + free(ctx); + errno = err; + return NULL; +} + +/* + * Final interface + */ + +int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp) +{ + static struct GAIAContext *ctx; + + if (nitems <= 0) + return 0; + + if (sevp && sevp->sigev_notify != SIGEV_NONE + && sevp->sigev_notify != SIGEV_SIGNAL + && sevp->sigev_notify != SIGEV_THREAD) + goto einval; + + if (mode == GAI_WAIT) { + gaia_lookup(pthread_self(), list, nitems, sevp); + return 0; + } else if (mode == GAI_NOWAIT) { + if (!ctx) { + ctx = gaia_create_context(); + if (!ctx) + return EAI_MEMORY; + } + return gaia_post_request(ctx, list, nitems, sevp); + } einval: errno = EINVAL; return EAI_SYSTEM; } -#endif +#else /* without threads not much to do */ + +int getaddrinfo_a(int mode, struct gaicb *list[], int nitems, struct sigevent *sevp) +{ + errno = ENOSYS; + return EAI_SYSTEM; +} + +#endif /* !HAVE_PTHREAD_H */ +#endif /* !HAVE_GETADDRINFO_A */ -- 2.39.5