blob: b3a152ddcd689cd663edd9edce1574cda498b09f [file] [log] [blame]
/*
* libiio - Library for interfacing industrial I/O (IIO) devices
*
* Copyright (C) 2016 Analog Devices, Inc.
* Author: Paul Cercueil <paul.cercueil@analog.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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 General Public License for more details.
*/
#include "thread-pool.h"
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/eventfd.h>
#include <unistd.h>
/*
* This is used to make sure that all active threads have finished cleanup when
* a STOP event is received. We don't use pthread_join() since for most threads
* we are OK with them exiting asynchronously and there really is no place to
* call pthread_join() to free the thread's resources. We only need to
* synchronize the threads that are still active when the iiod is shutdown to
* give them a chance to release all resources, disable buffers etc, before
* iio_context_destroy() is called.
*/
struct thread_pool {
pthread_mutex_t thread_count_lock;
pthread_cond_t thread_count_cond;
unsigned int thread_count;
int stop_fd;
};
struct thread_body_data {
struct thread_pool *pool;
void (*f)(struct thread_pool *, void *);
void *d;
};
static void thread_pool_thread_started(struct thread_pool *pool)
{
pthread_mutex_lock(&pool->thread_count_lock);
pool->thread_count++;
pthread_mutex_unlock(&pool->thread_count_lock);
}
static void thread_pool_thread_stopped(struct thread_pool *pool)
{
pthread_mutex_lock(&pool->thread_count_lock);
pool->thread_count--;
pthread_cond_signal(&pool->thread_count_cond);
pthread_mutex_unlock(&pool->thread_count_lock);
}
static void * thread_body(void *d)
{
struct thread_body_data *pdata = d;
(*pdata->f)(pdata->pool, pdata->d);
thread_pool_thread_stopped(pdata->pool);
free(pdata);
return NULL;
}
int thread_pool_add_thread(struct thread_pool *pool,
void (*f)(struct thread_pool *, void *),
void *d, const char *name)
{
struct thread_body_data *pdata;
sigset_t sigmask, oldsigmask;
pthread_attr_t attr;
pthread_t thd;
int ret;
pdata = malloc(sizeof(*pdata));
if (!pdata)
return -ENOMEM;
pdata->f = f;
pdata->d = d;
pdata->pool = pool;
sigfillset(&sigmask);
pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
/* In order to avoid race conditions thread_pool_thread_started() must
* be called before the thread is created and
* thread_pool_thread_stopped() must be called right before leaving
* the thread. */
thread_pool_thread_started(pool);
ret = pthread_create(&thd, &attr, thread_body, pdata);
if (ret) {
free(pdata);
thread_pool_thread_stopped(pool);
} else {
#ifdef HAS_PTHREAD_SETNAME_NP
pthread_setname_np(thd, name);
#endif
}
pthread_attr_destroy(&attr);
pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
return ret;
}
struct thread_pool * thread_pool_new(void)
{
struct thread_pool *pool;
pool = malloc(sizeof(*pool));
if (!pool) {
errno = ENOMEM;
return NULL;
}
pool->stop_fd = eventfd(0, EFD_NONBLOCK);
if (pool->stop_fd == -1) {
int err = errno;
free(pool);
errno = err;
return NULL;
}
pthread_mutex_init(&pool->thread_count_lock, NULL);
pthread_cond_init(&pool->thread_count_cond, NULL);
pool->thread_count = 0;
return pool;
}
int thread_pool_get_poll_fd(const struct thread_pool *pool)
{
return pool->stop_fd;
}
void thread_pool_stop(struct thread_pool *pool)
{
uint64_t e = 1;
int ret;
do {
ret = write(pool->stop_fd, &e, sizeof(e));
} while (ret == -1 && errno == EINTR);
}
void thread_pool_stop_and_wait(struct thread_pool *pool)
{
uint64_t e;
int ret;
thread_pool_stop(pool);
pthread_mutex_lock(&pool->thread_count_lock);
while (pool->thread_count)
pthread_cond_wait(&pool->thread_count_cond,
&pool->thread_count_lock);
pthread_mutex_unlock(&pool->thread_count_lock);
do {
ret = read(pool->stop_fd, &e, sizeof(e));
} while (ret != -1 || errno == EINTR);
}
void thread_pool_destroy(struct thread_pool *pool)
{
pthread_mutex_destroy(&pool->thread_count_lock);
pthread_cond_destroy(&pool->thread_count_cond);
close(pool->stop_fd);
free(pool);
}