1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-20 11:11:24 +00:00

Implement atomic_swap() and atomic_testandset().

Reviewed by:	arch, bde, jilles, kib
This commit is contained in:
Jung-uk Kim 2013-08-21 22:03:06 +00:00
parent 8e0cf70b5d
commit 8a1ee2d346
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=254617
3 changed files with 166 additions and 51 deletions

View File

@ -23,7 +23,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd September 27, 2005
.Dd August 20, 2013
.Dt ATOMIC 9
.Os
.Sh NAME
@ -62,6 +62,10 @@
.Fn atomic_subtract_[acq_|rel_]<type> "volatile <type> *p" "<type> v"
.Ft void
.Fn atomic_store_rel_<type> "volatile <type> *p" "<type> v"
.Ft <type>
.Fn atomic_swap_<type> "volatile <type> *p" "<type> v"
.Ft int
.Fn atomic_testandset_<type> "volatile <type> *p" "u_int v"
.Sh DESCRIPTION
Each of the atomic operations is guaranteed to be atomic in the presence of
interrupts.
@ -184,9 +188,9 @@ This section describes the semantics of each operation using a C like notation.
.Bd -literal -compact
if (*dst == old) {
*dst = new;
return 1;
return (1);
} else
return 0;
return (0);
.Ed
.El
.Pp
@ -203,7 +207,7 @@ and
.Bd -literal -compact
tmp = *p;
*p += v;
return tmp;
return (tmp);
.Ed
.El
.Pp
@ -216,9 +220,9 @@ and
.Dq Li 32
and do not have any variants with memory barriers at this time.
.Bl -hang
.It Fn atomic_load addr
.It Fn atomic_load p
.Bd -literal -compact
return (*addr)
return (*p);
.Ed
.El
.Pp
@ -226,11 +230,11 @@ The
.Fn atomic_load
functions are only provided with acquire memory barriers.
.Bl -hang
.It Fn atomic_readandclear addr
.It Fn atomic_readandclear p
.Bd -literal -compact
temp = *addr;
*addr = 0;
return (temp);
tmp = *p;
*p = 0;
return (tmp);
.Ed
.El
.Pp
@ -243,8 +247,7 @@ functions are not implemented for the types
.Dq Li 8 ,
and
.Dq Li 16
and do
not have any variants with memory barriers at this time.
and do not have any variants with memory barriers at this time.
.Bl -hang
.It Fn atomic_set p v
.Bd -literal -compact
@ -264,6 +267,44 @@ The
.Fn atomic_store
functions are only provided with release memory barriers.
.Pp
.Bl -hang
.It Fn atomic_swap p v
.Bd -literal -compact
tmp = *p;
*p = v;
return (tmp);
.Ed
.El
.Pp
The
.Fn atomic_swap
functions are not implemented for the types
.Dq Li char ,
.Dq Li short ,
.Dq Li ptr ,
.Dq Li 8 ,
and
.Dq Li 16
and do not have any variants with memory barriers at this time.
.Bl -hang
.It Fn atomic_testandset p v
.Bd -literal -compact
bit = 1 << (v % (sizeof(*p) * NBBY));
tmp = (*p & bit) != 0;
*p |= bit;
return (tmp);
.Ed
.El
.Pp
The
.Fn atomic_testandset
functions are only implemented for the types
.Dq Li int ,
.Dq Li long
and
.Dq Li 32
and do not have any variants with memory barriers at this time.
.Pp
The type
.Dq Li 64
is currently not implemented for any of the atomic operations on the
@ -275,15 +316,17 @@ architectures.
.Sh RETURN VALUES
The
.Fn atomic_cmpset
function
returns the result of the compare operation.
function returns the result of the compare operation.
The
.Fn atomic_fetchadd ,
.Fn atomic_load ,
.Fn atomic_readandclear ,
and
.Fn atomic_readandclear
functions
return the value at the specified address.
.Fn atomic_swap
functions return the value at the specified address.
The
.Fn atomic_testandset
function returns the result of the test operation.
.Sh EXAMPLES
This example uses the
.Fn atomic_cmpset_acq_ptr
@ -354,3 +397,9 @@ The
.Fn atomic_fetchadd
operations were added in
.Fx 6.0 .
The
.Fn atomic_swap
and
.Fn atomic_testandset
operations were added in
.Fx 10.0 .

