Re: Compiling C++ modules

Avi Kivity
2006-04-25T17:53:12Z

Dmitry Torokhov wrote:
>>>>>
>>>> No, it's optimized out. gcc notices that &lock doesn't change and that
>>>> 'l' never escapes the function.
>>>>
>>>>
>>> "l" that propects critical section gets thrown away???
>>>
>> Calm down, the storage for 'l' is thrown away, but its effects remain.
>>
>
> Would you mind explaining implemenation details a little bit?
>
(I don't know how familiar you are with C++ so I'm explaining it from
the basics, apologies if I'm repeating things you know)

Very often one needs to acquire a resource, do something with it, and
then free the resource. Here, "resource" can mean a file descriptor, a
reference into a reference counted object, or, in our case, a spinlock.
And we want "free" to mean "free no matter what", e.g. on a normal path
or an exception path.

In C++, you code it as a guard object:

struct spinlock_guard {
spinlock_guard(spinlock_t *lock) { sl = lock; spin_lock(sl); }
~spinlock_guard() { spin_unlock(sl); }

spinlock_t *sl;
};

(this would be coded differently, trying to keep it C-like)

To use it, create a spinlock_guard object that goes into scope before
you use the data you want to protect:

spinlock_t some_lock.;

void f()
{
blah();
spinlock_guard guard(&some_lock);
__f(); /* do nonatomic stuff */
if (__g())
return;
__h()
}

C++ treats this as if you've written:

spinlock_t some_lock;

void f()
{
spinlock_guard guard;

blah();
guard.sl = &some_lock;
spin_lock(guard.sl);
__f();
if (__g())
goto out;
__h();
out:
spin_unlock(guard.sl);
}

Additionally, C++ guarantees that if an exception is thrown after
spin_lock() is called, then the spin_unlock() will also be called.
That's an interesting mechanism by itself.

Now, the optimizer sees that guard.sl is a constant expression
(&some_lock), and that guard.sl's address is not passed to any function,
so it eliminates the variable entirely, leaving us with

spinlock_t some_lock;

void f()
{
blah();
spin_lock(&some_lock);
__f();
if (__g())
goto out;
__h();
out:
spin_unlock(&some_lock);
}

(the original C++ compiler actually worked by writing out the C code and
letting the C compiler compile it; nowadays both compilers use the same
intermediate representation, optimizer, and code generator).

--
Do not meddle in the internals of kernels, for they are subtle and quick to panic.