Discussion:
libfuse: exiting fuse_session_loop
(too old to reply)
Andrew Tomazos
2012-01-18 00:14:23 UTC
Permalink
Hey guys,

(Context: Ubuntu 11.10 and libfuse 2.8.4-1.4ubuntu1 Linux
3.0.0-14-generic #23-Ubuntu SMP Mon Nov 21 20:28:43 UTC 2011 x86_64
x86_64 x86_64 GNU/Linux)

I'm trying to use libfuse. I want to cause fuse_session_loop to exit
(from a signal handler or a different thread), but when I call
fuse_session_exit nothing happens until the session receives a new
request.

fuse_session_exit sets a flag that is read by fuse_session_exited.
Debugging into fuse_session_loop it appears to block on
fuse_chan_recv, so it doesn't check fuse_session_exited again until
the top of the loop...

int fuse_session_loop(struct fuse_session *se)
{
int res = 0;
struct fuse_chan *ch = fuse_session_next_chan(se, NULL);
size_t bufsize = fuse_chan_bufsize(ch);
char *buf = (char *) malloc(bufsize);
if (!buf) {
fprintf(stderr, "fuse: failed to allocate read buffer\n");
return -1;
}

while (!fuse_session_exited(se)) {
struct fuse_chan *tmpch = ch;
res = fuse_chan_recv(&tmpch, buf, bufsize); <--- BLOCKING
if (res == -EINTR)
continue;
if (res <= 0)
break;
fuse_session_process(se, buf, res, tmpch);
}

free(buf);
fuse_session_reset(se);
return res < 0 ? -1 : 0;
}

fuse_chan_recv calls fuse_kern_chan_receive which blocks on the "read"
syscall of the "/dev/fuse" device, so even though the
fuse_session_exited flag is set nothing happens yet.

static int fuse_kern_chan_receive(struct fuse_chan **chp, char *buf,
size_t size)
{
struct fuse_chan *ch = *chp;
int err;
ssize_t res;
struct fuse_session *se = fuse_chan_session(ch);
assert(se != NULL);

restart:
res = read(fuse_chan_fd(ch), buf, size); <--- BLOCKING
err = errno;

if (fuse_session_exited(se))
return 0;
if (res == -1) {
/* ENOENT means the operation was interrupted, it's safe
to restart */
if (err == ENOENT)
goto restart;

if (err == ENODEV) {
fuse_session_exit(se);
return 0;
}
/* Errors occuring during normal operation: EINTR (read
interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem
umounted) */
if (err != EINTR && err != EAGAIN)
perror("fuse: reading device");
return -err;
}
if ((size_t) res < sizeof(struct fuse_in_header)) {
fprintf(stderr, "short read on fuse device\n");
return -EIO;
}
return res;
}

This problem seems to effect the hello_ll.c example provided with
libfuse as well as my program. It makes me think that perhaps there is
some mechanism that is not working that should. Perhaps
fuse_session_exit is supposed to be also doing something that
interrupts the read call, that for some reason is not working on my
system.

Any ideas?

<http://stackoverflow.com/questions/8903448/libfuse-exiting-fuse-session-loop>
Mike Shal
2012-01-19 00:18:26 UTC
Permalink
Post by Andrew Tomazos
Hey guys,
(Context: Ubuntu 11.10 and libfuse 2.8.4-1.4ubuntu1 Linux
3.0.0-14-generic #23-Ubuntu SMP Mon Nov 21 20:28:43 UTC 2011 x86_64
x86_64 x86_64 GNU/Linux)
I'm trying to use libfuse. I want to cause fuse_session_loop to exit
(from a signal handler or a different thread), but when I call
fuse_session_exit nothing happens until the session receives a new
request.
fuse_session_exit sets a flag that is read by fuse_session_exited.
Debugging into fuse_session_loop it appears to block on
fuse_chan_recv, so it doesn't check fuse_session_exited again until
the top of the loop...
I used to do something similar in my program. I had a thread running
fuse, and another thread that called fuse_exit() on the fuse structure
to set the flag. Since it doesn't actually quit until another event
comes in, I just had it try to open the mount point to cause an
immediate event. Eg:

fuse_exit(fuse structure);
fd = open(fuse mountpoint)
(fd should be -1 here, since we expect the fuse loop to exit and this
call to fail)