View File

@ -54,12 +54,14 @@
* atomic_clear_int(P, V) (*(u_int *)(P) &= ~(V))
* atomic_add_int(P, V) (*(u_int *)(P) += (V))
* atomic_subtract_int(P, V) (*(u_int *)(P) -= (V))
* atomic_swap_int(P, V) (return (*(u_int *)(P)); *(u_int *)(P) = (V);)
* atomic_readandclear_int(P) (return (*(u_int *)(P)); *(u_int *)(P) = 0;)
*
* atomic_set_long(P, V) (*(u_long *)(P) |= (V))
* atomic_clear_long(P, V) (*(u_long *)(P) &= ~(V))
* atomic_add_long(P, V) (*(u_long *)(P) += (V))
* atomic_subtract_long(P, V) (*(u_long *)(P) -= (V))
* atomic_swap_long(P, V) (return (*(u_long *)(P)); *(u_long *)(P) = (V);)
* atomic_readandclear_long(P) (return (*(u_long *)(P)); *(u_long *)(P) = 0;)
*/
@ -80,6 +82,8 @@ int atomic_cmpset_int(volatile u_int *dst, u_int expect, u_int src);
int atomic_cmpset_long(volatile u_long *dst, u_long expect, u_long src);
u_int atomic_fetchadd_int(volatile u_int *p, u_int v);
u_long atomic_fetchadd_long(volatile u_long *p, u_long v);
int atomic_testandset_int(volatile u_int *p, u_int v);
int atomic_testandset_long(volatile u_long *p, u_int v);
#define ATOMIC_LOAD(TYPE, LOP) \
u_##TYPE atomic_load_acq_##TYPE(volatile u_##TYPE *p)
@ -205,6 +209,40 @@ atomic_fetchadd_long(volatile u_long *p, u_long v)
return (v);
}
static __inline int
atomic_testandset_int(volatile u_int *p, u_int v)
{
u_char res;
__asm __volatile(
" " MPLOCKED " "
" btsl %2,%1 ; "
" setc %0 ; "
"# atomic_testandset_int"
: "=q" (res), /* 0 */
"+m" (*p) /* 1 */
: "Ir" (v & 0x1f) /* 2 */
: "cc");
return (res);
}
static __inline int
atomic_testandset_long(volatile u_long *p, u_int v)
{
u_char res;
__asm __volatile(
" " MPLOCKED " "
" btsq %2,%1 ; "
" setc %0 ; "
"# atomic_testandset_long"
: "=q" (res), /* 0 */
"+m" (*p) /* 1 */
: "Jr" ((u_long)(v & 0x3f)) /* 2 */
: "cc");
return (res);
}
/*
* We assume that a = b will do atomic loads and stores. Due to the
* IA32 memory model, a simple store guarantees release semantics.
@ -296,43 +334,39 @@ ATOMIC_STORE(long);
#ifndef WANT_FUNCTIONS
/* Read the current value and store a zero in the destination. */
/* Read the current value and store a new value in the destination. */
#ifdef __GNUCLIKE_ASM
static __inline u_int
atomic_readandclear_int(volatile u_int *p)
atomic_swap_int(volatile u_int *p, u_int v)
{
u_int res;
res = 0;
__asm __volatile(
" xchgl %1,%0 ; "
"# atomic_readandclear_int"
: "+r" (res), /* 0 */
"# atomic_swap_int"
: "+r" (v), /* 0 */
"+m" (*p)); /* 1 */
return (res);
return (v);
}
static __inline u_long
atomic_readandclear_long(volatile u_long *p)
atomic_swap_long(volatile u_long *p, u_long v)
{
u_long res;
res = 0;
__asm __volatile(
" xchgq %1,%0 ; "
"# atomic_readandclear_long"
: "+r" (res), /* 0 */
"# atomic_swap_long"
: "+r" (v), /* 0 */
"+m" (*p)); /* 1 */
return (res);
return (v);
}
#else /* !__GNUCLIKE_ASM */
u_int atomic_readandclear_int(volatile u_int *p);
u_long atomic_readandclear_long(volatile u_long *p);
u_int atomic_swap_int(volatile u_int *p, u_int v);
u_long atomic_swap_long(volatile u_long *p, u_long v);
#endif /* __GNUCLIKE_ASM */
@ -376,6 +410,9 @@ u_long atomic_readandclear_long(volatile u_long *p);
#define atomic_cmpset_acq_long atomic_cmpset_long
#define atomic_cmpset_rel_long atomic_cmpset_long
#define atomic_readandclear_int(p) atomic_swap_int(p, 0)
#define atomic_readandclear_long(p) atomic_swap_long(p, 0)
/* Operations on 8-bit bytes. */
#define atomic_set_8 atomic_set_char
#define atomic_set_acq_8 atomic_set_acq_char
@ -426,8 +463,10 @@ u_long atomic_readandclear_long(volatile u_long *p);
#define atomic_cmpset_32 atomic_cmpset_int
#define atomic_cmpset_acq_32 atomic_cmpset_acq_int
#define atomic_cmpset_rel_32 atomic_cmpset_rel_int
#define atomic_swap_32 atomic_swap_int
#define atomic_readandclear_32 atomic_readandclear_int
#define atomic_fetchadd_32 atomic_fetchadd_int
#define atomic_testandset_32 atomic_testandset_int
/* Operations on 64-bit quad words. */
#define atomic_set_64 atomic_set_long
@ -447,7 +486,9 @@ u_long atomic_readandclear_long(volatile u_long *p);
#define atomic_cmpset_64 atomic_cmpset_long
#define atomic_cmpset_acq_64 atomic_cmpset_acq_long
#define atomic_cmpset_rel_64 atomic_cmpset_rel_long
#define atomic_swap_64 atomic_swap_long
#define atomic_readandclear_64 atomic_readandclear_long
#define atomic_testandset_64 atomic_testandset_long
/* Operations on pointers. */
#define atomic_set_ptr atomic_set_long
@ -467,6 +508,7 @@ u_long atomic_readandclear_long(volatile u_long *p);
#define atomic_cmpset_ptr atomic_cmpset_long
#define atomic_cmpset_acq_ptr atomic_cmpset_acq_long
#define atomic_cmpset_rel_ptr atomic_cmpset_rel_long
#define atomic_swap_ptr atomic_swap_long
#define atomic_readandclear_ptr atomic_readandclear_long
#endif /* !WANT_FUNCTIONS */

