Thread: [HACKERS] shm_toc_lookup API

[HACKERS] shm_toc_lookup API

From
Tom Lane
Date:
shm_toc_lookup() returns NULL if it fails to find the desired key.
Out of the 30 or so call sites, there is exactly one that has any
use for that.  Some of the rest have Asserts that they get back
a non-null result, but the majority just blithely dereference the
pointer.  I do not find this cool at all; given that we're accessing
a shared data structure, we should assign more than zero probability
to the idea that we might not find what we expect to find when we
expect to find it.

I think that shm_toc_lookup() ought to be made to throw elog(ERROR) on an
unexpected failure.  To satisfy the one caller that doesn't want an error,
we could either add a "bool noError" parameter to it, or split it into two
functions shm_toc_lookup() and shm_toc_lookup_noerror().  The latter would
require touching less code, but the former is probably more like what we'd
have had if this were designed more carefully to begin with.

Any preferences?
        regards, tom lane



Re: [HACKERS] shm_toc_lookup API

From
Robert Haas
Date:
On Mon, Jun 5, 2017 at 11:19 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> shm_toc_lookup() returns NULL if it fails to find the desired key.
> Out of the 30 or so call sites, there is exactly one that has any
> use for that.  Some of the rest have Asserts that they get back
> a non-null result, but the majority just blithely dereference the
> pointer.  I do not find this cool at all; given that we're accessing
> a shared data structure, we should assign more than zero probability
> to the idea that we might not find what we expect to find when we
> expect to find it.
>
> I think that shm_toc_lookup() ought to be made to throw elog(ERROR) on an
> unexpected failure.  To satisfy the one caller that doesn't want an error,
> we could either add a "bool noError" parameter to it, or split it into two
> functions shm_toc_lookup() and shm_toc_lookup_noerror().  The latter would
> require touching less code, but the former is probably more like what we'd
> have had if this were designed more carefully to begin with.

Either is OK with me.

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



Re: [HACKERS] shm_toc_lookup API

From
Tom Lane
Date:
Robert Haas <robertmhaas@gmail.com> writes:
> On Mon, Jun 5, 2017 at 11:19 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> I think that shm_toc_lookup() ought to be made to throw elog(ERROR) on an
>> unexpected failure.  To satisfy the one caller that doesn't want an error,
>> we could either add a "bool noError" parameter to it, or split it into two
>> functions shm_toc_lookup() and shm_toc_lookup_noerror().  The latter would
>> require touching less code, but the former is probably more like what we'd
>> have had if this were designed more carefully to begin with.

> Either is OK with me.

Done with the extra parameter.  If we discover a reason to back-patch
this, we'd likely need to do it the other way in back branches, but
for now I'll refrain.

While I'm looking at this ... seems like there's a pretty basic coding-
rule violation here, namely that shm_toc_lookup() thinks it can read
toc->toc_nentry without any sort of locking.  Since that field is declared
Size, this amounts to an assumption that 64-bit reads are atomic, which
is not per project practice.

In practice it probably can't fail even if 64-bit reads aren't atomic,
simply because we'll never have enough entries in a shm_toc to make the
high-order half ever change.  But that just begs the question why the
field is declared Size rather than int.  I think we should make it the
latter.

I am also thinking that most of the shm_toc functions need to have the
toc pointers declared as "volatile *", but particularly shm_toc_lookup.
That read_barrier call might prevent the hardware from reordering
accesses, but I don't think it stops the compiler from doing so.

These problems are probably just latent, because it looks to me like
in our current usage no process would ever be doing lookups on shm_tocs
that are being changed concurrently.  But they'll bite us someday.
        regards, tom lane



Re: [HACKERS] shm_toc_lookup API

From
Robert Haas
Date:
On Mon, Jun 5, 2017 at 12:19 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> While I'm looking at this ... seems like there's a pretty basic coding-
> rule violation here, namely that shm_toc_lookup() thinks it can read
> toc->toc_nentry without any sort of locking.  Since that field is declared
> Size, this amounts to an assumption that 64-bit reads are atomic, which
> is not per project practice.
>
> In practice it probably can't fail even if 64-bit reads aren't atomic,
> simply because we'll never have enough entries in a shm_toc to make the
> high-order half ever change.  But that just begs the question why the
> field is declared Size rather than int.  I think we should make it the
> latter.