However, I have since changed this to just calling system("fusermount
-u -z " mountpoint) on Linux (and use unmount() on OSX) because the
fuse_exit() approach was leaking memory. This has since been fixed
here:

https://github.com/torvalds/linux/commit/5dfcc87fd79dfb96ed155b524337dbd0da4f5993

So that may or may not be a concern for you depending on your kernel
and how often you startup/shutdown your fs.

Additionally, if you plan to use helgrind to help find threading
issues in your fs, the fuse_exit() approach will generate a report
(which you can ignore with a suppression file). The fusermount
approach does not need a suppression file if you use a single-threaded
fuse loop.

Hope this helps!
-Mike
Miklos Szeredi
2012-01-20 15:47:38 UTC
Permalink
Post by Mike Shal
Post by Andrew Tomazos
Hey guys,
(Context: Ubuntu 11.10 and libfuse 2.8.4-1.4ubuntu1 Linux
3.0.0-14-generic #23-Ubuntu SMP Mon Nov 21 20:28:43 UTC 2011 x86_64
x86_64 x86_64 GNU/Linux)
I'm trying to use libfuse. I want to cause fuse_session_loop to exit
(from a signal handler or a different thread), but when I call
fuse_session_exit nothing happens until the session receives a new
request.
fuse_session_exit sets a flag that is read by fuse_session_exited.
Debugging into fuse_session_loop it appears to block on
fuse_chan_recv, so it doesn't check fuse_session_exited again until
the top of the loop...
I used to do something similar in my program. I had a thread running
fuse, and another thread that called fuse_exit() on the fuse structure
to set the flag. Since it doesn't actually quit until another event
comes in, I just had it try to open the mount point to cause an
fuse_exit(fuse structure);
fd = open(fuse mountpoint)
(fd should be -1 here, since we expect the fuse loop to exit and this
call to fail)
However, I have since changed this to just calling system("fusermount
-u -z " mountpoint) on Linux (and use unmount() on OSX) because the
fuse_exit() approach was leaking memory. This has since been fixed
https://github.com/torvalds/linux/commit/5dfcc87fd79dfb96ed155b524337dbd0da4f5993
So that may or may not be a concern for you depending on your kernel
and how often you startup/shutdown your fs.
Here's an untested patch to introduce fuse_terminate(). This should do
what you need.

Can you please test?

Thanks,
Miklos


diff --git a/include/fuse.h b/include/fuse.h
index b05152d..2f9fbe6 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -670,10 +670,27 @@ int fuse_loop(struct fuse *f);
/**
* Exit from event loop
*
+ * This sets the exit flag on the event loop. Note: this will only
+ * cause immediate exit if called from a signal handler or from a
+ * filesystem operation. Otherwise the loop will exit on the next
+ * event (filesystem operation or signal).
+ *
* @param f the FUSE handle
*/
void fuse_exit(struct fuse *f);

+
+/**
+ * Terminate the event loop
+ *
+ * Similar to fuse_exit() but also sends SIGTERM to the loop, causing
+ * immediate exit even if not called from a filesystem operation or
+ * signal handler.
+ *
+ * @param f the FUSE handle
+ */
+void fuse_terminate(struct fuse *f);
+
/**
* FUSE event loop with multiple threads
*
diff --git a/lib/fuse.c b/lib/fuse.c
index e01f450..df47c3d 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -49,32 +49,6 @@

#define NODE_TABLE_MIN_SIZE 8192

-struct fuse_config {
- unsigned int uid;
- unsigned int gid;
- unsigned int umask;
- double entry_timeout;
- double negative_timeout;
- double attr_timeout;
- double ac_attr_timeout;
- int ac_attr_timeout_set;
- int remember;
- int nopath;
- int debug;
- int hard_remove;
- int use_ino;
- int readdir_ino;
- int set_mode;
- int set_uid;
- int set_gid;
- int direct_io;
- int kernel_cache;
- int auto_cache;
- int intr;
- int intr_signal;
- int help;
- char *modules;
-};

struct fuse_fs {
struct fuse_operations op;
@@ -94,13 +68,6 @@ struct lock_queue_element {
pthread_cond_t cond;
};

-struct node_table {
- struct node **array;
- size_t use;
- size_t size;
- size_t split;
-};
-
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
@@ -108,38 +75,12 @@ struct node_table {
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)

-struct list_head {
- struct list_head *next;
- struct list_head *prev;
-};
-
struct node_slab {
struct list_head list; /* must be the first member */
struct list_head freelist;
int used;
};

-struct fuse {
- struct fuse_session *se;
- struct node_table name_table;
- struct node_table id_table;
- struct list_head lru_table;
- fuse_ino_t ctr;
- unsigned int generation;
- unsigned int hidectr;
- pthread_mutex_t lock;
- struct fuse_config conf;
- int intr_installed;
- struct fuse_fs *fs;
- int nullpath_ok;
- int curr_ticket;
- struct lock_queue_element *lockq;
- int pagesize;
- struct list_head partial_slabs;
- struct list_head full_slabs;
- pthread_t prune_thread;
-};
-
struct lock {
int type;
off_t start;
@@ -4209,6 +4150,12 @@ void fuse_exit(struct fuse *f)
fuse_session_exit(f->se);
}

+void fuse_terminate(struct fuse *f)
+{
+ fuse_session_exit(f->se);
+ pthread_kill(f->main_thread, SIGTERM);
+}
+
struct fuse_context *fuse_get_context(void)
{
return &fuse_get_context_internal()->ctx;
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 78f1467..2759e37 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -58,6 +58,67 @@ struct fuse_notify_req {
struct fuse_notify_req *prev;
};

+struct fuse_config {
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int umask;
+ double entry_timeout;
+ double negative_timeout;
+ double attr_timeout;
+ double ac_attr_timeout;
+ int ac_attr_timeout_set;
+ int remember;
+ int nopath;
+ int debug;
+ int hard_remove;
+ int use_ino;
+ int readdir_ino;
+ int set_mode;
+ int set_uid;
+ int set_gid;
+ int direct_io;
+ int kernel_cache;
+ int auto_cache;
+ int intr;
+ int intr_signal;
+ int help;
+ char *modules;
+};
+
+struct list_head {
+ struct list_head *next;
+ struct list_head *prev;
+};
+
+struct node_table {
+ struct node **array;
+ size_t use;
+ size_t size;
+ size_t split;
+};
+
+struct fuse {
+ struct fuse_session *se;
+ struct node_table name_table;
+ struct node_table id_table;
+ struct list_head lru_table;
+ fuse_ino_t ctr;
+ unsigned int generation;
+ unsigned int hidectr;
+ pthread_mutex_t lock;
+ struct fuse_config conf;
+ int intr_installed;
+ struct fuse_fs *fs;
+ int nullpath_ok;
+ int curr_ticket;
+ struct lock_queue_element *lockq;
+ int pagesize;
+ struct list_head partial_slabs;
+ struct list_head full_slabs;
+ pthread_t prune_thread;
+ pthread_t main_thread;
+};
+
struct fuse_ll {
int debug;
int allow_root;
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 95bc7d9..5314730 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -196,6 +196,7 @@ FUSE_2.9 {
fuse_clean_cache;
fuse_reply_mmap;
fuse_lowlevel_notify_delete;
+ fuse_terminate;

local:
*;
diff --git a/lib/helper.c b/lib/helper.c
index ace19dd..e34491c 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -301,6 +301,8 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
if (fd)
*fd = fuse_chan_fd(ch);

+ fuse->main_thread = pthread_self();
+
return fuse;

err_unmount:
Mike Shal
2012-01-20 18:36:47 UTC
Permalink
Here's an untested patch to introduce fuse_terminate().  This should do
what you need.
Can you please test?
Thanks,
Miklos
diff --git a/include/fuse.h b/include/fuse.h
index b05152d..2f9fbe6 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -670,10 +670,27 @@ int fuse_loop(struct fuse *f);
 /**
 * Exit from event loop
 *
+ * This sets the exit flag on the event loop.  Note: this will only
+ * cause immediate exit if called from a signal handler or from a
+ * filesystem operation.  Otherwise the loop will exit on the next
+ * event (filesystem operation or signal).
+ *
 */
 void fuse_exit(struct fuse *f);
+
+/**
+ * Terminate the event loop
+ *
+ * Similar to fuse_exit() but also sends SIGTERM to the loop, causing
+ * immediate exit even if not called from a filesystem operation or
+ * signal handler.
+ *
+ */
+void fuse_terminate(struct fuse *f);
+
 /**
 * FUSE event loop with multiple threads
 *
diff --git a/lib/fuse.c b/lib/fuse.c
index e01f450..df47c3d 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -49,32 +49,6 @@
 #define NODE_TABLE_MIN_SIZE 8192
-struct fuse_config {
-       unsigned int uid;
-       unsigned int gid;
-       unsigned int  umask;
-       double entry_timeout;
-       double negative_timeout;
-       double attr_timeout;
-       double ac_attr_timeout;
-       int ac_attr_timeout_set;
-       int remember;
-       int nopath;
-       int debug;
-       int hard_remove;
-       int use_ino;
-       int readdir_ino;
-       int set_mode;
-       int set_uid;
-       int set_gid;
-       int direct_io;
-       int kernel_cache;
-       int auto_cache;
-       int intr;
-       int intr_signal;
-       int help;
-       char *modules;
-};
 struct fuse_fs {
       struct fuse_operations op;
@@ -94,13 +68,6 @@ struct lock_queue_element {
       pthread_cond_t cond;
 };
-struct node_table {
-       struct node **array;
-       size_t use;
-       size_t size;
-       size_t split;
-};
-
 #define container_of(ptr, type, member) ({                              \
                        const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                        (type *)( (char *)__mptr - offsetof(type,member) );})
@@ -108,38 +75,12 @@ struct node_table {
 #define list_entry(ptr, type, member)           \
        container_of(ptr, type, member)
-struct list_head {
-       struct list_head *next;
-       struct list_head *prev;
-};
-
 struct node_slab {
       struct list_head list;  /* must be the first member */
       struct list_head freelist;
       int used;
 };
-struct fuse {
-       struct fuse_session *se;
-       struct node_table name_table;
-       struct node_table id_table;
-       struct list_head lru_table;
-       fuse_ino_t ctr;
-       unsigned int generation;
-       unsigned int hidectr;
-       pthread_mutex_t lock;
-       struct fuse_config conf;
-       int intr_installed;
-       struct fuse_fs *fs;
-       int nullpath_ok;
-       int curr_ticket;
-       struct lock_queue_element *lockq;
-       int pagesize;
-       struct list_head partial_slabs;
-       struct list_head full_slabs;
-       pthread_t prune_thread;
-};
-
 struct lock {
       int type;
       off_t start;
@@ -4209,6 +4150,12 @@ void fuse_exit(struct fuse *f)
       fuse_session_exit(f->se);
 }
+void fuse_terminate(struct fuse *f)
+{
+       fuse_session_exit(f->se);
+       pthread_kill(f->main_thread, SIGTERM);
+}
+
 struct fuse_context *fuse_get_context(void)
 {
       return &fuse_get_context_internal()->ctx;
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 78f1467..2759e37 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -58,6 +58,67 @@ struct fuse_notify_req {
       struct fuse_notify_req *prev;
 };
+struct fuse_config {
+       unsigned int uid;
+       unsigned int gid;
+       unsigned int  umask;
+       double entry_timeout;
+       double negative_timeout;
+       double attr_timeout;
+       double ac_attr_timeout;
+       int ac_attr_timeout_set;
+       int remember;
+       int nopath;
+       int debug;
+       int hard_remove;
+       int use_ino;
+       int readdir_ino;
+       int set_mode;
+       int set_uid;
+       int set_gid;
+       int direct_io;
+       int kernel_cache;
+       int auto_cache;
+       int intr;
+       int intr_signal;
+       int help;
+       char *modules;
+};
+
+struct list_head {
+       struct list_head *next;
+       struct list_head *prev;
+};
+
+struct node_table {
+       struct node **array;
+       size_t use;
+       size_t size;
+       size_t split;
+};
+
+struct fuse {
+       struct fuse_session *se;
+       struct node_table name_table;
+       struct node_table id_table;
+       struct list_head lru_table;
+       fuse_ino_t ctr;
+       unsigned int generation;
+       unsigned int hidectr;
+       pthread_mutex_t lock;
+       struct fuse_config conf;
+       int intr_installed;
+       struct fuse_fs *fs;
+       int nullpath_ok;
+       int curr_ticket;
+       struct lock_queue_element *lockq;
+       int pagesize;
+       struct list_head partial_slabs;
+       struct list_head full_slabs;
+       pthread_t prune_thread;
+       pthread_t main_thread;
+};
+
 struct fuse_ll {
       int debug;
       int allow_root;
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 95bc7d9..5314730 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -196,6 +196,7 @@ FUSE_2.9 {
               fuse_clean_cache;
               fuse_reply_mmap;
               fuse_lowlevel_notify_delete;
+               fuse_terminate;
               *;
diff --git a/lib/helper.c b/lib/helper.c
index ace19dd..e34491c 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -301,6 +301,8 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
       if (fd)
               *fd = fuse_chan_fd(ch);
+       fuse->main_thread = pthread_self();
+
       return fuse;
I tried it out, but I ran into the following issues:
- I don't go through fuse_setup_common() (I use fuse_mount();
fuse_new(); then pthread_create a new thread to do fuse_loop()), so
main_thread was never set, resulting in a crash.

- If instead I explicitly set main_thread to my thread that does
fuse_loop(), fuse will terminate on fuse_terminate(), but my process
is already checking for SIGTERM and shuts down as a result. I could
probably reconfigure things to ignore this SIGTERM, but I don't know
if it is worth the effort.

So for me it is not so useful - I am content to continue using the
system(fusermount) way that I do now. I can't speak for the original
poster though. Maybe he is doing things slightly different.

I do think the extra clarification in the comment of fuse_exit() would
be helpful either way.

Thanks!
-Mike
Hans Beckerus
2012-01-20 22:55:28 UTC
Permalink
I second that the improved comment makes a lot more sense now.
Have not been able to try it out yet, but from the looks of it it would
mean that I no longer need to call pthread_kill()
to terminate the fuse_loop() thread. The latter does the job well though
but looks rather "improvised".

Hans
Post by Mike Shal
Post by Miklos Szeredi
Here's an untested patch to introduce fuse_terminate(). This should do
what you need.
Can you please test?
Thanks,
Miklos
diff --git a/include/fuse.h b/include/fuse.h
index b05152d..2f9fbe6 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -670,10 +670,27 @@ int fuse_loop(struct fuse *f);
/**
* Exit from event loop
*
+ * This sets the exit flag on the event loop. Note: this will only
+ * cause immediate exit if called from a signal handler or from a
+ * filesystem operation. Otherwise the loop will exit on the next
+ * event (filesystem operation or signal).
+ *
*/
void fuse_exit(struct fuse *f);
+
+/**
+ * Terminate the event loop
+ *
+ * Similar to fuse_exit() but also sends SIGTERM to the loop, causing
+ * immediate exit even if not called from a filesystem operation or
+ * signal handler.
+ *
+ */
+void fuse_terminate(struct fuse *f);
+
/**
* FUSE event loop with multiple threads
*
diff --git a/lib/fuse.c b/lib/fuse.c
index e01f450..df47c3d 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -49,32 +49,6 @@
#define NODE_TABLE_MIN_SIZE 8192
-struct fuse_config {
- unsigned int uid;
- unsigned int gid;
- unsigned int umask;
- double entry_timeout;
- double negative_timeout;
- double attr_timeout;
- double ac_attr_timeout;
- int ac_attr_timeout_set;
- int remember;
- int nopath;
- int debug;
- int hard_remove;
- int use_ino;
- int readdir_ino;
- int set_mode;
- int set_uid;
- int set_gid;
- int direct_io;
- int kernel_cache;
- int auto_cache;
- int intr;
- int intr_signal;
- int help;
- char *modules;
-};
struct fuse_fs {
struct fuse_operations op;
@@ -94,13 +68,6 @@ struct lock_queue_element {
pthread_cond_t cond;
};
-struct node_table {
- struct node **array;
- size_t use;
- size_t size;
- size_t split;
-};
-
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
@@ -108,38 +75,12 @@ struct node_table {
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
-struct list_head {
- struct list_head *next;
- struct list_head *prev;
-};
-
struct node_slab {
struct list_head list; /* must be the first member */
struct list_head freelist;
int used;
};
-struct fuse {
- struct fuse_session *se;
- struct node_table name_table;
- struct node_table id_table;
- struct list_head lru_table;
- fuse_ino_t ctr;
- unsigned int generation;
- unsigned int hidectr;
- pthread_mutex_t lock;
- struct fuse_config conf;
- int intr_installed;
- struct fuse_fs *fs;
- int nullpath_ok;
- int curr_ticket;
- struct lock_queue_element *lockq;
- int pagesize;
- struct list_head partial_slabs;
- struct list_head full_slabs;
- pthread_t prune_thread;
-};
-
struct lock {
int type;
off_t start;
@@ -4209,6 +4150,12 @@ void fuse_exit(struct fuse *f)
fuse_session_exit(f->se);
}
+void fuse_terminate(struct fuse *f)
+{
+ fuse_session_exit(f->se);
+ pthread_kill(f->main_thread, SIGTERM);
+}
+
struct fuse_context *fuse_get_context(void)
{
return&fuse_get_context_internal()->ctx;
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 78f1467..2759e37 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -58,6 +58,67 @@ struct fuse_notify_req {
struct fuse_notify_req *prev;
};
+struct fuse_config {
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int umask;
+ double entry_timeout;
+ double negative_timeout;
+ double attr_timeout;
+ double ac_attr_timeout;
+ int ac_attr_timeout_set;
+ int remember;
+ int nopath;
+ int debug;
+ int hard_remove;
+ int use_ino;
+ int readdir_ino;
+ int set_mode;
+ int set_uid;
+ int set_gid;
+ int direct_io;
+ int kernel_cache;
+ int auto_cache;
+ int intr;
+ int intr_signal;
+ int help;
+ char *modules;
+};
+
+struct list_head {
+ struct list_head *next;
+ struct list_head *prev;
+};
+
+struct node_table {
+ struct node **array;
+ size_t use;
+ size_t size;
+ size_t split;
+};
+
+struct fuse {
+ struct fuse_session *se;
+ struct node_table name_table;
+ struct node_table id_table;
+ struct list_head lru_table;
+ fuse_ino_t ctr;
+ unsigned int generation;
+ unsigned int hidectr;
+ pthread_mutex_t lock;
+ struct fuse_config conf;
+ int intr_installed;
+ struct fuse_fs *fs;
+ int nullpath_ok;
+ int curr_ticket;
+ struct lock_queue_element *lockq;
+ int pagesize;
+ struct list_head partial_slabs;
+ struct list_head full_slabs;
+ pthread_t prune_thread;
+ pthread_t main_thread;
+};
+
struct fuse_ll {
int debug;
int allow_root;
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 95bc7d9..5314730 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -196,6 +196,7 @@ FUSE_2.9 {
fuse_clean_cache;
fuse_reply_mmap;
fuse_lowlevel_notify_delete;
+ fuse_terminate;
*;
diff --git a/lib/helper.c b/lib/helper.c
index ace19dd..e34491c 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -301,6 +301,8 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
if (fd)
*fd = fuse_chan_fd(ch);
+ fuse->main_thread = pthread_self();
+
return fuse;
- I don't go through fuse_setup_common() (I use fuse_mount();
fuse_new(); then pthread_create a new thread to do fuse_loop()), so
main_thread was never set, resulting in a crash.
- If instead I explicitly set main_thread to my thread that does
fuse_loop(), fuse will terminate on fuse_terminate(), but my process
is already checking for SIGTERM and shuts down as a result. I could
probably reconfigure things to ignore this SIGTERM, but I don't know
if it is worth the effort.
So for me it is not so useful - I am content to continue using the
system(fusermount) way that I do now. I can't speak for the original
poster though. Maybe he is doing things slightly different.
I do think the extra clarification in the comment of fuse_exit() would
be helpful either way.
Thanks!
-Mike
------------------------------------------------------------------------------
Keep Your Developer Skills Current with LearnDevNow!
The most comprehensive online learning library for Microsoft developers
is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3,
Metro Style Apps, more. Free future releases when you subscribe now!
http://p.sf.net/sfu/learndevnow-d2d
_______________________________________________
fuse-devel mailing list
https://lists.sourceforge.net/lists/listinfo/fuse-devel
Miklos Szeredi
2012-01-23 16:27:24 UTC
Permalink
Post by Mike Shal
- I don't go through fuse_setup_common() (I use fuse_mount();
fuse_new(); then pthread_create a new thread to do fuse_loop()), so
main_thread was never set, resulting in a crash.
Thanks for testing. Yeah f->main_thread should be initialized in
fuse_loop/fuse_loop_mt, rather than in fuse_setup.

Fixed patch below.
Post by Mike Shal
- If instead I explicitly set main_thread to my thread that does
fuse_loop(), fuse will terminate on fuse_terminate(), but my process
is already checking for SIGTERM and shuts down as a result. I could
probably reconfigure things to ignore this SIGTERM, but I don't know
if it is worth the effort.
Instead of initiating shutdown from SIGTERM you can initiate it after
fuse_loop(). That way you can use the default signal handlers which are
set up with fuse_set_signal_handlers().
Post by Mike Shal
So for me it is not so useful - I am content to continue using the
system(fusermount) way that I do now. I can't speak for the original
poster though. Maybe he is doing things slightly different.
Doing "fusermount -u -z ..." will cleanly remove the mount and will not
fail.

But note, this is not guaranteed to terminate the filesystem. If some
process is still accessing it (e.g. CWD inside the mount) then the
filesystem process will continue serving requests as long as it needs.

So doing fuse_terminate() and "fusermount -u -z" are two different
things with different purposes. Which one you need is for you to
decide.

Thanks,
Miklos

diff --git a/include/fuse.h b/include/fuse.h
index b05152d..2f9fbe6 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -670,10 +670,27 @@ int fuse_loop(struct fuse *f);
/**
* Exit from event loop
*
+ * This sets the exit flag on the event loop. Note: this will only
+ * cause immediate exit if called from a signal handler or from a
+ * filesystem operation. Otherwise the loop will exit on the next
+ * event (filesystem operation or signal).
+ *
* @param f the FUSE handle
*/
void fuse_exit(struct fuse *f);

+
+/**
+ * Terminate the event loop
+ *
+ * Similar to fuse_exit() but also sends SIGTERM to the loop, causing
+ * immediate exit even if not called from a filesystem operation or
+ * signal handler.
+ *
+ * @param f the FUSE handle
+ */
+void fuse_terminate(struct fuse *f);
+
/**
* FUSE event loop with multiple threads
*
diff --git a/lib/fuse.c b/lib/fuse.c
index e01f450..fb9ec05 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -49,32 +49,6 @@

#define NODE_TABLE_MIN_SIZE 8192

-struct fuse_config {
- unsigned int uid;
- unsigned int gid;
- unsigned int umask;
- double entry_timeout;
- double negative_timeout;
- double attr_timeout;
- double ac_attr_timeout;
- int ac_attr_timeout_set;
- int remember;
- int nopath;
- int debug;
- int hard_remove;
- int use_ino;
- int readdir_ino;
- int set_mode;
- int set_uid;
- int set_gid;
- int direct_io;
- int kernel_cache;
- int auto_cache;
- int intr;
- int intr_signal;
- int help;
- char *modules;
-};

struct fuse_fs {
struct fuse_operations op;
@@ -94,13 +68,6 @@ struct lock_queue_element {
pthread_cond_t cond;
};

-struct node_table {
- struct node **array;
- size_t use;
- size_t size;
- size_t split;
-};
-
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
@@ -108,38 +75,12 @@ struct node_table {
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)

-struct list_head {
- struct list_head *next;
- struct list_head *prev;
-};
-
struct node_slab {
struct list_head list; /* must be the first member */
struct list_head freelist;
int used;
};

-struct fuse {
- struct fuse_session *se;
- struct node_table name_table;
- struct node_table id_table;
- struct list_head lru_table;
- fuse_ino_t ctr;
- unsigned int generation;
- unsigned int hidectr;
- pthread_mutex_t lock;
- struct fuse_config conf;
- int intr_installed;
- struct fuse_fs *fs;
- int nullpath_ok;
- int curr_ticket;
- struct lock_queue_element *lockq;
- int pagesize;
- struct list_head partial_slabs;
- struct list_head full_slabs;
- pthread_t prune_thread;
-};
-
struct lock {
int type;
off_t start;
@@ -4188,13 +4129,26 @@ static int fuse_session_loop_remember(struct fuse *f)

int fuse_loop(struct fuse *f)
{
+ int err;
+
if (!f)
return -1;

+ pthread_mutex_lock(&f->lock);
+ f->main_thread = pthread_self();
+ f->in_loop = 1;
+ pthread_mutex_unlock(&f->lock);
+
if (lru_enabled(f))
- return fuse_session_loop_remember(f);
+ err = fuse_session_loop_remember(f);
+ else
+ err = fuse_session_loop(f->se);

- return fuse_session_loop(f->se);
+ pthread_mutex_lock(&f->lock);
+ f->in_loop = 0;
+ pthread_mutex_unlock(&f->lock);
+
+ return err;
}

int fuse_invalidate(struct fuse *f, const char *path)
@@ -4209,6 +4163,15 @@ void fuse_exit(struct fuse *f)
fuse_session_exit(f->se);
}

+void fuse_terminate(struct fuse *f)
+{
+ pthread_mutex_lock(&f->lock);
+ fuse_session_exit(f->se);
+ if (f->in_loop)
+ pthread_kill(f->main_thread, SIGTERM);
+ pthread_mutex_unlock(&f->lock);
+}
+
struct fuse_context *fuse_get_context(void)
{
return &fuse_get_context_internal()->ctx;
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 78f1467..3de5103 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -58,6 +58,68 @@ struct fuse_notify_req {
struct fuse_notify_req *prev;
};

+struct fuse_config {
+ unsigned int uid;
+ unsigned int gid;
+ unsigned int umask;
+ double entry_timeout;
+ double negative_timeout;
+ double attr_timeout;
+ double ac_attr_timeout;
+ int ac_attr_timeout_set;
+ int remember;
+ int nopath;
+ int debug;
+ int hard_remove;
+ int use_ino;
+ int readdir_ino;
+ int set_mode;
+ int set_uid;
+ int set_gid;
+ int direct_io;
+ int kernel_cache;
+ int auto_cache;
+ int intr;
+ int intr_signal;
+ int help;
+ char *modules;
+};
+
+struct list_head {
+ struct list_head *next;
+ struct list_head *prev;
+};
+
+struct node_table {
+ struct node **array;
+ size_t use;
+ size_t size;
+ size_t split;
+};
+
+struct fuse {
+ struct fuse_session *se;
+ struct node_table name_table;
+ struct node_table id_table;
+ struct list_head lru_table;
+ fuse_ino_t ctr;
+ unsigned int generation;
+ unsigned int hidectr;
+ pthread_mutex_t lock;
+ struct fuse_config conf;
+ int intr_installed;
+ struct fuse_fs *fs;
+ int nullpath_ok;
+ int curr_ticket;
+ struct lock_queue_element *lockq;
+ int pagesize;
+ struct list_head partial_slabs;
+ struct list_head full_slabs;
+ pthread_t prune_thread;
+ pthread_t main_thread;
+ int in_loop;
+};
+
struct fuse_ll {
int debug;
int allow_root;
diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c
index f6dbe71..7a8537d 100644
--- a/lib/fuse_mt.c
+++ b/lib/fuse_mt.c
@@ -66,6 +66,24 @@ static int mt_chan_receive(struct fuse_chan **chp, char *buf, size_t size)
return sizeof(cmd);
}

+static int fuse_loop_mt_session(struct fuse *f, struct fuse_session *se)
+{
+ int err;
+
+ pthread_mutex_lock(&f->lock);
+ f->main_thread = pthread_self();
+ f->in_loop = 1;
+ pthread_mutex_unlock(&f->lock);
+
+ err = fuse_session_loop_mt(se);
+
+ pthread_mutex_lock(&f->lock);
+ f->in_loop = 0;
+ pthread_mutex_unlock(&f->lock);
+
+ return err;
+}
+
int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data)
{
int res;
@@ -100,21 +118,23 @@ int fuse_loop_mt_proc(struct fuse *f, fuse_processor_t proc, void *data)
return -1;
}
fuse_session_add_chan(se, ch);
- res = fuse_session_loop_mt(se);
+ res = fuse_loop_mt_session(f, se);
fuse_session_destroy(se);
return res;
}

int fuse_loop_mt(struct fuse *f)
{
+ int res;
+
if (f == NULL)
return -1;

- int res = fuse_start_cleanup_thread(f);
+ res = fuse_start_cleanup_thread(f);
if (res)
return -1;

- res = fuse_session_loop_mt(fuse_get_session(f));
+ res = fuse_loop_mt_session(f, fuse_get_session(f));
fuse_stop_cleanup_thread(f);
return res;
}
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 95bc7d9..5314730 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -196,6 +196,7 @@ FUSE_2.9 {
fuse_clean_cache;
fuse_reply_mmap;
fuse_lowlevel_notify_delete;
+ fuse_terminate;

local:
*;
Andrew Tomazos
2012-01-21 05:10:41 UTC
Permalink
Hey all,

So I figured out what was going on in my case anyway:

Normally if a signal handler is executed while a system call (such as
read(2)) is blocking, the system call returns immediately (after the
signal handler is completed executing) with an EINTR. This is clearly
the behaviour that fuse_session_loop and fuse_session_exit were
designed for.

However if the signal handler is installed with the SA_RESTART flag
set (see sigaction(2)) the system call will not return with EINTR
after the signal handler has executed. The system call will resume
blocking instead.

For some reason on my system (Ubuntu 11.10 x86_64) the default
behaviour of signal(2) is to install the signal handler with the
SA_RESTART flag.

ie the strace of the following program...

#include <stdlib.h>
#include <signal.h>

void f(int signum) {}

int main()
{
signal(SIGINT,f);
return EXIT_SUCCESS;
}

...is as follows...

rt_sigaction(SIGINT, {0x400524, [INT], SA_RESTORER|SA_RESTART,
0x7f4997e1f420}, {SIG_DFL, [], 0}, 8) = 0

For this reason the signals (in the examples provided with fuse and my
own program) did not interrupt the blocking read in
fuse_kern_chan_receive as their authors expected them too.

The fix was to use sigaction(2) (and leave SA_RESTART bit zeroed) to
install the handler (instead of signal(2)).

An open question that still remains is why does a call to signal(2)
have the SA_RESTART flag on by default? I would expect interrupting
(not restarting) is the expected default behavior.

<http://stackoverflow.com/questions/8903448/libfuse-exiting-fuse-session-loop>

Enjoy,
Andrew.
Post by Mike Shal
Post by Andrew Tomazos
Hey guys,
(Context: Ubuntu 11.10 and libfuse 2.8.4-1.4ubuntu1 Linux
3.0.0-14-generic #23-Ubuntu SMP Mon Nov 21 20:28:43 UTC 2011 x86_64
x86_64 x86_64 GNU/Linux)
I'm trying to use libfuse. I want to cause fuse_session_loop to exit
(from a signal handler or a different thread), but when I call
fuse_session_exit nothing happens until the session receives a new
request.
fuse_session_exit sets a flag that is read by fuse_session_exited.
Debugging into fuse_session_loop it appears to block on
fuse_chan_recv, so it doesn't check fuse_session_exited again until
the top of the loop...
I used to do something similar in my program. I had a thread running
fuse, and another thread that called fuse_exit() on the fuse structure
to set the flag. Since it doesn't actually quit until another event
comes in, I just had it try to open the mount point to cause an
fuse_exit(fuse structure);
fd = open(fuse mountpoint)
(fd should be -1 here, since we expect the fuse loop to exit and this
call to fail)
However, I have since changed this to just calling system("fusermount
-u -z " mountpoint) on Linux (and use unmount() on OSX) because the
fuse_exit() approach was leaking memory. This has since been fixed
https://github.com/torvalds/linux/commit/5dfcc87fd79dfb96ed155b524337dbd0da4f5993
So that may or may not be a concern for you depending on your kernel
and how often you startup/shutdown your fs.
Here's an untested patch to introduce fuse_terminate().  This should do
what you need.
Can you please test?
Thanks,
Miklos
diff --git a/include/fuse.h b/include/fuse.h
index b05152d..2f9fbe6 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -670,10 +670,27 @@ int fuse_loop(struct fuse *f);
 /**
 * Exit from event loop
 *
+ * This sets the exit flag on the event loop.  Note: this will only
+ * cause immediate exit if called from a signal handler or from a
+ * filesystem operation.  Otherwise the loop will exit on the next
+ * event (filesystem operation or signal).
+ *
 */
 void fuse_exit(struct fuse *f);
+
+/**
+ * Terminate the event loop
+ *
+ * Similar to fuse_exit() but also sends SIGTERM to the loop, causing
+ * immediate exit even if not called from a filesystem operation or
+ * signal handler.
+ *
+ */
+void fuse_terminate(struct fuse *f);
+
 /**
 * FUSE event loop with multiple threads
 *
diff --git a/lib/fuse.c b/lib/fuse.c
index e01f450..df47c3d 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -49,32 +49,6 @@
 #define NODE_TABLE_MIN_SIZE 8192
-struct fuse_config {
-       unsigned int uid;
-       unsigned int gid;
-       unsigned int  umask;
-       double entry_timeout;
-       double negative_timeout;
-       double attr_timeout;
-       double ac_attr_timeout;
-       int ac_attr_timeout_set;
-       int remember;
-       int nopath;
-       int debug;
-       int hard_remove;
-       int use_ino;
-       int readdir_ino;
-       int set_mode;
-       int set_uid;
-       int set_gid;
-       int direct_io;
-       int kernel_cache;
-       int auto_cache;
-       int intr;
-       int intr_signal;
-       int help;
-       char *modules;
-};
 struct fuse_fs {
       struct fuse_operations op;
@@ -94,13 +68,6 @@ struct lock_queue_element {
       pthread_cond_t cond;
 };
-struct node_table {
-       struct node **array;
-       size_t use;
-       size_t size;
-       size_t split;
-};
-
 #define container_of(ptr, type, member) ({                              \
                        const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                        (type *)( (char *)__mptr - offsetof(type,member) );})
@@ -108,38 +75,12 @@ struct node_table {
 #define list_entry(ptr, type, member)           \
        container_of(ptr, type, member)
-struct list_head {
-       struct list_head *next;
-       struct list_head *prev;
-};
-
 struct node_slab {
       struct list_head list;  /* must be the first member */
       struct list_head freelist;
       int used;
 };
-struct fuse {
-       struct fuse_session *se;
-       struct node_table name_table;
-       struct node_table id_table;
-       struct list_head lru_table;
-       fuse_ino_t ctr;
-       unsigned int generation;
-       unsigned int hidectr;
-       pthread_mutex_t lock;
-       struct fuse_config conf;
-       int intr_installed;
-       struct fuse_fs *fs;
-       int nullpath_ok;
-       int curr_ticket;
-       struct lock_queue_element *lockq;
-       int pagesize;
-       struct list_head partial_slabs;
-       struct list_head full_slabs;
-       pthread_t prune_thread;
-};
-
 struct lock {
       int type;
       off_t start;
@@ -4209,6 +4150,12 @@ void fuse_exit(struct fuse *f)
       fuse_session_exit(f->se);
 }
+void fuse_terminate(struct fuse *f)
+{
+       fuse_session_exit(f->se);
+       pthread_kill(f->main_thread, SIGTERM);
+}
+
 struct fuse_context *fuse_get_context(void)
 {
       return &fuse_get_context_internal()->ctx;
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 78f1467..2759e37 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -58,6 +58,67 @@ struct fuse_notify_req {
       struct fuse_notify_req *prev;
 };
+struct fuse_config {
+       unsigned int uid;
+       unsigned int gid;
+       unsigned int  umask;
+       double entry_timeout;
+       double negative_timeout;
+       double attr_timeout;
+       double ac_attr_timeout;
+       int ac_attr_timeout_set;
+       int remember;
+       int nopath;
+       int debug;
+       int hard_remove;
+       int use_ino;
+       int readdir_ino;
+       int set_mode;
+       int set_uid;
+       int set_gid;
+       int direct_io;
+       int kernel_cache;
+       int auto_cache;
+       int intr;
+       int intr_signal;
+       int help;
+       char *modules;
+};
+
+struct list_head {
+       struct list_head *next;
+       struct list_head *prev;
+};
+
+struct node_table {
+       struct node **array;
+       size_t use;
+       size_t size;
+       size_t split;
+};
+
+struct fuse {
+       struct fuse_session *se;
+       struct node_table name_table;
+       struct node_table id_table;
+       struct list_head lru_table;
+       fuse_ino_t ctr;
+       unsigned int generation;
+       unsigned int hidectr;
+       pthread_mutex_t lock;
+       struct fuse_config conf;
+       int intr_installed;
+       struct fuse_fs *fs;
+       int nullpath_ok;
+       int curr_ticket;
+       struct lock_queue_element *lockq;
+       int pagesize;
+       struct list_head partial_slabs;
+       struct list_head full_slabs;
+       pthread_t prune_thread;
+       pthread_t main_thread;
+};
+
 struct fuse_ll {
       int debug;
       int allow_root;
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 95bc7d9..5314730 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -196,6 +196,7 @@ FUSE_2.9 {
               fuse_clean_cache;
               fuse_reply_mmap;
               fuse_lowlevel_notify_delete;
+               fuse_terminate;
               *;
diff --git a/lib/helper.c b/lib/helper.c
index ace19dd..e34491c 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -301,6 +301,8 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
       if (fd)
               *fd = fuse_chan_fd(ch);
+       fuse->main_thread = pthread_self();
+
       return fuse;
--
Andrew Tomazos <***@tomazos.com> <http://www.tomazos.com>
Andrew Tomazos
2012-01-21 05:26:00 UTC
Permalink
Miklos,

I took a look at your patch. I find that architecturally the cleanest
way to terminate off a blocking queue is to add a unique shutdown
message as a queue item, rather than having a separate flag and trying
to interrupt the block. In this case this would mean replacing the
exit flag, with some sort of shutdown request that gets injected into
the /dev/fuse handle incoming requests. Perhaps it can be written to
/dev/fuse, go through the kernel module (with the kernel module taking
some action on it as well, perhaps refusing any more requests for
example) and then be read off /dev/fuse and come through the same as
any other request, the only difference being is that after it is
processed it causes the event loop to exit.

Just my 2c. I have only just started using libfuse, so keep in mind I
have no idea what I'm talking about. :)

Enjoy,
Andrew.
Post by Andrew Tomazos
Hey all,
Normally if a signal handler is executed while a system call (such as
read(2)) is blocking, the system call returns immediately (after the
signal handler is completed executing) with an EINTR. This is clearly
the behaviour that fuse_session_loop and fuse_session_exit were
designed for.
However if the signal handler is installed with the SA_RESTART flag
set (see sigaction(2)) the system call will not return with EINTR
after the signal handler has executed. The system call will resume
blocking instead.
For some reason on my system (Ubuntu 11.10 x86_64) the default
behaviour of signal(2) is to install the signal handler with the
SA_RESTART flag.
ie the strace of the following program...
#include <stdlib.h>
#include <signal.h>
void f(int signum) {}
int main()
{
   signal(SIGINT,f);
   return EXIT_SUCCESS;
}
...is as follows...
rt_sigaction(SIGINT, {0x400524, [INT], SA_RESTORER|SA_RESTART,
0x7f4997e1f420}, {SIG_DFL, [], 0}, 8) = 0
For this reason the signals (in the examples provided with fuse and my
own program) did not interrupt the blocking read in
fuse_kern_chan_receive as their authors expected them too.
The fix was to use sigaction(2) (and leave SA_RESTART bit zeroed) to
install the handler (instead of signal(2)).
An open question that still remains is why does a call to signal(2)
have the SA_RESTART flag on by default? I would expect interrupting
(not restarting) is the expected default behavior.
<http://stackoverflow.com/questions/8903448/libfuse-exiting-fuse-session-loop>
Enjoy,
Andrew.
Post by Mike Shal
Post by Andrew Tomazos
Hey guys,
(Context: Ubuntu 11.10 and libfuse 2.8.4-1.4ubuntu1 Linux
3.0.0-14-generic #23-Ubuntu SMP Mon Nov 21 20:28:43 UTC 2011 x86_64
x86_64 x86_64 GNU/Linux)
I'm trying to use libfuse. I want to cause fuse_session_loop to exit
(from a signal handler or a different thread), but when I call
fuse_session_exit nothing happens until the session receives a new
request.
fuse_session_exit sets a flag that is read by fuse_session_exited.
Debugging into fuse_session_loop it appears to block on
fuse_chan_recv, so it doesn't check fuse_session_exited again until
the top of the loop...
I used to do something similar in my program. I had a thread running
fuse, and another thread that called fuse_exit() on the fuse structure
to set the flag. Since it doesn't actually quit until another event
comes in, I just had it try to open the mount point to cause an
fuse_exit(fuse structure);
fd = open(fuse mountpoint)
(fd should be -1 here, since we expect the fuse loop to exit and this
call to fail)
However, I have since changed this to just calling system("fusermount
-u -z " mountpoint) on Linux (and use unmount() on OSX) because the
fuse_exit() approach was leaking memory. This has since been fixed
https://github.com/torvalds/linux/commit/5dfcc87fd79dfb96ed155b524337dbd0da4f5993
So that may or may not be a concern for you depending on your kernel
and how often you startup/shutdown your fs.
Here's an untested patch to introduce fuse_terminate().  This should do
what you need.
Can you please test?
Thanks,
Miklos
diff --git a/include/fuse.h b/include/fuse.h
index b05152d..2f9fbe6 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -670,10 +670,27 @@ int fuse_loop(struct fuse *f);
 /**
 * Exit from event loop
 *
+ * This sets the exit flag on the event loop.  Note: this will only
+ * cause immediate exit if called from a signal handler or from a
+ * filesystem operation.  Otherwise the loop will exit on the next
+ * event (filesystem operation or signal).
+ *
 */
 void fuse_exit(struct fuse *f);
+
+/**
+ * Terminate the event loop
+ *
+ * Similar to fuse_exit() but also sends SIGTERM to the loop, causing
+ * immediate exit even if not called from a filesystem operation or
+ * signal handler.
+ *
+ */
+void fuse_terminate(struct fuse *f);
+
 /**
 * FUSE event loop with multiple threads
 *
diff --git a/lib/fuse.c b/lib/fuse.c
index e01f450..df47c3d 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -49,32 +49,6 @@
 #define NODE_TABLE_MIN_SIZE 8192
-struct fuse_config {
-       unsigned int uid;
-       unsigned int gid;
-       unsigned int  umask;
-       double entry_timeout;
-       double negative_timeout;
-       double attr_timeout;
-       double ac_attr_timeout;
-       int ac_attr_timeout_set;
-       int remember;
-       int nopath;
-       int debug;
-       int hard_remove;
-       int use_ino;
-       int readdir_ino;
-       int set_mode;
-       int set_uid;
-       int set_gid;
-       int direct_io;
-       int kernel_cache;
-       int auto_cache;
-       int intr;
-       int intr_signal;
-       int help;
-       char *modules;
-};
 struct fuse_fs {
       struct fuse_operations op;
@@ -94,13 +68,6 @@ struct lock_queue_element {
       pthread_cond_t cond;
 };
-struct node_table {
-       struct node **array;
-       size_t use;
-       size_t size;
-       size_t split;
-};
-
 #define container_of(ptr, type, member) ({                              \
                        const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                        (type *)( (char *)__mptr - offsetof(type,member) );})
@@ -108,38 +75,12 @@ struct node_table {
 #define list_entry(ptr, type, member)           \
        container_of(ptr, type, member)
-struct list_head {
-       struct list_head *next;
-       struct list_head *prev;
-};
-
 struct node_slab {
       struct list_head list;  /* must be the first member */
       struct list_head freelist;
       int used;
 };
-struct fuse {
-       struct fuse_session *se;
-       struct node_table name_table;
-       struct node_table id_table;
-       struct list_head lru_table;
-       fuse_ino_t ctr;
-       unsigned int generation;
-       unsigned int hidectr;
-       pthread_mutex_t lock;
-       struct fuse_config conf;
-       int intr_installed;
-       struct fuse_fs *fs;
-       int nullpath_ok;
-       int curr_ticket;
-       struct lock_queue_element *lockq;
-       int pagesize;
-       struct list_head partial_slabs;
-       struct list_head full_slabs;
-       pthread_t prune_thread;
-};
-
 struct lock {
       int type;
       off_t start;
@@ -4209,6 +4150,12 @@ void fuse_exit(struct fuse *f)
       fuse_session_exit(f->se);
 }
+void fuse_terminate(struct fuse *f)
+{
+       fuse_session_exit(f->se);
+       pthread_kill(f->main_thread, SIGTERM);
+}
+
 struct fuse_context *fuse_get_context(void)
 {
       return &fuse_get_context_internal()->ctx;
diff --git a/lib/fuse_i.h b/lib/fuse_i.h
index 78f1467..2759e37 100644
--- a/lib/fuse_i.h
+++ b/lib/fuse_i.h
@@ -58,6 +58,67 @@ struct fuse_notify_req {
       struct fuse_notify_req *prev;
 };
+struct fuse_config {
+       unsigned int uid;
+       unsigned int gid;
+       unsigned int  umask;
+       double entry_timeout;
+       double negative_timeout;
+       double attr_timeout;
+       double ac_attr_timeout;
+       int ac_attr_timeout_set;
+       int remember;
+       int nopath;
+       int debug;
+       int hard_remove;
+       int use_ino;
+       int readdir_ino;
+       int set_mode;
+       int set_uid;
+       int set_gid;
+       int direct_io;
+       int kernel_cache;
+       int auto_cache;
+       int intr;
+       int intr_signal;
+       int help;
+       char *modules;
+};
+
+struct list_head {
+       struct list_head *next;
+       struct list_head *prev;
+};
+
+struct node_table {
+       struct node **array;
+       size_t use;
+       size_t size;
+       size_t split;
+};
+
+struct fuse {
+       struct fuse_session *se;
+       struct node_table name_table;
+       struct node_table id_table;
+       struct list_head lru_table;
+       fuse_ino_t ctr;
+       unsigned int generation;
+       unsigned int hidectr;
+       pthread_mutex_t lock;
+       struct fuse_config conf;
+       int intr_installed;
+       struct fuse_fs *fs;
+       int nullpath_ok;
+       int curr_ticket;
+       struct lock_queue_element *lockq;
+       int pagesize;
+       struct list_head partial_slabs;
+       struct list_head full_slabs;
+       pthread_t prune_thread;
+       pthread_t main_thread;
+};
+
 struct fuse_ll {
       int debug;
       int allow_root;
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 95bc7d9..5314730 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -196,6 +196,7 @@ FUSE_2.9 {
               fuse_clean_cache;
               fuse_reply_mmap;
               fuse_lowlevel_notify_delete;
+               fuse_terminate;
               *;
diff --git a/lib/helper.c b/lib/helper.c
index ace19dd..e34491c 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -301,6 +301,8 @@ struct fuse *fuse_setup_common(int argc, char *argv[],
       if (fd)
               *fd = fuse_chan_fd(ch);
+       fuse->main_thread = pthread_self();
+
       return fuse;
--
--
Andrew Tomazos <***@tomazos.com> <http://www.tomazos.com>
Miklos Szeredi
2012-01-23 16:38:47 UTC
Permalink
Post by Andrew Tomazos
Miklos,
I took a look at your patch. I find that architecturally the cleanest
way to terminate off a blocking queue is to add a unique shutdown
message as a queue item, rather than having a separate flag and trying
to interrupt the block. In this case this would mean replacing the
exit flag, with some sort of shutdown request that gets injected into
the /dev/fuse handle incoming requests. Perhaps it can be written to
/dev/fuse, go through the kernel module (with the kernel module taking
some action on it as well, perhaps refusing any more requests for
example) and then be read off /dev/fuse and come through the same as
any other request, the only difference being is that after it is
processed it causes the event loop to exit.
Well, there's an interface for aboring the filesystem, which is I think
sort of what you are describing:

echo 1 > /sys/fs/fuse/connections/$DEV/abort

where $DEV is the device number of the fuse filesystem.

Thanks,
Miklos
Miklos Szeredi
2012-01-23 16:31:54 UTC
Permalink
Post by Andrew Tomazos
Hey all,
Normally if a signal handler is executed while a system call (such as
read(2)) is blocking, the system call returns immediately (after the
signal handler is completed executing) with an EINTR. This is clearly
the behaviour that fuse_session_loop and fuse_session_exit were
designed for.
However if the signal handler is installed with the SA_RESTART flag
set (see sigaction(2)) the system call will not return with EINTR
after the signal handler has executed. The system call will resume
blocking instead.
For some reason on my system (Ubuntu 11.10 x86_64) the default
behaviour of signal(2) is to install the signal handler with the
SA_RESTART flag.
Why do you need to install your own signal handler?

As I've said, the cleanest way to do this is to use the default signal
handlers provided by fuse and do your own cleanup after fuse_loop/_mt()
or fuse_main(), whichever you are using.

Thanks,
Miklos
Andrew Tomazos
2012-01-24 00:37:37 UTC
Permalink
Post by Miklos Szeredi
Why do you need to install your own signal handler?
Because the signal handlers are already used by the rest of the
application. I have to take many more actions than just shutting down
the fuse event loop thread on receipt of certain signals.

Essentially my interface to the fuse library is wrapped as follows:

class FuseSystem
{
public:
// mount a filesystem at path mountpoint using the ops object
to handle the operations:
FuseSystem(const string& mountpoint, IFuseOperations& ops, bool
debug = false);

// loop until exit called
void loop();

// exit loop
void exit();

// unmount
~FuseSystem();
private:
...
};

FuseSystem::exit() should be able to be called from anywhere (another
thread or a signal handler) and it should shutdown loop cleanly and
immediately.

The object should be multi-instance capable, ie you should be able to
have multiple different FuseSystem instances (that access either the
same or different IFuseOperations instances) in different threads in
the same process. (Even better would be the option to have a shared
event loop and use non-blocking io such as select/poll to read the
/dev/fuse handles).

If you're curious here is IFuseOperations:

class IFuseOperations
{
public:
virtual void init(fuse_conn_info& conn) = 0;
virtual void destroy() = 0;

virtual fuse_entry_param mknod(fuse_ino_t parent, const string& name,
uid_t user, gid_t group, mode_t mode, dev_t rdev) = 0;
virtual void forget(fuse_ino_t ino, unsigned long nlookup) = 0;
virtual struct statvfs statfs() = 0;

virtual fuse_entry_param mkdir(fuse_ino_t parent, const string& name,
uid_t user, gid_t group, mode_t mode) = 0;
virtual void unlink(fuse_ino_t parent, const string& name) = 0;
virtual fuse_entry_param lookup(fuse_ino_t parent, const string& name) = 0;
virtual void rmdir(fuse_ino_t parent, const string& name) = 0;
virtual void rename(fuse_ino_t parent, const string& name, fuse_ino_t
newparent, const string& newname) = 0;
virtual fuse_entry_param link( fuse_ino_t newparent, const string&
newname, fuse_ino_t ino) = 0;
virtual fuse_entry_param symlink(fuse_ino_t parent, const string&
name, uid_t user, gid_t group, const string& link) = 0;

virtual void opendir(fuse_ino_t ino, fuse_file_info& fi) = 0;
virtual PBuffer readdir(fuse_ino_t ino, const fuse_file_info& fi,
off_t off, size_t size) = 0;
virtual void fsyncdir(fuse_ino_t ino, const fuse_file_info& fi, bool
datasync) = 0;
virtual void releasedir(fuse_ino_t ino, const fuse_file_info& fi) = 0;

virtual pair<struct stat, double> getattr(fuse_ino_t ino) = 0;
virtual pair<struct stat, double> setattr(fuse_ino_t ino, const
fuse_file_info& fi, int to_set, const struct stat& attr) = 0;

virtual void open(fuse_ino_t ino, fuse_file_info& fi) = 0;
virtual PBuffer read(fuse_ino_t ino, const fuse_file_info& fi, off_t
off, size_t size) = 0;
virtual size_t write(fuse_ino_t ino, const fuse_file_info& fi, off_t
off, Rom buf) = 0;
virtual void flush(fuse_ino_t ino, const fuse_file_info& fi) = 0;
virtual void release(fuse_ino_t ino, const fuse_file_info& fi) = 0;
virtual void fsync(fuse_ino_t ino, const fuse_file_info& fi, bool
datasync) = 0;

virtual string readlink(fuse_ino_t ino) = 0;

virtual ~IFuseOperations() {}
};

So the user just subclasses this, implements these methods and passes
an instance to the FuseSystem constructor.

Here is FuseSystem implementation:

// (C) 2011, Andrew Tomazos <***@tomazos.com>. All Rights Reserved.

#include "Standard.pch"

#include "Fuse/FuseSystem.h"

#include "OperatingSystem/SystemError.h"
#include "OperatingSystem/StackTrace.h"
#include "OperatingSystem/Log.h"

#include "Fuse/FuseOrigin.h"

class FuseRequest : public FuseOrigin
{
public:
IFuseOperations& operations()
{
return ((FuseSystem*) fuse_req_userdata(m))->operations();
}

template<class F, class ... A>
void reply(F f, A&&... a)
{
int res = f(m, forward<A>(a)...);
if (res != 0)
{
Error("___ returned ___", FunctionString((void*)f), res);
}
}

// TODO: check errors
void err(int err_) { reply(fuse_reply_err, err_); }
void none() { fuse_reply_none(m); }
void entry(const fuse_entry_param& e) { reply(fuse_reply_entry, &e); }
void create(const fuse_entry_param& e, const fuse_file_info& fi) {
reply(fuse_reply_create, &e, &fi); }
void attr(const pair<struct stat, double>& attr) {
reply(fuse_reply_attr, &attr.first, attr.second); }
void readlink(const string& link) { reply(fuse_reply_readlink, link.c_str()); }
void open(const fuse_file_info& fi) { reply(fuse_reply_open, &fi); }
void write(size_t count) { reply(fuse_reply_write, count); }
void buf(const PBuffer& buf) { reply(fuse_reply_buf, buf->cbegin(),
buf->size()); }
void statfs(const struct statvfs& stbuf) { reply(fuse_reply_statfs, &stbuf); }

private:
FuseRequest(fuse_req_t req) : FuseOrigin(req) {}

friend class FuseSystemOperationsType;
};

struct FuseSystemOperationsType : public fuse_lowlevel_ops
{
static void cb_init(void *userdata, fuse_conn_info* conn) {
((FuseSystem*) userdata)->operations().init(*conn); }

static void cb_destroy(void *userdata) { ((FuseSystem*)
userdata)->operations().destroy(); }

static void cb_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
{
FuseRequest freq(req);
try { freq.entry(freq.operations().lookup(parent, name)); }
catch (int e) { freq.err(e); }
}

static void cb_forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup)
{
FuseRequest freq(req);
freq.operations().forget(ino, nlookup);
freq.none();
}

static void cb_getattr(fuse_req_t req, fuse_ino_t ino, fuse_file_info*)
{
FuseRequest freq(req);
try { freq.attr(freq.operations().getattr(ino)); }
catch (int e) { freq.err(e); }
}

// TODO Add non-int exceptions

static void cb_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr,
int to_set, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.attr(freq.operations().setattr(ino, *fi, to_set, *attr)); }
catch (int e) { freq.err(e); }
}

static void cb_readlink(fuse_req_t req, fuse_ino_t ino)
{
FuseRequest freq(req);
try { freq.readlink(freq.operations().readlink(ino)); }
catch (int e) { freq.err(e); }
}

static void cb_mknod(fuse_req_t req, fuse_ino_t parent, const char* name,
mode_t mode, dev_t rdev)
{
FuseRequest freq(req);
try { freq.entry(freq.operations().mknod(parent, name, freq.user(),
freq.group(), mode, rdev)); }
catch (int e) { freq.err(e); }
}

static void cb_mkdir(fuse_req_t req, fuse_ino_t parent, const char*
name, mode_t mode)
{
FuseRequest freq(req);
try { freq.entry(freq.operations().mkdir(parent, name, freq.user(),
freq.group(), mode)); }
catch (int e) { freq.err(e); }
}

static void cb_unlink(fuse_req_t req, fuse_ino_t parent, const char* name)
{
FuseRequest freq(req);
try { freq.operations().unlink(parent, name); freq.err(0); }
catch (int e) { freq.err(e); }
}

static void cb_rmdir(fuse_req_t req, fuse_ino_t parent, const char* name)
{
FuseRequest freq(req);
try { freq.operations().rmdir(parent, name); freq.err(0); }
catch (int e) { freq.err(e); }
}

static void cb_symlink(fuse_req_t req, const char* link, fuse_ino_t parent,
const char* name)
{
FuseRequest freq(req);
try { freq.entry(freq.operations().symlink(parent, name,
freq.user(), freq.group(), link)); }
catch (int e) { freq.err(e); }
}

static void cb_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
fuse_ino_t newparent, const char* newname)
{
FuseRequest freq(req);
try { freq.operations().rename(parent, name, newparent, newname);
freq.err(0); }
catch (int e) { freq.err(e); }
}

static void cb_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
const char* newname)
{
Log("cb_link");
FuseRequest freq(req);
try { freq.entry(freq.operations().link(newparent, newname, ino)); }
catch (int e) { freq.err(e); }
}

static void cb_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.operations().open(ino, *fi); freq.open(*fi); }
catch (int e) { freq.err(e); }
}

static void cb_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.buf(freq.operations().read(ino, *fi, off, size)); }
catch (int e) { freq.err(e); }
}

static void cb_write(fuse_req_t req, fuse_ino_t ino, const char* buf,
size_t size, off_t off, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.write(freq.operations().write(ino, *fi, off, Rom(buf, size))); }
catch (int e) { freq.err(e); }
}

static void cb_flush(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.operations().flush(ino, *fi); freq.err(0); }
catch (int e) { freq.err(e); }
}

static void cb_release(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.operations().release(ino, *fi); freq.err(0); }
catch (int e) { freq.err(e); }
}

