Index: main/manager.c =================================================================== --- main/manager.c (revision 262152) +++ main/manager.c (working copy) @@ -113,11 +113,12 @@ int usecount; /*!< # of clients who still need the event */ int category; unsigned int seq; /*!< sequence number */ - AST_LIST_ENTRY(eventqent) eq_next; + struct timeval tv; /*!< When event was allocated */ + AST_RWLIST_ENTRY(eventqent) eq_next; char eventdata[1]; /*!< really variable size, allocated by append_event() */ }; -static AST_LIST_HEAD_STATIC(all_events, eventqent); +static AST_RWLIST_HEAD_STATIC(all_events, eventqent); static int displayconnects = 1; static int allowmultiplelogin = 1; @@ -222,8 +223,6 @@ int fd; }; -#define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next)) - static AST_LIST_HEAD_STATIC(sessions, mansession_session); /*! \brief user descriptor, as read from the config file. @@ -332,14 +331,15 @@ { struct eventqent *ret; - AST_LIST_LOCK(&all_events); - ret = AST_LIST_LAST(&all_events); + AST_RWLIST_RDLOCK(&all_events); + ret = AST_RWLIST_LAST(&all_events); /* the list is never empty now, but may become so when * we optimize it in the future, so be prepared. */ - if (ret) + if (ret) { ast_atomic_fetchadd_int(&ret->usecount, 1); - AST_LIST_UNLOCK(&all_events); + } + AST_RWLIST_UNLOCK(&all_events); return ret; } @@ -350,14 +350,24 @@ static void purge_events(void) { struct eventqent *ev; + struct timeval now = ast_tvnow(); - AST_LIST_LOCK(&all_events); - while ( (ev = AST_LIST_FIRST(&all_events)) && - ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) { - AST_LIST_REMOVE_HEAD(&all_events, eq_next); + AST_RWLIST_WRLOCK(&all_events); + while ( (ev = AST_RWLIST_FIRST(&all_events)) && + ev->usecount == 0 && AST_RWLIST_NEXT(ev, eq_next)) { + AST_RWLIST_REMOVE_HEAD(&all_events, eq_next); ast_free(ev); } - AST_LIST_UNLOCK(&all_events); + + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&all_events, ev, eq_next) { + /* 2.5 times whatever the HTTP timeout is (maximum 2.5 hours) is the maximum time that we will definitely cache an event */ + if (ev->usecount == 0 && ast_tvdiff_sec(now, ev->tv) > (httptimeout > 3600 ? 3600 : httptimeout) * 2.5) { + AST_RWLIST_REMOVE_CURRENT(eq_next); + ast_free(ev); + } + } + AST_RWLIST_TRAVERSE_SAFE_END; + AST_RWLIST_UNLOCK(&all_events); } /*! @@ -770,13 +780,13 @@ case CLI_GENERATE: return NULL; } - AST_LIST_LOCK(&all_events); - AST_LIST_TRAVERSE(&all_events, s, eq_next) { + AST_RWLIST_RDLOCK(&all_events); + AST_RWLIST_TRAVERSE(&all_events, s, eq_next) { ast_cli(a->fd, "Usecount: %d\n", s->usecount); ast_cli(a->fd, "Category: %d\n", s->category); ast_cli(a->fd, "Event:\n%s", s->eventdata); } - AST_LIST_UNLOCK(&all_events); + AST_RWLIST_UNLOCK(&all_events); return CLI_SUCCESS; } @@ -812,21 +822,17 @@ AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"), }; -/* - * Decrement the usecount for the event; if it goes to zero, - * (why check for e->next ?) wakeup the - * main thread, which is in charge of freeing the record. - * Returns the next record. - */ -static struct eventqent *unref_event(struct eventqent *e) +static struct eventqent *advance_event(struct eventqent *e) { - ast_atomic_fetchadd_int(&e->usecount, -1); - return AST_LIST_NEXT(e, eq_next); -} + struct eventqent *next; -static void ref_event(struct eventqent *e) -{ - ast_atomic_fetchadd_int(&e->usecount, 1); + AST_RWLIST_RDLOCK(&all_events); + if ((next = AST_RWLIST_NEXT(e, eq_next))) { + ast_atomic_fetchadd_int(&next->usecount, 1); + ast_atomic_fetchadd_int(&e->usecount, -1); + } + AST_RWLIST_UNLOCK(&all_events); + return next; } /* @@ -847,7 +853,7 @@ fclose(session->f); ast_mutex_destroy(&session->__lock); ast_free(session); - unref_event(eqe); + ast_atomic_fetchadd_int(&eqe->usecount, -1); } static void destroy_session(struct mansession_session *session) @@ -1638,8 +1644,9 @@ for (x = 0; x < timeout || timeout < 0; x++) { ast_mutex_lock(&s->session->__lock); - if (NEW_EVENT(s)) + if (AST_RWLIST_NEXT(s->session->last_ev, eq_next)) { needexit = 1; + } /* We can have multiple HTTP session point to the same mansession entry. * The way we deal with it is not very nice: newcomers kick out the previous * HTTP session. XXX this needs to be improved. @@ -1661,15 +1668,14 @@ ast_debug(1, "Finished waiting for an event!\n"); ast_mutex_lock(&s->session->__lock); if (s->session->waiting_thread == pthread_self()) { - struct eventqent *eqe; + struct eventqent *eqe = s->session->last_ev; astman_send_response(s, m, "Success", "Waiting for Event completed."); - while ( (eqe = NEW_EVENT(s)) ) { - ref_event(eqe); + while ((eqe = advance_event(eqe))) { if (((s->session->readperm & eqe->category) == eqe->category) && ((s->session->send_events & eqe->category) == eqe->category)) { astman_append(s, "%s", eqe->eventdata); } - s->session->last_ev = unref_event(s->session->last_ev); + s->session->last_ev = eqe; } astman_append(s, "Event: WaitEventComplete\r\n" @@ -2679,17 +2685,16 @@ ast_mutex_lock(&s->session->__lock); if (s->session->f != NULL) { - struct eventqent *eqe; + struct eventqent *eqe = s->session->last_ev; - while ( (eqe = NEW_EVENT(s)) ) { - ref_event(eqe); + while ((eqe = advance_event(eqe))) { if (!ret && s->session->authenticated && (s->session->readperm & eqe->category) == eqe->category && (s->session->send_events & eqe->category) == eqe->category) { if (send_string(s, eqe->eventdata) < 0) ret = -1; /* don't send more */ } - s->session->last_ev = unref_event(s->session->last_ev); + s->session->last_ev = eqe; } } ast_mutex_unlock(&s->session->__lock); @@ -3304,12 +3309,13 @@ tmp->usecount = 0; tmp->category = category; tmp->seq = ast_atomic_fetchadd_int(&seq, 1); - AST_LIST_NEXT(tmp, eq_next) = NULL; + tmp->tv = ast_tvnow(); + AST_RWLIST_NEXT(tmp, eq_next) = NULL; strcpy(tmp->eventdata, str); - AST_LIST_LOCK(&all_events); - AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next); - AST_LIST_UNLOCK(&all_events); + AST_RWLIST_WRLOCK(&all_events); + AST_RWLIST_INSERT_TAIL(&all_events, tmp, eq_next); + AST_RWLIST_UNLOCK(&all_events); return 0; }