Yeah.  I think a shm_toc with more than 2^10 entries would probably
perform badly enough that somebody would rewrite this entire module,
so we don't really need to worry about having more than 2^31.
Changing to int (or uint32) seems fine.

> I am also thinking that most of the shm_toc functions need to have the
> toc pointers declared as "volatile *", but particularly shm_toc_lookup.
> That read_barrier call might prevent the hardware from reordering
> accesses, but I don't think it stops the compiler from doing so.

If it doesn't prevent both the hardware and the compiler from
reordering, it's broken.  See the comments for pg_read_barrier() in
atomics.h.

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



Re: [HACKERS] shm_toc_lookup API

From
Tom Lane
Date:
Robert Haas <robertmhaas@gmail.com> writes:
> On Mon, Jun 5, 2017 at 12:19 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> In practice it probably can't fail even if 64-bit reads aren't atomic,
>> simply because we'll never have enough entries in a shm_toc to make the
>> high-order half ever change.  But that just begs the question why the
>> field is declared Size rather than int.  I think we should make it the
>> latter.

> Yeah.  I think a shm_toc with more than 2^10 entries would probably
> perform badly enough that somebody would rewrite this entire module,
> so we don't really need to worry about having more than 2^31.
> Changing to int (or uint32) seems fine.

Done with uint32.

>> I am also thinking that most of the shm_toc functions need to have the
>> toc pointers declared as "volatile *", but particularly shm_toc_lookup.

(actually, they do already use volatile pointers, except for shm_toc_lookup)

>> That read_barrier call might prevent the hardware from reordering
>> accesses, but I don't think it stops the compiler from doing so.

> If it doesn't prevent both the hardware and the compiler from
> reordering, it's broken.  See the comments for pg_read_barrier() in
> atomics.h.

Meh.  Without volatile, I think that the compiler would be within its
rights to elide the nentry local variable and re-fetch toc->toc_nentry
each time through the loop.  It'd be unlikely to do so, granted, but
I'm not convinced that pg_read_barrier() would prevent that.

However, as long as the write barrier in shm_toc_insert does what it's
supposed to, I think we'd be safe even if that happened.  So probably
it's a moot point.
        regards, tom lane



Re: [HACKERS] shm_toc_lookup API

From
Andres Freund
Date:
On 2017-06-05 14:57:10 -0400, Tom Lane wrote:
> > If it doesn't prevent both the hardware and the compiler from
> > reordering, it's broken.  See the comments for pg_read_barrier() in
> > atomics.h.
> 
> Meh.  Without volatile, I think that the compiler would be within its
> rights to elide the nentry local variable and re-fetch toc->toc_nentry
> each time through the loop.

I don't think that's true. Excerption from the docs of the macros:
About pg_read_barrier()* A read barrier must act as a compiler barrier, and in addition must
About pg_compiler_barrier():* A compiler barrier need not (and preferably should not) emit any actual* machine code,
butmust act as an optimization fence: the compiler must not* reorder loads or stores to main memory around the barrier.
However, the* CPU may still reorder loads or stores at runtime, if the architecture's* memory model permits this.*/
 

Given that I don't see how it'd be permissible to elide the local
variable.  Are you saying that's permitted, or that our implementations
don't guarantee that?

- Andres



Re: [HACKERS] shm_toc_lookup API

From
Tom Lane
Date:
Andres Freund <andres@anarazel.de> writes:
> On 2017-06-05 14:57:10 -0400, Tom Lane wrote:
>> Meh.  Without volatile, I think that the compiler would be within its
>> rights to elide the nentry local variable and re-fetch toc->toc_nentry
>> each time through the loop.

> I don't think that's true.

Perhaps not, but I'm not quite convinced.  Anyway it doesn't matter,
at least not here.
        regards, tom lane



Re: [HACKERS] shm_toc_lookup API

From
Andres Freund
Date:
On 2017-06-05 15:06:06 -0400, Tom Lane wrote:
> Andres Freund <andres@anarazel.de> writes:
> > On 2017-06-05 14:57:10 -0400, Tom Lane wrote:
> >> Meh.  Without volatile, I think that the compiler would be within its
> >> rights to elide the nentry local variable and re-fetch toc->toc_nentry
> >> each time through the loop.
> 
> > I don't think that's true.
> 
> Perhaps not, but I'm not quite convinced.  Anyway it doesn't matter,
> at least not here.

If not, we have a bunch of bugs in other files.  So if there are
concerns, we should better resolve them.

- Andres