Re: Strange Windows problem, lock_timeout test request - Mailing list pgsql-hackers

From Robert Haas
Subject Re: Strange Windows problem, lock_timeout test request
Date
Msg-id CA+TgmobkMbQVCtKNYgXzA=g+OEteAkYDacCnM9XpeX332-yV9g@mail.gmail.com
Whole thread Raw
In response to Re: Strange Windows problem, lock_timeout test request  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: Strange Windows problem, lock_timeout test request  (Greg Stark <stark@mit.edu>)
List pgsql-hackers
On Thu, Mar 21, 2013 at 8:16 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Robert Haas <robertmhaas@gmail.com> writes:
>> On Mon, Mar 18, 2013 at 10:09 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>>> Because it's wrong.  Removing "volatile" means that the compiler is
>>> permitted to optimize away stores (and fetches!) on the basis of their
>>> being unnecessary according to straight-line analysis of the code.
>>> Write barriers don't fix that, they only say that stores that the
>>> compiler chooses to issue at all have to be ordered a certain way.
>
>> I don't think this is correct.  The read and write barriers as
>> implemented are designed to function as compiler barriers also, just
>> as they do in the Linux kernel and every other piece of software I've
>> found that implements anything remotely like this, with the lone
>> exception of PostgreSQL.  In PostgreSQL, spinlock acquisition and
>> release are defined as CPU barriers but not a compiler barrier, and
>> this necessitates extensive use of volatile all over the code base
>> which would be unnecessary if we did this the way it's done in Linux
>> and elsewhere.
>
> I think you're just as mistaken as Zoltan.   Barriers enforce ordering
> of operations, not whether an operation occurs at all.

Surely not.  Suppose the user does this:

some_global_var = 1;
some_function();
some_global_var = 2;

I hope we can both agree that any compiler which thinks it doesn't
need to store 1 in some_global_var is completely nuts, because
some_function() might perform any arbitrary computation, including one
that depends on some_global_var being 1 rather than whatever value it
had before that.  Of course, if a global optimizer can prove that
some_function() can't do anything that can possibly care about
some_global_var, then the store could be omitted, but not otherwise.
Should the compiler omit the store and should there then be a bug in
the program, we wouldn't say "oh, some_global_var ought to be declared
volatile".  We would say "that compiler is buggy".

Now, conversely, in this situation, it seems to me (and I think you'll
agree) that the compiler could be forgiven for omitting one of the
stores:

some_global_var = 1;
local_var = 42;
some_global_var = 2;

If we declare some_global_var as volatile, it will force the compiler
to perform both stores regardless of what the optimizer thinks, and
moreover, the second store is required to happen after the first one,
because the definition of volatile is that volatile references are
globally sequenced with respect TO EACH OTHER.  However, the store to
local_var could be moved around with respect to the other two or
omitted altogether unless local_var is also declared volatile.

Now, consider this:

some_global_var = 1;
pg_compiler_barrier(); /* or in Linux, barrier() */
some_global_var = 2;

The compiler barrier is exactly equivalent to the unknown function in
the first example, except that no function is actually called.  The
semantics are precisely that the compiler must assume that, at the
point the barrier intervenes, an unknown operation will occur which
may depend on the contents of any word in memory and which may modify
any word in memory.  Thus, the compiler may not postpone stores
requested before the barrier until after the barrier on the grounds
that the values will be overwritten after the barrier; and if any
global variables have been loaded into registers before the barrier it
must be assumed that, after the barrier, the registers may no longer
match those global variables.

It is true that any barrier, including a compiler barrier, serves only
to separate instructions.  But I don't believe it follows in any way
that the barrier therefore prohibits reordering operations but not
omitting them altogether.  Such a definition would have no practical
utility.  The compiler is not free to willy-nilly leave out things
that the user asks it to do any more than it is free to willy-nilly
reorder them.  If it were, programming would be chaos, and everything
would have to be volatile.  What the compiler is free to do is to
reorder and omit instructions where the programmer won't, in a
single-thread execution context, be able to notice the difference.
And a compiler barrier is an explicit notification that, in essence,
the programmer will notice if things are not just the way he wrote
them when that point in the code is reached.

To see the difference between this and volatile, consider the following:

a = 1;
b = 1;
c = 1;
a = 2;
b = 2;
c = 2;

Absent any special precautions, the compiler may well optimize the
first set of stores away completely and perform the second set in any
order it likes.  If we make all the variables volatile, it will do all
6 stores in precisely the order specified, omitting nothing and
reordering nothing.  If we instead stick a compiler barrier just after
the assignment "c = 1", then the compiler must store 1 in all three
variables (in any order that it likes), and then store 2 in all three
variables (in any order that it likes).  The "barrier" essentially
divides up the code into chunks and requires that those chunks be
optimized independently by the compiler without knowledge of what
earlier or later chunks are doing.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: Let's invent a function to report lock-wait-blocking PIDs
Next
From: Ants Aasma
Date:
Subject: Re: Enabling Checksums