Re: [PATCH] tools/memory-model: document the "one-time init" pattern

From: Alan Stern
Date: Mon Jul 27 2020 - 11:17:50 EST


On Fri, Jul 17, 2020 at 10:28:18PM -0700, Eric Biggers wrote:
> I'm still not sure this is the best API.

I cast my vote for something along the following lines. It's simple,
easily understood and easily used. The approach has two variants: One
that returns an integer and one that returns a pointer. I'll use the
pointer variant to illustrate.

Given a type "T", an object x of type pointer-to-T, and a function
"func" that takes various arguments and returns a pointer-to-T, the
accepted API for calling func once would be to create once_func() as
follows:

T *once_func(T **ppt, args...)
{
static DEFINE_MUTEX(mut);
T *p;

p = smp_load_acquire(ppt); /* Mild optimization */
if (p)
return p;

mutex_lock(mut);
p = smp_load_acquire(ppt);
if (!p) {
p = func(args...);
if (!IS_ERR_OR_NULL(p))
smp_store_release(ppt, p);
}
mutex_unlock(mut);
return p;
}

Users then would have to call once_func(&x, args...) and check the
result. Different x objects would constitute different "once"
domains.

(In the integer variant, x, p and the return type of func are all int,
and ppt is an int *. Everything else is the same. This variant would
be used in cases where you're not allocating anything, you're doing
some other sort of initialization only once.)

While this would be a perfectly good recipe in itself, the whole thing
can be made much simpler for users by creating a MAKE_ONCE_FUNC macro
which would generate once_func given the type T, the name "func", and
the args. The result is type-safe.

IMO the fact that once_func() is not inline is an advantage, not a
drawback. Yes, it doesn't actually do any allocation or anything like
that -- the idea is that once_func's purpose is merely to ensure that
func is successfully called only once. Any memory allocation or other
stuff of that sort should be handled by func.

In fact, the only drawback I can think of is that because this relies
on a single mutex for all the different possible x's, it might lead to
locking conflicts (if func had to call once_func() recursively, for
example). In most reasonable situations such conflicts would not
arise.

Alan Stern