diff --git a/include/haproxy/queue.h b/include/haproxy/queue.h index e77370cdd..e4201fb17 100644 --- a/include/haproxy/queue.h +++ b/include/haproxy/queue.h @@ -39,6 +39,7 @@ unsigned int srv_dynamic_maxconn(const struct server *s); int pendconn_redistribute(struct server *s); int pendconn_grab_from_px(struct server *s); void pendconn_unlink(struct pendconn *p); +int pendconn_must_try_again(struct pendconn *p); /* Removes the pendconn from the server/proxy queue. It supports being called * with NULL for pendconn and with a pendconn not in the list. It is the diff --git a/src/queue.c b/src/queue.c index e55bb5898..bf32d4239 100644 --- a/src/queue.c +++ b/src/queue.c @@ -621,6 +621,68 @@ int pendconn_dequeue(struct stream *strm) return 0; } +/* checks after a successful pendconn_add() if the connection ended up being + * alone with no active connection left to dequeue it. In such a case it will + * simply remove it from the queue, free it and return non-zero to inform the + * caller that it must try to add the connection again, otherwise it returns + * zero, indicating that the connection will be handled normally. The caller + * might have to drop SF_DIRECT and/or SF_ASSIGNED if the conn was on a proxy. + */ +int pendconn_must_try_again(struct pendconn *p) +{ + struct queue *q = p->queue; + struct proxy *px = q->px; + struct server *sv = q->sv; + int ret = 0; + + if (likely(!HA_ATOMIC_LOAD(&p->node.node.leaf_p))) + goto leave; + + /* for a server, we need at least one conn left on this server to + * find ours. + */ + if (likely(sv && HA_ATOMIC_LOAD(&sv->served))) + goto leave; + + /* for a backend, we need at least one conn left on any of this + * backend's servers to find ours. + */ + if (likely(!sv && HA_ATOMIC_LOAD(&px->served))) + goto leave; + + /* OK the situation is not safe anymore, we need to check if we're + * still in the queue under a lock. + */ + HA_SPIN_LOCK(QUEUE_LOCK, &q->lock); + HA_SPIN_LOCK(QUEUE_LOCK, &p->del_lock); + + if (p->node.node.leaf_p) { + eb32_delete(&p->node); + _HA_ATOMIC_DEC(&q->length); + _HA_ATOMIC_INC(&q->idx); + _HA_ATOMIC_DEC(&px->totpend); + ret = 1; + } + + HA_SPIN_UNLOCK(QUEUE_LOCK, &p->del_lock); + HA_SPIN_UNLOCK(QUEUE_LOCK, &q->lock); + + /* check if the connection was still queued. If not, it means its + * processing has begun so it's safe. + */ + if (!ret) + goto leave; + + /* The pendconn is not queued anymore and will not be so we're safe + * to free it. + */ + p->strm->pend_pos = NULL; + pool_free(pool_head_pendconn, p); + +leave: + return ret; +} + static enum act_return action_set_priority_class(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *s, int flags) {