LI_LISTEN, /* started, listening but not enabled */
LI_READY, /* started, listening and enabled */
LI_FULL, /* reached its connection limit */
+ LI_LIMITED, /* transient state: limits have been reached, listener is queued */
};
/* Listener transitions
* +-----------------
* disable()
*
+ * The LIMITED state my be used when a limit has been detected just before
+ * using a listener. In this case, the listener MUST be queued into the
+ * appropriate wait queue (either the proxy's or the global one). It may be
+ * set back to the READY state at any instant and for any reason, so one must
+ * not rely on this state.
*/
/* listener socket options */
struct task * (*handler)(struct task *t); /* protocol handler. It is a task */
int *timeout; /* pointer to client-side timeout */
struct proxy *frontend; /* the frontend this listener belongs to, or NULL */
+ struct list wait_queue; /* link element to make the listener wait for something (LI_LIMITED) */
unsigned int analysers; /* bitmap of required protocol analysers */
int nice; /* nice value to assign to the instanciated tasks */
union { /* protocol-dependant access restrictions */
return;
if (listener->state == LI_READY)
EV_FD_CLR(listener->fd, DIR_RD);
+ if (listener->state == LI_LIMITED)
+ LIST_DEL(&listener->wait_queue);
listener->state = LI_LISTEN;
}
if (shutdown(l->fd, SHUT_RD) != 0)
return 0; /* should always be OK */
+ if (l->state == LI_LIMITED)
+ LIST_DEL(&l->wait_queue);
+
EV_FD_CLR(l->fd, DIR_RD);
l->state = LI_PAUSED;
return 1;
}
-/* This function tries to resume a temporarily disabled listener. Paused, full
- * and disabled listeners are handled, which means that this function may
- * replace enable_listener(). The resulting state will either be LI_READY or
- * LI_FULL. 0 is returned in case of failure to resume (eg: dead socket).
+/* This function tries to resume a temporarily disabled listener. Paused, full,
+ * limited and disabled listeners are handled, which means that this function
+ * may replace enable_listener(). The resulting state will either be LI_READY
+ * or LI_FULL. 0 is returned in case of failure to resume (eg: dead socket).
*/
int resume_listener(struct listener *l)
{
if (l->state == LI_READY)
return 1;
+ if (l->state == LI_LIMITED)
+ LIST_DEL(&l->wait_queue);
+
if (l->nbconn >= l->maxconn) {
l->state = LI_FULL;
return 1;
void listener_full(struct listener *l)
{
if (l->state >= LI_READY) {
+ if (l->state == LI_LIMITED)
+ LIST_DEL(&l->wait_queue);
+
EV_FD_CLR(l->fd, DIR_RD);
l->state = LI_FULL;
}
}
+/* Marks a ready listener as limited so that we only try to re-enable it when
+ * resources are free again. It will be queued into the specified queue.
+ */
+void limit_listener(struct listener *l, struct list *list)
+{
+ if (l->state == LI_READY) {
+ LIST_ADDQ(list, &l->wait_queue);
+ EV_FD_CLR(l->fd, DIR_RD);
+ l->state = LI_LIMITED;
+ }
+}
+
/* This function adds all of the protocol's listener's file descriptors to the
* polling lists when they are in the LI_LISTEN state. It is intended to be
* used as a protocol's generic enable_all() primitive, for use after the
return ERR_NONE;
}
+/* Dequeues all of the listeners waiting for a resource in wait queue <queue>. */
+void dequeue_all_listeners(struct list *list)
+{
+ struct listener *listener, *l_back;
+
+ list_for_each_entry_safe(listener, l_back, list, wait_queue) {
+ /* This cannot fail because the listeners are by definition in
+ * the LI_LIMITED state. The function also removes the entry
+ * from the queue.
+ */
+ resume_listener(listener);
+ }
+}
+
/* This function closes the listening socket for the specified listener,
* provided that it's already in a listening state. The listener enters the
* LI_ASSIGNED state. It always returns ERR_NONE. This function is intended
if (listener->state == LI_READY)
EV_FD_CLR(listener->fd, DIR_RD);
+ if (listener->state == LI_LIMITED)
+ LIST_DEL(&listener->wait_queue);
+
if (listener->state >= LI_PAUSED) {
fd_delete(listener->fd);
listener->state = LI_ASSIGNED;