mirror of
http://git.haproxy.org/git/haproxy.git
synced 2026-02-13 04:59:13 +02:00
BUG/MINOR: event_hdl: make event_hdl_subscribe thread-safe
List insertion in event_hdl_subscribe() was not thread-safe when dealing
with unique identifiers. Indeed, in this case the list insertion is
conditional (we check for a duplicate, then we insert). And while we're
using mt lists for this, the whole operation is not atomic: there is a
race between the check and the insertion.
This could lead to the same ID being registered multiple times with
concurrent calls to event_hdl_subscribe() on the same ID.
To fix this, we add 'insert_lock' dedicated lock in the subscription
list struct. The lock's cost is nearly 0 since it is only used when
registering identified subscriptions and the lock window is very short:
we only guard the duplicate check and the list insertion to make the
conditional insertion "atomic" within a given subscription list.
This is the only place where we need the lock: as soon as the item is
properly inserted we're out of trouble because all other operations on
the list are already thread-safe thanks to mt lists.
A new lock hint is introduced: LOCK_EHDL which is dedicated to event_hdl
The patch may seem quite large since we had to rework the logic around
the subscribe function and switch from simple mt_list to a dedicated
struct wrapping both the mt_list and the insert_lock for the
event_hdl_sub_list type.
(sizeof(event_hdl_sub_list) is now 24 instead of 16)
However, all the changes are internal: we don't break the API.
If 68e692da0 ("MINOR: event_hdl: add event handler base api")
is being backported, then this commit should be backported with it.
This commit is contained in:
committed by
Christopher Faulet
parent
53eb6aecce
commit
d514ca45c6
@@ -24,7 +24,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <haproxy/list-t.h>
|
||||
#include <haproxy/api-t.h>
|
||||
#include <haproxy/thread-t.h>
|
||||
|
||||
/* event data struct are defined as followed */
|
||||
struct event_hdl_cb_data_template {
|
||||
@@ -72,8 +73,13 @@ struct event_hdl_sub_type
|
||||
uint16_t subtype;
|
||||
};
|
||||
|
||||
/* event_hdl_sub_list is an alias to mt_list (please use this for portability) */
|
||||
typedef struct mt_list event_hdl_sub_list;
|
||||
struct event_hdl_sub_list_head {
|
||||
struct mt_list head;
|
||||
__decl_thread(HA_SPINLOCK_T insert_lock);
|
||||
};
|
||||
|
||||
/* event_hdl_sub_list is an alias (please use this for portability) */
|
||||
typedef struct event_hdl_sub_list_head event_hdl_sub_list;
|
||||
/* event_hdl_async_equeue is an alias to mt_list (please use this for portability) */
|
||||
typedef struct mt_list event_hdl_async_equeue;
|
||||
|
||||
|
||||
@@ -221,6 +221,14 @@ uint64_t event_hdl_id(const char *scope, const char *name);
|
||||
* If <sub_list> is not specified (equals NULL):
|
||||
* global subscription list (process wide) will be used.
|
||||
*
|
||||
* For identified subscriptions (EVENT_HDL_ID_*), the function is safe against
|
||||
* concurrent subscriptions attempts with the same ID: the ID will only be
|
||||
* inserted once in the list and subsequent attempts will yield an error.
|
||||
* However, trying to register the same ID multiple times is considered as
|
||||
* an error (no specific error code is returned in this case) so the check should
|
||||
* be performed by the caller if it is expected. (The caller must ensure that the ID
|
||||
* is unique to prevent the error from being raised)
|
||||
*
|
||||
* Returns 1 in case of success, 0 in case of failure (invalid argument / memory error)
|
||||
*/
|
||||
int event_hdl_subscribe(event_hdl_sub_list *sub_list,
|
||||
@@ -422,7 +430,8 @@ static inline struct event_hdl_async_event *event_hdl_async_equeue_pop(event_hdl
|
||||
*/
|
||||
static inline void event_hdl_sub_list_init(event_hdl_sub_list *sub_list)
|
||||
{
|
||||
MT_LIST_INIT(sub_list);
|
||||
MT_LIST_INIT(&sub_list->head);
|
||||
HA_SPIN_INIT(&sub_list->insert_lock);
|
||||
}
|
||||
|
||||
/* use this function when you need to destroy <sub_list>
|
||||
|
||||
@@ -425,6 +425,7 @@ enum lock_label {
|
||||
IDLE_CONNS_LOCK,
|
||||
QUIC_LOCK,
|
||||
OCSP_LOCK,
|
||||
EHDL_LOCK,
|
||||
OTHER_LOCK,
|
||||
/* WT: make sure never to use these ones outside of development,
|
||||
* we need them for lock profiling!
|
||||
|
||||
Reference in New Issue
Block a user