static void cb_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.operations().fsync(ino, *fi, datasync); freq.err(0); }
catch (int e) { freq.err(e); }
}

static void cb_opendir(fuse_req_t req, fuse_ino_t ino, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.operations().opendir(ino, *fi); freq.open(*fi); }
catch (int e) { freq.err(e); }
}

static void cb_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t off, fuse_file_info* fi)
{
FuseRequest freq(req);
try { freq.buf(freq.operations().readdir(ino, *fi, off, size)); }
catch (int e) { freq.err(e); }
}

static void cb_releasedir(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
FuseRequest freq(req);
try { freq.operations().releasedir(ino, *fi); freq.err(0); }
catch (int e) { freq.err(e); }
}

static void cb_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
FuseRequest freq(req);
try { freq.operations().fsyncdir(ino, *fi, datasync); freq.err(0); }
catch (int e) { freq.err(e); }
}

static void cb_statfs(fuse_req_t req, fuse_ino_t)
{
FuseRequest freq(req);
try { freq.statfs(freq.operations().statfs()); }
catch (int e) { freq.err(e); }
}

FuseSystemOperationsType();
};

FuseSystemOperationsType FuseSystemOperations;

FuseSystemOperationsType::FuseSystemOperationsType()
{
this->init = cb_init;
this->destroy = cb_destroy;
this->lookup = cb_lookup;
this->forget = cb_forget;
this->getattr = cb_getattr;
this->setattr = cb_setattr;
this->readlink = cb_readlink;
this->mknod = cb_mknod;
this->mkdir = cb_mkdir;
this->unlink = cb_unlink;
this->rmdir = cb_rmdir;
this->symlink = cb_symlink;
this->rename = cb_rename;
this->link = cb_link;
this->open = cb_open;
this->read = cb_read;
this->write = cb_write;
this->flush = cb_flush;
this->release = cb_release;
this->fsync = cb_fsync;
this->opendir = cb_opendir;
this->readdir = cb_readdir;
this->releasedir = cb_releasedir;
this->fsyncdir = cb_fsyncdir;
this->statfs = cb_statfs;
this->setxattr = nullptr;
this->getxattr = nullptr;
this->listxattr = nullptr;
this->removexattr = nullptr;
this->access = nullptr;
this->create = nullptr;
this->getlk = nullptr;
this->setlk = nullptr;
this->bmap = nullptr;
this->ioctl = nullptr;
this->poll = nullptr;
}
;

