<snip> > > >> + * Example usage: > >> + * @code{.c} > >> + * #define MAX_Y_LEN (16) > >> + * // Application-defined example data structure, protected by a seqlock. > >> + * struct config { > >> + * rte_seqlock_t lock; > >> + * int param_x; > >> + * char param_y[MAX_Y_LEN]; > >> + * }; > >> + * > >> + * // Accessor function for reading config fields. > >> + * void > >> + * config_read(const struct config *config, int *param_x, char > >> +*param_y) > >> + * { > >> + * // Temporary variables, just to improve readability. > > I think the above comment is not necessary. It is beneficial to copy the > protected data to keep the read side critical section small. > > > > The data here would be copied into the buffers supplied by config_read() > anyways, so it's a copy regardless. I see what you mean here. I would think the local variables add confusion, the copy can happen to the passed parameters directly. I will leave it to you to decide.
> > >> + * int tentative_x; > >> + * char tentative_y[MAX_Y_LEN]; > >> + * uint32_t sn; > >> + * > >> + * sn = rte_seqlock_read_lock(&config->lock); > >> + * do { > >> + * // Loads may be atomic or non-atomic, as in this > >> example. > >> + * tentative_x = config->param_x; > >> + * strcpy(tentative_y, config->param_y); > >> + * } while (!rte_seqlock_read_tryunlock(&config->lock, &sn)); > >> + * // An application could skip retrying, and try again later, if > >> + * // progress is possible without the data. > >> + * > >> + * *param_x = tentative_x; > >> + * strcpy(param_y, tentative_y); > >> + * } > >> + * > >> + * // Accessor function for writing config fields. > >> + * void > >> + * config_update(struct config *config, int param_x, const char > >> +*param_y) > >> + * { > >> + * rte_seqlock_write_lock(&config->lock); > >> + * // Stores may be atomic or non-atomic, as in this example. > >> + * config->param_x = param_x; > >> + * strcpy(config->param_y, param_y); > >> + * rte_seqlock_write_unlock(&config->lock); > >> + * } > >> + * @endcode > >> + * > >> + * @see > >> + * https://en.wikipedia.org/wiki/Seqlock. > >> + */ > >> + > >> +#include <stdbool.h> > >> +#include <stdint.h> > >> + > >> +#include <rte_atomic.h> > >> +#include <rte_branch_prediction.h> > >> +#include <rte_spinlock.h> > >> + > >> +/** > >> + * The RTE seqlock type. > >> + */ > >> +typedef struct { > >> + uint32_t sn; /**< A sequence number for the protected data. */ > >> + rte_spinlock_t lock; /**< Spinlock used to serialize writers. */ } > > Suggest using ticket lock for the writer side. It should have low overhead > when there is a single writer, but provides better functionality when there > are > multiple writers. > > > > Is a seqlock the synchronization primitive of choice for high-contention > cases? > I would say no, but I'm not sure what you would use instead. I think Stephen has come across some use cases of high contention writers with readers, maybe Stephen can provide some input. IMO, there is no harm/perf issues in using ticket lock. > > <snip>