Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 20 Dec 2007 02:44:23 +1100 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Bruce Evans <brde@optusnet.com.au>
Cc:        cvs-src@freebsd.org, src-committers@freebsd.org, Warner Losh <imp@freebsd.org>, cvs-all@freebsd.org
Subject:   Re: cvs commit: src/lib/libc/stdtime localtime.c
Message-ID:  <20071220021032.T37695@delplex.bde.org>
In-Reply-To: <20071219210516.I37050@delplex.bde.org>
References:  <200712190430.lBJ4UB7M018392@repoman.freebsd.org> <20071219210516.I37050@delplex.bde.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Wed, 19 Dec 2007, Bruce Evans wrote:

> On Wed, 19 Dec 2007, Warner Losh wrote:
>
>> imp         2007-12-19 04:30:11 UTC
>> 
>>  FreeBSD src repository
>> 
>>  Modified files:
>>    lib/libc/stdtime     localtime.c
>>  Log:
>>  Reduce lock contention for simple cases.
>> 
>>  # this really should be done with pthread_once, but I've debugged this 
>> code.
>> 
>>  Reviewed by: arch@
>
> Reviewers weren't happy with this.  I now think that the only bug in
> it is that it unnecesarily depends i386 memory semantics (that writes
> are not reordered).  There should be a write barrier before the flag
> is set, to ensure that the writes which initialize things occur before
> the write that sets the flag.

it also depends on the compiler not reordering writes.  But gcc does,
and I think is permitted to, reorder writes:

%%%
int x;
int y;

main()
{
 	x = 1;
 	y = 2;
 	x = 3;
}
%%%

compiles to:

%%%
 	.file	"z.c"
 	.text
 	.p2align 2,,3
.globl main
 	.type	main, @function
main:
 	pushl	%ebp
 	movl	%esp, %ebp
 	subl	$8, %esp
 	andl	$-16, %esp
 	movl	$2, y
 	movl	$3, x
 	leave
 	ret
 	.size	main, .-main
 	.comm	x,4,4
 	.comm	y,4,4
 	.ident	"GCC: (GNU) 3.3.3 [FreeBSD] 20031106"
%%%

Moving the first assignment to x to after the assignment to y and then
optimizing away the first assigment are is permitted since neither x
nor y is volatile and their are no function calls that might access x
or y.  It is done since it is easy to see that it is valid -- there
are obviously no function calls and no pointers to cause aliasing
problems.  C has a rule that writes that the program must operate
as if writes are done at sequence points, and there is a sequence
point on every semicolon in the above, so it takes some analysis
to see that the transformation is valid.  Writes may be reordered
even across function calls, but usually aren't because the function
is not visible or the analysis is too hard.

Signal handlers also cannot access x or y, especially to read
them and act on their value, since signal handlers are only permitted
to assign (write) to variables of type sig_atomic_t.

Similarly if x is only assigned to once.  The same things that permit
removing the first of the 2 assignments to x in the above permit
doing the assignments in any order.

With normal locking, this problem is handled magically by the implicit
write barrier in mutex unlock.  The write barrier first causes all
writes to be flushed in some order.  With i386 memory semantics, this
order is the same as the compiler decided to use, which may be quite
different from the program order, but any differences don't matter
because the rendevous at the lock prevents anything else looking at
any of the variables until they have all been written.  Then the lock
is released, and the write order doesn't matter because everything has
been written before anything looks at the variables.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20071220021032.T37695>