FuseSystem::FuseSystem(const string& mountpoint, IFuseOperations& ops,
bool debug) :
m_mountpoint(mountpoint),
m_channel(0),
m_session(0),
m_ops(ops),
m_args(FUSE_ARGS_INIT(0, NULL))
{
fuse_opt_add_arg(&m_args, m_mountpoint.c_str());

if (debug)
fuse_opt_add_arg(&m_args, "-d");

fuse_opt_add_arg(&m_args, "-o");
fuse_opt_add_arg(&m_args, "default_permissions");

if (fuse_parse_cmdline(&m_args, NULL, NULL, NULL))
throw runtime_error("fuse_parse_cmdline");

m_channel = fuse_mount(m_mountpoint.c_str(), &m_args);

if (m_channel == nullptr)
throw SystemError("fuse_mount(___)", m_mountpoint);

m_session = fuse_lowlevel_new(&m_args, &FuseSystemOperations,
sizeof(fuse_lowlevel_ops), this);

if (m_session == nullptr)
throw SystemError("fuse_lowlevel_new(___)", mountpoint);

fuse_session_add_chan(m_session, m_channel);

}

void FuseSystem::loop()
{
auto err = fuse_session_loop(m_session);
if (err)
throw runtime_error(Format("fuse_session_loop returned ", err));
}

