blob: 61205547f2394a1f13bf42da83337252090834e4 [file] [log] [blame]
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001 Miklos Szeredi (mszeredi@inf.bme.hu)
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
*/
#include "fuse.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
#include <sys/time.h>
#define FUSE_WORKER_IDLE 10
static pthread_mutex_t fuse_mt_lock = PTHREAD_MUTEX_INITIALIZER;
struct fuse_worker {
struct fuse_worker *next;
struct fuse_worker *prev;
struct fuse *f;
void *data;
fuse_processor_t proc;
struct fuse_cmd *cmd;
int avail;
pthread_cond_t start;
};
static void *do_work(void *data)
{
struct fuse_worker *w = (struct fuse_worker *) data;
int ret;
do {
struct timeval now;
struct timespec timeout;
w->proc(w->f, w->cmd, w->data);
pthread_mutex_lock(&fuse_mt_lock);
w->avail = 1;
w->cmd = NULL;
gettimeofday(&now, NULL);
timeout.tv_sec = now.tv_sec + FUSE_WORKER_IDLE;
timeout.tv_nsec = now.tv_usec * 1000;
ret = 0;
while(w->cmd == NULL && ret != ETIMEDOUT)
ret = pthread_cond_timedwait(&w->start, &fuse_mt_lock, &timeout);
if(ret == ETIMEDOUT) {
struct fuse_worker *next = w->next;
struct fuse_worker *prev = w->prev;
prev->next = next;
next->prev = prev;
pthread_cond_destroy(&w->start);
free(w);
}
w->avail = 0;
pthread_mutex_unlock(&fuse_mt_lock);
} while(ret != ETIMEDOUT);
return NULL;
}
static void start_thread(struct fuse_worker *w)
{
pthread_t thrid;
sigset_t oldset;
sigset_t newset;
int res;
/* Disallow signal reception in worker threads */
sigfillset(&newset);
pthread_sigmask(SIG_SETMASK, &newset, &oldset);
res = pthread_create(&thrid, NULL, do_work, w);
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
if(res != 0) {
fprintf(stderr, "Error creating thread: %s\n", strerror(res));
exit(1);
}
pthread_detach(thrid);
}
void __fuse_loop_mt(struct fuse *f, fuse_processor_t proc, void *data)
{
struct fuse_worker *head;
head = malloc(sizeof(struct fuse_worker));
head->next = head;
head->prev = head;
while(1) {
struct fuse_worker *w;
struct fuse_cmd *cmd = __fuse_read_cmd(f);
if(cmd == NULL)
exit(1);
pthread_mutex_lock(&fuse_mt_lock);
for(w = head->next; w != head; w = w->next)
if(w->avail)
break;
if(w != head) {
pthread_cond_signal(&w->start);
w->cmd = cmd;
w = NULL;
}
else {
struct fuse_worker *prev = head->prev;
struct fuse_worker *next = head;
w = malloc(sizeof(struct fuse_worker));
w->prev = prev;
w->next = next;
next->prev = w;
prev->next = w;
w->f = f;
w->data = data;
w->proc = proc;
w->cmd = cmd;
w->avail = 0;
pthread_cond_init(&w->start, NULL);
}
pthread_mutex_unlock(&fuse_mt_lock);
if(w != NULL)
start_thread(w);
}
}
void fuse_loop_mt(struct fuse *f)
{
__fuse_loop_mt(f, (fuse_processor_t) __fuse_process_cmd, NULL);
}