|
From: Andrew T. <an...@to...> - 2012-01-21 05:10:56
|
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.
On Fri, Jan 20, 2012 at 4:47 PM, Miklos Szeredi <mi...@sz...> wrote:
> Mike Shal <ma...@gm...> writes:
>
>> On Tue, Jan 17, 2012 at 7:14 PM, Andrew Tomazos <an...@to...> wrote:
>>> 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.
>
> 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:
--
Andrew Tomazos <an...@to...> <http://www.tomazos.com>
|