void FuseSystem::exit()
{
fuse_session_exit(m_session);
}

FuseSystem::~FuseSystem()
{
if (m_session != nullptr && m_channel != nullptr)
fuse_session_remove_chan(m_channel);

if (m_session != nullptr)
fuse_session_destroy(m_session);

if (m_channel != nullptr)
fuse_unmount(m_mountpoint.c_str(), m_channel);

fuse_opt_free_args(&m_args);
}

Enjoy,
Andrew.
Mike Shal
2012-01-25 04:13:30 UTC
Permalink
Post by Miklos Szeredi
Post by Andrew Tomazos
Hey all,
Normally if a signal handler is executed while a system call (such as
read(2)) is blocking, the system call returns immediately (after the
signal handler is completed executing) with an EINTR. This is clearly
the behaviour that fuse_session_loop and fuse_session_exit were
designed for.
However if the signal handler is installed with the SA_RESTART flag
set (see sigaction(2)) the system call will not return with EINTR
after the signal handler has executed. The system call will resume
blocking instead.
For some reason on my system (Ubuntu 11.10 x86_64) the default
behaviour of signal(2) is to install the signal handler with the
SA_RESTART flag.
Why do you need to install your own signal handler?
As I've said, the cleanest way to do this is to use the default signal
handlers provided by fuse and do your own cleanup after fuse_loop/_mt()
or fuse_main(), whichever you are using.
Thanks,
Miklos
I was able to use the fuse signal handlers along with the latest
fuse_terminate() patch and it seems to work. However, I did have to
add some extra synchronization between my fuse thread and my main
thread. Essentially my fuse thread looks like:

fuse_thread:
fuse_loop()
kill(0, SIGINT) // propagate SIGINT in case the main process was
killed with SIGINT directly (which doesn't go to the process group)
get fuse lock
fuse_remove_signal_handlers()
fuse_unmount()
fuse_destroy()
fuse structure = NULL
release fuse lock

I need to call unmount in here because my main thread waits for all
the worker threads/processes that use the filesystem to return before
calling fuse_terminate(). If I send a signal, one of the threads may
get stuck trying to use the fuse fs, but since I've already broken out
of the loop the request will hang unless I unmount.

My main thread then does this:

main_thread:
wait for all worker threads/processes
get fuse lock
if(fuse structure)
fuse_terminate()
release fuse lock
join fuse thread

This allows me to cleanup if fuse catches a signal (in which case the
fuse structure is already NULL and doesn't need to terminate), or if
it exits cleanly I call terminate and then the fuse thread shuts down.

It was a bit tricky to get my head wrapped around but it seems to work
as designed now :)

Thanks,
-Mike

Continue reading on narkive:
Loading...