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;
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.
{
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;
}
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);
}
/*!
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;
}
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;
}
/*
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)
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.
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"
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);
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;
}