View File

@ -54,12 +54,14 @@
* atomic_clear_int(P, V) (*(u_int *)(P) &= ~(V))
* atomic_add_int(P, V) (*(u_int *)(P) += (V))
* atomic_subtract_int(P, V) (*(u_int *)(P) -= (V))
* atomic_swap_int(P, V) (return (*(u_int *)(P)); *(u_int *)(P) = (V);)
* atomic_readandclear_int(P) (return (*(u_int *)(P)); *(u_int *)(P) = 0;)
*
* atomic_set_long(P, V) (*(u_long *)(P) |= (V))
* atomic_clear_long(P, V) (*(u_long *)(P) &= ~(V))
* atomic_add_long(P, V) (*(u_long *)(P) += (V))
* atomic_subtract_long(P, V) (*(u_long *)(P) -= (V))
* atomic_swap_long(P, V) (return (*(u_long *)(P)); *(u_long *)(P) = (V);)
* atomic_readandclear_long(P) (return (*(u_long *)(P)); *(u_long *)(P) = 0;)
*/
@ -78,6 +80,7 @@ void atomic_##NAME##_barr_##TYPE(volatile u_##TYPE *p, u_##TYPE v)
int atomic_cmpset_int(volatile u_int *dst, u_int expect, u_int src);
u_int atomic_fetchadd_int(volatile u_int *p, u_int v);
int atomic_testandset_int(volatile u_int *p, u_int v);
#define ATOMIC_LOAD(TYPE, LOP) \
u_##TYPE atomic_load_acq_##TYPE(volatile u_##TYPE *p)
@ -275,6 +278,23 @@ atomic_fetchadd_int(volatile u_int *p, u_int v)
return (v);
}
static __inline int
atomic_testandset_int(volatile u_int *p, u_int v)
{
u_char res;
__asm __volatile(
" " MPLOCKED " "
" btsl %2,%1 ; "
" setc %0 ; "
"# atomic_testandset_int"
: "=q" (res), /* 0 */
"+m" (*p) /* 1 */
: "Ir" (v & 0x1f) /* 2 */
: "cc");
return (res);
}
/*
* We assume that a = b will do atomic loads and stores. Due to the
* IA32 memory model, a simple store guarantees release semantics.
@ -386,43 +406,40 @@ atomic_fetchadd_long(volatile u_long *p, u_long v)
return (atomic_fetchadd_int((volatile u_int *)p, (u_int)v));
}
/* Read the current value and store a zero in the destination. */
static __inline int
atomic_testandset_long(volatile u_long *p, u_int v)
{
return (atomic_testandset_int((volatile u_int *)p, v));
}
/* Read the current value and store a new value in the destination. */
#ifdef __GNUCLIKE_ASM
static __inline u_int
atomic_readandclear_int(volatile u_int *p)
atomic_swap_int(volatile u_int *p, u_int v)
{
u_int res;
res = 0;
__asm __volatile(
" xchgl %1,%0 ; "
"# atomic_readandclear_int"
: "+r" (res), /* 0 */
"# atomic_swap_int"
: "+r" (v), /* 0 */
"+m" (*p)); /* 1 */
return (res);
return (v);
}
static __inline u_long
atomic_readandclear_long(volatile u_long *p)
atomic_swap_long(volatile u_long *p, u_long v)
{
u_long res;
res = 0;
__asm __volatile(
" xchgl %1,%0 ; "
"# atomic_readandclear_long"
: "+r" (res), /* 0 */
"+m" (*p)); /* 1 */
return (res);
return (atomic_swap_int((volatile u_int *)p, (u_int)v));
}
#else /* !__GNUCLIKE_ASM */
u_int atomic_readandclear_int(volatile u_int *p);
u_long atomic_readandclear_long(volatile u_long *p);
u_int atomic_swap_int(volatile u_int *p, u_int v);
u_long atomic_swap_long(volatile u_long *p, u_long v);
#endif /* __GNUCLIKE_ASM */
@ -466,6 +483,9 @@ u_long atomic_readandclear_long(volatile u_long *p);
#define atomic_cmpset_acq_long atomic_cmpset_long
#define atomic_cmpset_rel_long atomic_cmpset_long
#define atomic_readandclear_int(p) atomic_swap_int(p, 0)
#define atomic_readandclear_long(p) atomic_swap_long(p, 0)
/* Operations on 8-bit bytes. */
#define atomic_set_8 atomic_set_char
#define atomic_set_acq_8 atomic_set_acq_char
@ -516,8 +536,10 @@ u_long atomic_readandclear_long(volatile u_long *p);
#define atomic_cmpset_32 atomic_cmpset_int
#define atomic_cmpset_acq_32 atomic_cmpset_acq_int
#define atomic_cmpset_rel_32 atomic_cmpset_rel_int
#define atomic_swap_32 atomic_swap_int
#define atomic_readandclear_32 atomic_readandclear_int
#define atomic_fetchadd_32 atomic_fetchadd_int
#define atomic_testandset_32 atomic_testandset_int
/* Operations on pointers. */
#define atomic_set_ptr(p, v) \
@ -556,6 +578,8 @@ u_long atomic_readandclear_long(volatile u_long *p);
#define atomic_cmpset_rel_ptr(dst, old, new) \
atomic_cmpset_rel_int((volatile u_int *)(dst), (u_int)(old), \
(u_int)(new))
#define atomic_swap_ptr(p, v) \
atomic_swap_int((volatile u_int *)(p), (u_int)(v))
#define atomic_readandclear_ptr(p) \
atomic_readandclear_int((volatile u_int *)(p))