uloop: fix corner cases with recursive uloop_run calls

With multiple recursive calls to uloop_run, the callback for the same fd
can be run multiple times from different levels in the stack.
Prevent this by tracking the stack of uloop_fd callbacks and buffering new
incoming events for fds already on the stack.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
This commit is contained in:
Felix Fietkau 2013-06-18 12:01:08 +02:00
parent 35cee2c206
commit b9ebdbcc64
2 changed files with 58 additions and 2 deletions

53
uloop.c
View file

@ -43,6 +43,14 @@ struct uloop_fd_event {
unsigned int events; unsigned int events;
}; };
struct uloop_fd_stack {
struct uloop_fd_stack *next;
struct uloop_fd *fd;
unsigned int events;
};
static struct uloop_fd_stack *fd_stack = NULL;
#define ULOOP_MAX_EVENTS 10 #define ULOOP_MAX_EVENTS 10
static struct list_head timeouts = LIST_HEAD_INIT(timeouts); static struct list_head timeouts = LIST_HEAD_INIT(timeouts);
@ -285,6 +293,32 @@ static int uloop_fetch_events(int timeout)
#endif #endif
static bool uloop_fd_stack_event(struct uloop_fd *fd, int events)
{
struct uloop_fd_stack *cur;
/*
* Do not buffer events for level-triggered fds, they will keep firing.
* Caller needs to take care of recursion issues.
*/
if (!(fd->flags & ULOOP_EDGE_TRIGGER))
return false;
for (cur = fd_stack; cur; cur = cur->next) {
if (cur->fd != fd)
continue;
if (events < 0)
cur->fd = NULL;
else
cur->events |= events | ULOOP_EVENT_BUFFERED;
return true;
}
return false;
}
static void uloop_run_events(int timeout) static void uloop_run_events(int timeout)
{ {
struct uloop_fd_event *cur; struct uloop_fd_event *cur;
@ -298,17 +332,33 @@ static void uloop_run_events(int timeout)
} }
while (cur_nfds > 0) { while (cur_nfds > 0) {
struct uloop_fd_stack stack_cur;
unsigned int events;
cur = &cur_fds[cur_fd++]; cur = &cur_fds[cur_fd++];
cur_nfds--; cur_nfds--;
fd = cur->fd; fd = cur->fd;
events = cur->events;
if (!fd) if (!fd)
continue; continue;
if (!fd->cb) if (!fd->cb)
continue; continue;
fd->cb(fd, cur->events); if (uloop_fd_stack_event(fd, cur->events))
continue;
stack_cur.next = fd_stack;
stack_cur.fd = fd;
fd_stack = &stack_cur;
do {
stack_cur.events = 0;
fd->cb(fd, events);
events = stack_cur.events & ULOOP_EVENT_MASK;
} while (stack_cur.fd && events);
fd_stack = stack_cur.next;
return; return;
} }
} }
@ -352,6 +402,7 @@ int uloop_fd_delete(struct uloop_fd *fd)
cur_fds[cur_fd + i].fd = NULL; cur_fds[cur_fd + i].fd = NULL;
} }
fd->registered = false; fd->registered = false;
uloop_fd_stack_event(fd, -1);
return __uloop_fd_delete(fd); return __uloop_fd_delete(fd);
} }

View file

@ -44,8 +44,13 @@ typedef void (*uloop_process_handler)(struct uloop_process *c, int ret);
#define ULOOP_WRITE (1 << 1) #define ULOOP_WRITE (1 << 1)
#define ULOOP_EDGE_TRIGGER (1 << 2) #define ULOOP_EDGE_TRIGGER (1 << 2)
#define ULOOP_BLOCKING (1 << 3) #define ULOOP_BLOCKING (1 << 3)
#define ULOOP_EVENT_MASK (ULOOP_READ | ULOOP_WRITE)
/* internal flags */
#define ULOOP_EVENT_BUFFERED (1 << 4)
#ifdef USE_KQUEUE #ifdef USE_KQUEUE
#define ULOOP_EDGE_DEFER (1 << 4) #define ULOOP_EDGE_DEFER (1 << 5)
#endif #endif
struct uloop_fd struct uloop_fd