mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 21:55:25 +00:00
Rewrite virAtomic APIs using GLib's atomic ops code
There are a few issues with the current virAtomic APIs - They require use of a virAtomicInt struct instead of a plain int type - Several of the methods do not implement memory barriers - The methods do not implement compiler re-ordering barriers - There is no Win32 native impl The GLib library has a nice LGPLv2+ licensed impl of atomic ops that works with GCC, Win32, or pthreads.h that addresses all these problems. The main downside to their code is that the pthreads impl uses a single global mutex, instead of a per-variable mutex. Given that it does have a Win32 impl though, we don't expect anyone to seriously use the pthread.h impl, so this downside is not significant. * .gitignore: Ignore test case * configure.ac: Check for which atomic ops impl to use * src/Makefile.am: Add viratomic.c * src/nwfilter/nwfilter_dhcpsnoop.c: Switch to new atomic ops APIs and plain int datatype * src/util/viratomic.h: inline impls of all atomic ops for GCC, Win32 and pthreads * src/util/viratomic.c: Global pthreads mutex for atomic ops * tests/viratomictest.c: Test validate to validate safety of atomic ops. Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
parent
b49890de82
commit
0c9fd4cfe9
1
.gitignore
vendored
1
.gitignore
vendored
@ -152,6 +152,7 @@
|
|||||||
/tests/statstest
|
/tests/statstest
|
||||||
/tests/storagebackendsheepdogtest
|
/tests/storagebackendsheepdogtest
|
||||||
/tests/utiltest
|
/tests/utiltest
|
||||||
|
/tests/viratomictest
|
||||||
/tests/virauthconfigtest
|
/tests/virauthconfigtest
|
||||||
/tests/virbuftest
|
/tests/virbuftest
|
||||||
/tests/virdrivermoduletest
|
/tests/virdrivermoduletest
|
||||||
|
57
configure.ac
57
configure.ac
@ -159,6 +159,63 @@ AC_CHECK_HEADERS([pwd.h paths.h regex.h sys/un.h \
|
|||||||
sys/un.h sys/syscall.h netinet/tcp.h ifaddrs.h libtasn1.h \
|
sys/un.h sys/syscall.h netinet/tcp.h ifaddrs.h libtasn1.h \
|
||||||
net/if.h execinfo.h])
|
net/if.h execinfo.h])
|
||||||
|
|
||||||
|
dnl We need to decide at configure time if libvirt will use real atomic
|
||||||
|
dnl operations ("lock free") or emulated ones with a mutex.
|
||||||
|
|
||||||
|
dnl Note that the atomic ops are only available with GCC on x86 when
|
||||||
|
dnl using -march=i486 or higher. If we detect that the atomic ops are
|
||||||
|
dnl not available but would be available given the right flags, we want
|
||||||
|
dnl to abort and advise the user to fix their CFLAGS. It's better to do
|
||||||
|
dnl that then to silently fall back on emulated atomic ops just because
|
||||||
|
dnl the user had the wrong build environment.
|
||||||
|
|
||||||
|
atomic_ops=
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([for atomic ops implementation])
|
||||||
|
|
||||||
|
AC_TRY_COMPILE([], [__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4;],[
|
||||||
|
atomic_ops=gcc
|
||||||
|
],[])
|
||||||
|
|
||||||
|
if test "$atomic_ops" = "" ; then
|
||||||
|
SAVE_CFLAGS="${CFLAGS}"
|
||||||
|
CFLAGS="-march=i486"
|
||||||
|
AC_TRY_COMPILE([],
|
||||||
|
[__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4;],
|
||||||
|
[AC_MSG_ERROR([Libvirt must be built with -march=i486 or later.])],
|
||||||
|
[])
|
||||||
|
CFLAGS="${SAVE_CFLAGS}"
|
||||||
|
|
||||||
|
case "$host" in
|
||||||
|
*-*-mingw* | *-*-msvc* )
|
||||||
|
atomic_ops=win32
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if test "$ac_cv_header_pthread_h" = "yes" ; then
|
||||||
|
atomic_ops=pthread
|
||||||
|
else
|
||||||
|
AC_MSG_ERROR([Libvirt must be built with GCC or have pthread.h on non-Win32 platforms])
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$atomic_ops" in
|
||||||
|
gcc)
|
||||||
|
AC_DEFINE([VIR_ATOMIC_OPS_GCC],[1],[Use GCC atomic ops])
|
||||||
|
;;
|
||||||
|
win32)
|
||||||
|
AC_DEFINE([VIR_ATOMIC_OPS_WIN32],[1],[Use Win32 atomic ops])
|
||||||
|
;;
|
||||||
|
pthread)
|
||||||
|
AC_DEFINE([VIR_ATOMIC_OPS_PTHREAD],[1],[Use pthread atomic ops emulation])
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
AM_CONDITIONAL([WITH_ATOMIC_OPS_PTHREAD],[test "$atomic_ops" = "pthread"])
|
||||||
|
AC_MSG_RESULT([$atomic_ops])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AC_MSG_CHECKING([for struct ifreq in net/if.h])
|
AC_MSG_CHECKING([for struct ifreq in net/if.h])
|
||||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
[[
|
[[
|
||||||
|
@ -79,7 +79,7 @@ UTIL_SOURCES = \
|
|||||||
util/threadpool.c util/threadpool.h \
|
util/threadpool.c util/threadpool.h \
|
||||||
util/uuid.c util/uuid.h \
|
util/uuid.c util/uuid.h \
|
||||||
util/util.c util/util.h \
|
util/util.c util/util.h \
|
||||||
util/viratomic.h \
|
util/viratomic.h util/viratomic.c \
|
||||||
util/viraudit.c util/viraudit.h \
|
util/viraudit.c util/viraudit.h \
|
||||||
util/virauth.c util/virauth.h \
|
util/virauth.c util/virauth.h \
|
||||||
util/virauthconfig.c util/virauthconfig.h \
|
util/virauthconfig.c util/virauthconfig.h \
|
||||||
@ -1317,9 +1317,14 @@ if HAVE_SASL
|
|||||||
USED_SYM_FILES += libvirt_sasl.syms
|
USED_SYM_FILES += libvirt_sasl.syms
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if WITH_ATOMIC_OPS_PTHREAD
|
||||||
|
USED_SYM_FILES += libvirt_atomic.syms
|
||||||
|
endif
|
||||||
|
|
||||||
EXTRA_DIST += \
|
EXTRA_DIST += \
|
||||||
libvirt_public.syms \
|
libvirt_public.syms \
|
||||||
libvirt_private.syms \
|
libvirt_private.syms \
|
||||||
|
libvirt_atomic.syms \
|
||||||
libvirt_driver_modules.syms \
|
libvirt_driver_modules.syms \
|
||||||
libvirt_daemon.syms \
|
libvirt_daemon.syms \
|
||||||
libvirt_linux.syms \
|
libvirt_linux.syms \
|
||||||
|
3
src/libvirt_atomic.syms
Normal file
3
src/libvirt_atomic.syms
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
# viratomic.h
|
||||||
|
virAtomicLock;
|
@ -78,9 +78,9 @@
|
|||||||
struct virNWFilterSnoopState {
|
struct virNWFilterSnoopState {
|
||||||
/* lease file */
|
/* lease file */
|
||||||
int leaseFD;
|
int leaseFD;
|
||||||
virAtomicInt nLeases; /* number of active leases */
|
int nLeases; /* number of active leases */
|
||||||
virAtomicInt wLeases; /* number of written leases */
|
int wLeases; /* number of written leases */
|
||||||
virAtomicInt nThreads; /* number of running threads */
|
int nThreads; /* number of running threads */
|
||||||
/* thread management */
|
/* thread management */
|
||||||
virHashTablePtr snoopReqs;
|
virHashTablePtr snoopReqs;
|
||||||
virHashTablePtr ifnameToKey;
|
virHashTablePtr ifnameToKey;
|
||||||
@ -126,7 +126,7 @@ struct _virNWFilterSnoopReq {
|
|||||||
* publicSnoopReqs hash, the refctr may only
|
* publicSnoopReqs hash, the refctr may only
|
||||||
* be modified with the SnoopLock held
|
* be modified with the SnoopLock held
|
||||||
*/
|
*/
|
||||||
virAtomicInt refctr;
|
int refctr;
|
||||||
|
|
||||||
virNWFilterTechDriverPtr techdriver;
|
virNWFilterTechDriverPtr techdriver;
|
||||||
char *ifname;
|
char *ifname;
|
||||||
@ -240,7 +240,7 @@ struct _virNWFilterDHCPDecodeJob {
|
|||||||
unsigned char packet[PCAP_PBUFSIZE];
|
unsigned char packet[PCAP_PBUFSIZE];
|
||||||
int caplen;
|
int caplen;
|
||||||
bool fromVM;
|
bool fromVM;
|
||||||
virAtomicIntPtr qCtr;
|
int *qCtr;
|
||||||
};
|
};
|
||||||
|
|
||||||
# define DHCP_PKT_RATE 10 /* pkts/sec */
|
# define DHCP_PKT_RATE 10 /* pkts/sec */
|
||||||
@ -271,7 +271,7 @@ struct _virNWFilterSnoopPcapConf {
|
|||||||
const pcap_direction_t dir;
|
const pcap_direction_t dir;
|
||||||
const char *filter;
|
const char *filter;
|
||||||
virNWFilterSnoopRateLimitConf rateLimit; /* indep. rate limiters */
|
virNWFilterSnoopRateLimitConf rateLimit; /* indep. rate limiters */
|
||||||
virAtomicInt qCtr; /* number of jobs in the worker's queue */
|
int qCtr; /* number of jobs in the worker's queue */
|
||||||
const unsigned int maxQSize;
|
const unsigned int maxQSize;
|
||||||
unsigned long long penaltyTimeoutAbs;
|
unsigned long long penaltyTimeoutAbs;
|
||||||
};
|
};
|
||||||
@ -588,8 +588,7 @@ virNWFilterSnoopReqNew(const char *ifkey)
|
|||||||
|
|
||||||
req->threadStatus = THREAD_STATUS_NONE;
|
req->threadStatus = THREAD_STATUS_NONE;
|
||||||
|
|
||||||
if (virAtomicIntInit(&req->refctr) < 0 ||
|
if (virStrcpyStatic(req->ifkey, ifkey) == NULL ||
|
||||||
virStrcpyStatic(req->ifkey, ifkey) == NULL ||
|
|
||||||
virMutexInitRecursive(&req->lock) < 0)
|
virMutexInitRecursive(&req->lock) < 0)
|
||||||
goto err_free_req;
|
goto err_free_req;
|
||||||
|
|
||||||
@ -622,7 +621,7 @@ virNWFilterSnoopReqFree(virNWFilterSnoopReqPtr req)
|
|||||||
if (!req)
|
if (!req)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (virAtomicIntRead(&req->refctr) != 0)
|
if (virAtomicIntGet(&req->refctr) != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* free all leases */
|
/* free all leases */
|
||||||
@ -715,7 +714,7 @@ virNWFilterSnoopReqPut(virNWFilterSnoopReqPtr req)
|
|||||||
|
|
||||||
virNWFilterSnoopLock();
|
virNWFilterSnoopLock();
|
||||||
|
|
||||||
if (virAtomicIntDec(&req->refctr) == 0) {
|
if (virAtomicIntDecAndTest(&req->refctr)) {
|
||||||
/*
|
/*
|
||||||
* delete the request:
|
* delete the request:
|
||||||
* - if we don't find req on the global list anymore
|
* - if we don't find req on the global list anymore
|
||||||
@ -897,7 +896,7 @@ virNWFilterSnoopReqLeaseDel(virNWFilterSnoopReqPtr req,
|
|||||||
skip_instantiate:
|
skip_instantiate:
|
||||||
VIR_FREE(ipl);
|
VIR_FREE(ipl);
|
||||||
|
|
||||||
virAtomicIntDec(&virNWFilterSnoopState.nLeases);
|
virAtomicIntDecAndTest(&virNWFilterSnoopState.nLeases);
|
||||||
|
|
||||||
lease_not_found:
|
lease_not_found:
|
||||||
VIR_FREE(ipstr);
|
VIR_FREE(ipstr);
|
||||||
@ -1159,7 +1158,7 @@ static void virNWFilterDHCPDecodeWorker(void *jobdata, void *opaque)
|
|||||||
_("Instantiation of rules failed on "
|
_("Instantiation of rules failed on "
|
||||||
"interface '%s'"), req->ifname);
|
"interface '%s'"), req->ifname);
|
||||||
}
|
}
|
||||||
virAtomicIntDec(job->qCtr);
|
virAtomicIntDecAndTest(job->qCtr);
|
||||||
VIR_FREE(job);
|
VIR_FREE(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1170,7 +1169,7 @@ static int
|
|||||||
virNWFilterSnoopDHCPDecodeJobSubmit(virThreadPoolPtr pool,
|
virNWFilterSnoopDHCPDecodeJobSubmit(virThreadPoolPtr pool,
|
||||||
virNWFilterSnoopEthHdrPtr pep,
|
virNWFilterSnoopEthHdrPtr pep,
|
||||||
int len, pcap_direction_t dir,
|
int len, pcap_direction_t dir,
|
||||||
virAtomicIntPtr qCtr)
|
int *qCtr)
|
||||||
{
|
{
|
||||||
virNWFilterDHCPDecodeJobPtr job;
|
virNWFilterDHCPDecodeJobPtr job;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1376,8 +1375,7 @@ virNWFilterDHCPSnoopThread(void *req0)
|
|||||||
virNWFilterSnoopDHCPOpen(req->ifname, &req->macaddr,
|
virNWFilterSnoopDHCPOpen(req->ifname, &req->macaddr,
|
||||||
pcapConf[i].filter,
|
pcapConf[i].filter,
|
||||||
pcapConf[i].dir);
|
pcapConf[i].dir);
|
||||||
if (!pcapConf[i].handle ||
|
if (!pcapConf[i].handle) {
|
||||||
virAtomicIntInit(&pcapConf[i].qCtr) < 0) {
|
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1488,7 +1486,7 @@ virNWFilterDHCPSnoopThread(void *req0)
|
|||||||
unsigned int diff;
|
unsigned int diff;
|
||||||
|
|
||||||
/* submit packet to worker thread */
|
/* submit packet to worker thread */
|
||||||
if (virAtomicIntRead(&pcapConf[i].qCtr) >
|
if (virAtomicIntGet(&pcapConf[i].qCtr) >
|
||||||
pcapConf[i].maxQSize) {
|
pcapConf[i].maxQSize) {
|
||||||
if (last_displayed_queue - time(0) > 10) {
|
if (last_displayed_queue - time(0) > 10) {
|
||||||
last_displayed_queue = time(0);
|
last_displayed_queue = time(0);
|
||||||
@ -1554,7 +1552,7 @@ exit:
|
|||||||
pcap_close(pcapConf[i].handle);
|
pcap_close(pcapConf[i].handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
virAtomicIntDec(&virNWFilterSnoopState.nThreads);
|
virAtomicIntDecAndTest(&virNWFilterSnoopState.nThreads);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1805,7 +1803,7 @@ virNWFilterSnoopLeaseFileSave(virNWFilterSnoopIPLeasePtr ipl)
|
|||||||
|
|
||||||
/* keep dead leases at < ~95% of file size */
|
/* keep dead leases at < ~95% of file size */
|
||||||
if (virAtomicIntInc(&virNWFilterSnoopState.wLeases) >=
|
if (virAtomicIntInc(&virNWFilterSnoopState.wLeases) >=
|
||||||
virAtomicIntRead(&virNWFilterSnoopState.nLeases) * 20)
|
virAtomicIntGet(&virNWFilterSnoopState.nLeases) * 20)
|
||||||
virNWFilterSnoopLeaseFileLoad(); /* load & refresh lease file */
|
virNWFilterSnoopLeaseFileLoad(); /* load & refresh lease file */
|
||||||
|
|
||||||
err_exit:
|
err_exit:
|
||||||
@ -1836,7 +1834,7 @@ virNWFilterSnoopPruneIter(const void *payload,
|
|||||||
/*
|
/*
|
||||||
* have the entry removed if it has no leases and no one holds a ref
|
* have the entry removed if it has no leases and no one holds a ref
|
||||||
*/
|
*/
|
||||||
del_req = ((req->start == NULL) && (virAtomicIntRead(&req->refctr) == 0));
|
del_req = ((req->start == NULL) && (virAtomicIntGet(&req->refctr) == 0));
|
||||||
|
|
||||||
virNWFilterSnoopReqUnlock(req);
|
virNWFilterSnoopReqUnlock(req);
|
||||||
|
|
||||||
@ -1994,9 +1992,9 @@ virNWFilterSnoopLeaseFileLoad(void)
|
|||||||
static void
|
static void
|
||||||
virNWFilterSnoopJoinThreads(void)
|
virNWFilterSnoopJoinThreads(void)
|
||||||
{
|
{
|
||||||
while (virAtomicIntRead(&virNWFilterSnoopState.nThreads) != 0) {
|
while (virAtomicIntGet(&virNWFilterSnoopState.nThreads) != 0) {
|
||||||
VIR_WARN("Waiting for snooping threads to terminate: %u\n",
|
VIR_WARN("Waiting for snooping threads to terminate: %u\n",
|
||||||
virAtomicIntRead(&virNWFilterSnoopState.nThreads));
|
virAtomicIntGet(&virNWFilterSnoopState.nThreads));
|
||||||
usleep(1000 * 1000);
|
usleep(1000 * 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2061,10 +2059,7 @@ virNWFilterDHCPSnoopInit(void)
|
|||||||
VIR_DEBUG("Initializing DHCP snooping");
|
VIR_DEBUG("Initializing DHCP snooping");
|
||||||
|
|
||||||
if (virMutexInitRecursive(&virNWFilterSnoopState.snoopLock) < 0 ||
|
if (virMutexInitRecursive(&virNWFilterSnoopState.snoopLock) < 0 ||
|
||||||
virMutexInit(&virNWFilterSnoopState.activeLock) < 0 ||
|
virMutexInit(&virNWFilterSnoopState.activeLock) < 0)
|
||||||
virAtomicIntInit(&virNWFilterSnoopState.nLeases) < 0 ||
|
|
||||||
virAtomicIntInit(&virNWFilterSnoopState.wLeases) < 0 ||
|
|
||||||
virAtomicIntInit(&virNWFilterSnoopState.nThreads) < 0)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
virNWFilterSnoopState.ifnameToKey = virHashCreate(0, NULL);
|
virNWFilterSnoopState.ifnameToKey = virHashCreate(0, NULL);
|
||||||
|
35
src/util/viratomic.c
Normal file
35
src/util/viratomic.c
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* viratomic.h: atomic integer operations
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* Based on code taken from GLib 2.32, under the LGPLv2+
|
||||||
|
*
|
||||||
|
* Copyright (C) 2011 Ryan Lortie
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include "viratomic.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef VIR_ATOMIC_OPS_PTHREAD
|
||||||
|
|
||||||
|
pthread_mutex_t virAtomicLock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
#endif
|
@ -1,10 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* viratomic.h: atomic integer operations
|
* viratomic.h: atomic integer operations
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 IBM Corporation
|
* Copyright (C) 2012 Red Hat, Inc.
|
||||||
*
|
*
|
||||||
* Authors:
|
* Based on code taken from GLib 2.32, under the LGPLv2+
|
||||||
* Stefan Berger <stefanb@linux.vnet.ibm.com>
|
*
|
||||||
|
* Copyright (C) 2011 Ryan Lortie
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -25,130 +26,422 @@
|
|||||||
#ifndef __VIR_ATOMIC_H__
|
#ifndef __VIR_ATOMIC_H__
|
||||||
# define __VIR_ATOMIC_H__
|
# define __VIR_ATOMIC_H__
|
||||||
|
|
||||||
# include "threads.h"
|
# include "internal.h"
|
||||||
|
|
||||||
typedef struct _virAtomicInt virAtomicInt;
|
/**
|
||||||
typedef virAtomicInt *virAtomicIntPtr;
|
* virAtomicIntGet:
|
||||||
|
* Gets the current value of atomic.
|
||||||
|
*
|
||||||
|
* This call acts as a full compiler and hardware memory barrier
|
||||||
|
* (before the get)
|
||||||
|
*/
|
||||||
|
int virAtomicIntGet(volatile int *atomic)
|
||||||
|
ATTRIBUTE_NONNULL(1);
|
||||||
|
|
||||||
# define __VIR_ATOMIC_USES_LOCK
|
/**
|
||||||
|
* virAtomicIntSet:
|
||||||
|
* Sets the value of atomic to newval.
|
||||||
|
*
|
||||||
|
* This call acts as a full compiler and hardware memory barrier
|
||||||
|
* (after the set)
|
||||||
|
*/
|
||||||
|
void virAtomicIntSet(volatile int *atomic,
|
||||||
|
int newval)
|
||||||
|
ATTRIBUTE_NONNULL(1);
|
||||||
|
|
||||||
# if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ > 4)
|
/**
|
||||||
# undef __VIR_ATOMIC_USES_LOCK
|
* virAtomicIntInc:
|
||||||
|
* Increments the value of atomic by 1.
|
||||||
|
*
|
||||||
|
* Think of this operation as an atomic version of
|
||||||
|
* { *atomic += 1; return *atomic; }
|
||||||
|
*
|
||||||
|
* This call acts as a full compiler and hardware memory barrier.
|
||||||
|
*/
|
||||||
|
int virAtomicIntInc(volatile int *atomic)
|
||||||
|
ATTRIBUTE_NONNULL(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* virAtomicIntDecAndTest:
|
||||||
|
* Decrements the value of atomic by 1.
|
||||||
|
*
|
||||||
|
* Think of this operation as an atomic version of
|
||||||
|
* { *atomic -= 1; return *atomic == 0; }
|
||||||
|
*
|
||||||
|
* This call acts as a full compiler and hardware memory barrier.
|
||||||
|
*/
|
||||||
|
bool virAtomicIntDecAndTest(volatile int *atomic)
|
||||||
|
ATTRIBUTE_NONNULL(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* virAtomicIntCompareExchange:
|
||||||
|
* Compares atomic to oldval and, if equal, sets it to newval. If
|
||||||
|
* atomic was not equal to oldval then no change occurs.
|
||||||
|
*
|
||||||
|
* This compare and exchange is done atomically.
|
||||||
|
*
|
||||||
|
* Think of this operation as an atomic version of
|
||||||
|
* { if (*atomic == oldval) { *atomic = newval; return true; }
|
||||||
|
* else return false; }
|
||||||
|
*
|
||||||
|
* This call acts as a full compiler and hardware memory barrier.
|
||||||
|
*/
|
||||||
|
bool virAtomicIntCompareExchange(volatile int *atomic,
|
||||||
|
int oldval,
|
||||||
|
int newval)
|
||||||
|
ATTRIBUTE_NONNULL(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* virAtomicIntAdd:
|
||||||
|
* Atomically adds val to the value of atomic.
|
||||||
|
*
|
||||||
|
* Think of this operation as an atomic version of
|
||||||
|
* { tmp = *atomic; *atomic += val; return tmp; }
|
||||||
|
*
|
||||||
|
* This call acts as a full compiler and hardware memory barrier.
|
||||||
|
*/
|
||||||
|
int virAtomicIntAdd(volatile int *atomic,
|
||||||
|
int val)
|
||||||
|
ATTRIBUTE_NONNULL(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* virAtomicIntAnd:
|
||||||
|
* Performs an atomic bitwise 'and' of the value of atomic
|
||||||
|
* and val, storing the result back in atomic.
|
||||||
|
*
|
||||||
|
* This call acts as a full compiler and hardware memory barrier.
|
||||||
|
*
|
||||||
|
* Think of this operation as an atomic version of
|
||||||
|
* { tmp = *atomic; *atomic &= val; return tmp; }
|
||||||
|
*/
|
||||||
|
unsigned int virAtomicIntAnd(volatile unsigned int *atomic,
|
||||||
|
unsigned int val)
|
||||||
|
ATTRIBUTE_NONNULL(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* virAtomicIntOr:
|
||||||
|
* Performs an atomic bitwise 'or' of the value of atomic
|
||||||
|
* and val, storing the result back in atomic.
|
||||||
|
*
|
||||||
|
* Think of this operation as an atomic version of
|
||||||
|
* { tmp = *atomic; *atomic |= val; return tmp; }
|
||||||
|
*
|
||||||
|
* This call acts as a full compiler and hardware memory barrier.
|
||||||
|
*/
|
||||||
|
unsigned int virAtomicIntOr(volatile unsigned int *atomic,
|
||||||
|
unsigned int val)
|
||||||
|
ATTRIBUTE_NONNULL(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* virAtomicIntXor:
|
||||||
|
* Performs an atomic bitwise 'xor' of the value of atomic
|
||||||
|
* and val, storing the result back in atomic.
|
||||||
|
*
|
||||||
|
* Think of this operation as an atomic version of
|
||||||
|
* { tmp = *atomic; *atomic ^= val; return tmp; }
|
||||||
|
*
|
||||||
|
* This call acts as a full compiler and hardware memory barrier.
|
||||||
|
*/
|
||||||
|
unsigned int virAtomicIntXor(volatile unsigned int *atomic,
|
||||||
|
unsigned int val)
|
||||||
|
ATTRIBUTE_NONNULL(1);
|
||||||
|
|
||||||
|
|
||||||
|
# ifdef VIR_ATOMIC_OPS_GCC
|
||||||
|
|
||||||
|
# define virAtomicIntGet(atomic) \
|
||||||
|
(__extension__ ({ \
|
||||||
|
(void)verify_true(sizeof(*(atomic)) == sizeof(int)); \
|
||||||
|
(void)(0 ? *(atomic) ^ *(atomic) : 0); \
|
||||||
|
__sync_synchronize(); \
|
||||||
|
(int)*(atomic); \
|
||||||
|
}))
|
||||||
|
# define virAtomicIntSet(atomic, newval) \
|
||||||
|
(__extension__ ({ \
|
||||||
|
(void)verify_true(sizeof(*(atomic)) == sizeof(int)); \
|
||||||
|
(void)(0 ? *(atomic) ^ (newval) : 0); \
|
||||||
|
*(atomic) = (newval); \
|
||||||
|
__sync_synchronize(); \
|
||||||
|
}))
|
||||||
|
# define virAtomicIntInc(atomic) \
|
||||||
|
(__extension__ ({ \
|
||||||
|
(void)verify_true(sizeof(*(atomic)) == sizeof(int)); \
|
||||||
|
(void)(0 ? *(atomic) ^ *(atomic) : 0); \
|
||||||
|
__sync_add_and_fetch((atomic), 1); \
|
||||||
|
}))
|
||||||
|
# define virAtomicIntDecAndTest(atomic) \
|
||||||
|
(__extension__ ({ \
|
||||||
|
(void)verify_true(sizeof(*(atomic)) == sizeof(int)); \
|
||||||
|
(void)(0 ? *(atomic) ^ *(atomic) : 0); \
|
||||||
|
__sync_fetch_and_sub((atomic), 1) == 1; \
|
||||||
|
}))
|
||||||
|
# define virAtomicIntCompareExchange(atomic, oldval, newval) \
|
||||||
|
(__extension__ ({ \
|
||||||
|
(void)verify_true(sizeof(*(atomic)) == sizeof(int)); \
|
||||||
|
(void)(0 ? *(atomic) ^ (newval) ^ (oldval) : 0); \
|
||||||
|
(bool)__sync_bool_compare_and_swap((atomic), \
|
||||||
|
(oldval), (newval)); \
|
||||||
|
}))
|
||||||
|
# define virAtomicIntAdd(atomic, val) \
|
||||||
|
(__extension__ ({ \
|
||||||
|
(void)verify_true(sizeof(*(atomic)) == sizeof(int)); \
|
||||||
|
(void)(0 ? *(atomic) ^ (val) : 0); \
|
||||||
|
(int) __sync_fetch_and_add((atomic), (val)); \
|
||||||
|
}))
|
||||||
|
# define virAtomicIntAnd(atomic, val) \
|
||||||
|
(__extension__ ({ \
|
||||||
|
(void)verify_true(sizeof(*(atomic)) == sizeof(int)); \
|
||||||
|
(void) (0 ? *(atomic) ^ (val) : 0); \
|
||||||
|
(unsigned int) __sync_fetch_and_and((atomic), (val)); \
|
||||||
|
}))
|
||||||
|
# define virAtomicIntOr(atomic, val) \
|
||||||
|
(__extension__ ({ \
|
||||||
|
(void)verify_true(sizeof(*(atomic)) == sizeof(int)); \
|
||||||
|
(void) (0 ? *(atomic) ^ (val) : 0); \
|
||||||
|
(unsigned int) __sync_fetch_and_or((atomic), (val)); \
|
||||||
|
}))
|
||||||
|
# define virAtomicIntXor(atomic, val) \
|
||||||
|
(__extension__ ({ \
|
||||||
|
(void)verify_true(sizeof(*(atomic)) == sizeof(int)); \
|
||||||
|
(void) (0 ? *(atomic) ^ (val) : 0); \
|
||||||
|
(unsigned int) __sync_fetch_and_xor((atomic), (val)); \
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
# else
|
||||||
|
|
||||||
|
# ifdef VIR_ATOMIC_OPS_WIN32
|
||||||
|
|
||||||
|
# include <winsock2.h>
|
||||||
|
# include <windows.h>
|
||||||
|
# include <intrin.h>
|
||||||
|
# if !defined(_M_AMD64) && !defined (_M_IA64) && !defined(_M_X64)
|
||||||
|
# define InterlockedAnd _InterlockedAnd
|
||||||
|
# define InterlockedOr _InterlockedOr
|
||||||
|
# define InterlockedXor _InterlockedXor
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
static inline int virAtomicIntInit(virAtomicIntPtr vaip)
|
/*
|
||||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
|
* http://msdn.microsoft.com/en-us/library/ms684122(v=vs.85).aspx
|
||||||
static inline int virAtomicIntRead(virAtomicIntPtr vaip)
|
*/
|
||||||
ATTRIBUTE_NONNULL(1);
|
inline int
|
||||||
static inline void virAtomicIntSet(virAtomicIntPtr vaip, int val)
|
virAtomicIntGet(volatile int *atomic)
|
||||||
ATTRIBUTE_NONNULL(1);
|
{
|
||||||
static inline int virAtomicIntAdd(virAtomicIntPtr vaip, int add)
|
MemoryBarrier();
|
||||||
ATTRIBUTE_NONNULL(1);
|
return *atomic;
|
||||||
static inline int virAtomicIntSub(virAtomicIntPtr vaip, int add)
|
}
|
||||||
ATTRIBUTE_NONNULL(1);
|
|
||||||
static inline int virAtomicIntInc(virAtomicIntPtr vaip)
|
|
||||||
ATTRIBUTE_NONNULL(1);
|
|
||||||
static inline int virAtomicIntDec(virAtomicIntPtr vaip)
|
|
||||||
ATTRIBUTE_NONNULL(1);
|
|
||||||
|
|
||||||
# ifdef __VIR_ATOMIC_USES_LOCK
|
inline void
|
||||||
|
virAtomicIntSet(volatile int *atomic,
|
||||||
|
int newval)
|
||||||
|
{
|
||||||
|
*atomic = newval;
|
||||||
|
MemoryBarrier();
|
||||||
|
}
|
||||||
|
|
||||||
struct _virAtomicInt {
|
inline int
|
||||||
virMutex lock;
|
virAtomicIntInc(volatile int *atomic)
|
||||||
|
{
|
||||||
|
return InterlockedIncrement((volatile LONG *)atomic);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
virAtomicIntDecAndTest(volatile int *atomic)
|
||||||
|
{
|
||||||
|
return InterlockedDecrement((volatile LONG *)atomic) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
virAtomicIntCompareExchange(volatile int *atomic,
|
||||||
|
int oldval,
|
||||||
|
int newval)
|
||||||
|
{
|
||||||
|
return InterlockedCompareExchange((volatile LONG *)atomic, newval, oldval) == oldval;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int
|
||||||
|
virAtomicIntAdd(volatile int *atomic,
|
||||||
|
int val)
|
||||||
|
{
|
||||||
|
return InterlockedExchangeAdd((volatile LONG *)atomic, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int
|
||||||
|
virAtomicIntAnd(volatile unsigned int *atomic,
|
||||||
|
unsigned int val)
|
||||||
|
{
|
||||||
|
return InterlockedAnd((volatile LONG *)atomic, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int
|
||||||
|
virAtomicIntOr(volatile unsigned int *atomic,
|
||||||
|
unsigned int val)
|
||||||
|
{
|
||||||
|
return InterlockedOr((volatile LONG *)atomic, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int
|
||||||
|
virAtomicIntXor(volatile unsigned int *atomic,
|
||||||
|
unsigned int val)
|
||||||
|
{
|
||||||
|
return InterlockedXor((volatile LONG *)atomic, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# else
|
||||||
|
# ifdef VIR_ATOMIC_OPS_PTHREAD
|
||||||
|
# include <pthread.h>
|
||||||
|
|
||||||
|
extern pthread_mutex_t virAtomicLock;
|
||||||
|
|
||||||
|
inline int
|
||||||
|
virAtomicIntGet(volatile int *atomic)
|
||||||
|
{
|
||||||
int value;
|
int value;
|
||||||
};
|
|
||||||
|
|
||||||
static inline int
|
pthread_mutex_lock(&virAtomicLock);
|
||||||
virAtomicIntInit(virAtomicIntPtr vaip)
|
value = *atomic;
|
||||||
{
|
pthread_mutex_unlock(&virAtomicLock);
|
||||||
vaip->value = 0;
|
|
||||||
return virMutexInit(&vaip->lock);
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
inline void
|
||||||
virAtomicIntAdd(virAtomicIntPtr vaip, int add)
|
virAtomicIntSet(volatile int *atomic,
|
||||||
|
int value)
|
||||||
{
|
{
|
||||||
int ret;
|
pthread_mutex_lock(&virAtomicLock);
|
||||||
|
*atomic = value;
|
||||||
virMutexLock(&vaip->lock);
|
pthread_mutex_unlock(&virAtomicLock);
|
||||||
|
|
||||||
vaip->value += add;
|
|
||||||
ret = vaip->value;
|
|
||||||
|
|
||||||
virMutexUnlock(&vaip->lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
inline int
|
||||||
virAtomicIntSub(virAtomicIntPtr vaip, int sub)
|
virAtomicIntInc(volatile int *atomic)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
|
|
||||||
virMutexLock(&vaip->lock);
|
|
||||||
|
|
||||||
vaip->value -= sub;
|
|
||||||
ret = vaip->value;
|
|
||||||
|
|
||||||
virMutexUnlock(&vaip->lock);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
# else /* __VIR_ATOMIC_USES_LOCK */
|
|
||||||
|
|
||||||
struct _virAtomicInt {
|
|
||||||
int value;
|
int value;
|
||||||
};
|
|
||||||
|
|
||||||
static inline int
|
pthread_mutex_lock(&virAtomicLock);
|
||||||
virAtomicIntInit(virAtomicIntPtr vaip)
|
value = ++(*atomic);
|
||||||
{
|
pthread_mutex_unlock(&virAtomicLock);
|
||||||
vaip->value = 0;
|
|
||||||
return 0;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
inline bool
|
||||||
virAtomicIntAdd(virAtomicIntPtr vaip, int add)
|
virAtomicIntDecAndTest(volatile int *atomic)
|
||||||
{
|
{
|
||||||
return __sync_add_and_fetch(&vaip->value, add);
|
bool is_zero;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&virAtomicLock);
|
||||||
|
is_zero = --(*atomic) == 0;
|
||||||
|
pthread_mutex_unlock(&virAtomicLock);
|
||||||
|
|
||||||
|
return is_zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
inline bool
|
||||||
virAtomicIntSub(virAtomicIntPtr vaip, int sub)
|
virAtomicIntCompareExchange(volatile int *atomic,
|
||||||
|
int oldval,
|
||||||
|
int newval)
|
||||||
{
|
{
|
||||||
return __sync_sub_and_fetch(&vaip->value, sub);
|
bool success;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&virAtomicLock);
|
||||||
|
|
||||||
|
if ((success = (*atomic == oldval)))
|
||||||
|
*atomic = newval;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&virAtomicLock);
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
# endif /* __VIR_ATOMIC_USES_LOCK */
|
inline int
|
||||||
|
virAtomicIntAdd(volatile int *atomic,
|
||||||
|
int val)
|
||||||
|
|
||||||
/* common operations that need no locking or build on others */
|
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
virAtomicIntSet(virAtomicIntPtr vaip, int value)
|
|
||||||
{
|
{
|
||||||
vaip->value = value;
|
int oldval;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&virAtomicLock);
|
||||||
|
oldval = *atomic;
|
||||||
|
*atomic = oldval + val;
|
||||||
|
pthread_mutex_unlock(&virAtomicLock);
|
||||||
|
|
||||||
|
return oldval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
inline unsigned int
|
||||||
virAtomicIntRead(virAtomicIntPtr vaip)
|
virAtomicIntAnd(volatile unsigned int *atomic,
|
||||||
|
unsigned int val)
|
||||||
{
|
{
|
||||||
return *(volatile int *)&vaip->value;
|
unsigned int oldval;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&virAtomicLock);
|
||||||
|
oldval = *atomic;
|
||||||
|
*atomic = oldval & val;
|
||||||
|
pthread_mutex_unlock(&virAtomicLock);
|
||||||
|
|
||||||
|
return oldval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
inline unsigned int
|
||||||
virAtomicIntInc(virAtomicIntPtr vaip)
|
virAtomicIntOr(volatile unsigned int *atomic,
|
||||||
|
unsigned int val)
|
||||||
{
|
{
|
||||||
return virAtomicIntAdd(vaip, 1);
|
unsigned int oldval;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&virAtomicLock);
|
||||||
|
oldval = *atomic;
|
||||||
|
*atomic = oldval | val;
|
||||||
|
pthread_mutex_unlock(&virAtomicLock);
|
||||||
|
|
||||||
|
return oldval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
inline unsigned int
|
||||||
virAtomicIntDec(virAtomicIntPtr vaip)
|
virAtomicIntXor(volatile unsigned int *atomic,
|
||||||
|
unsigned int val)
|
||||||
{
|
{
|
||||||
return virAtomicIntSub(vaip, 1);
|
unsigned int oldval;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&virAtomicLock);
|
||||||
|
oldval = *atomic;
|
||||||
|
*atomic = oldval ^ val;
|
||||||
|
pthread_mutex_unlock(&virAtomicLock);
|
||||||
|
|
||||||
|
return oldval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# else
|
||||||
|
# error "No atomic integer impl for this platform"
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
/* The int/unsigned int casts here ensure that you can
|
||||||
|
* pass either an int or unsigned int to all atomic op
|
||||||
|
* functions, in the same way that we can with GCC
|
||||||
|
* atomic op helpers.
|
||||||
|
*/
|
||||||
|
# define virAtomicIntGet(atomic) \
|
||||||
|
virAtomicIntGet((int *)atomic)
|
||||||
|
# define virAtomicIntSet(atomic, val) \
|
||||||
|
virAtomicIntSet((int *)atomic, val)
|
||||||
|
# define virAtomicIntInc(atomic) \
|
||||||
|
virAtomicIntInc((int *)atomic)
|
||||||
|
# define virAtomicIntDecAndTest(atomic) \
|
||||||
|
virAtomicIntDecAndTest((int *)atomic)
|
||||||
|
# define virAtomicIntCompareExchange(atomic, oldval, newval) \
|
||||||
|
virAtomicIntCompareExchange((int *)atomic, oldval, newval)
|
||||||
|
# define virAtomicIntAdd(atomic, val) \
|
||||||
|
virAtomicIntAdd((int *)atomic, val)
|
||||||
|
# define virAtomicIntAnd(atomic, val) \
|
||||||
|
virAtomicIntAnd((unsigned int *)atomic, val)
|
||||||
|
# define virAtomicIntOr(atomic, val) \
|
||||||
|
virAtomicIntOr((unsigned int *)atomic, val)
|
||||||
|
# define virAtomicIntXor(atomic, val) \
|
||||||
|
virAtomicIntXor((unsigned int *)atomic, val)
|
||||||
|
|
||||||
|
# endif
|
||||||
|
|
||||||
#endif /* __VIR_ATOMIC_H */
|
#endif /* __VIR_ATOMIC_H */
|
||||||
|
@ -618,7 +618,7 @@ cleanup:
|
|||||||
#else /* __linux__ */
|
#else /* __linux__ */
|
||||||
|
|
||||||
int virFileLoopDeviceAssociate(const char *file,
|
int virFileLoopDeviceAssociate(const char *file,
|
||||||
char **dev)
|
char **dev ATTRIBUTE_UNUSED)
|
||||||
{
|
{
|
||||||
virReportSystemError(ENOSYS,
|
virReportSystemError(ENOSYS,
|
||||||
_("Unable to associate file %s with loop device"),
|
_("Unable to associate file %s with loop device"),
|
||||||
|
@ -89,6 +89,7 @@ test_programs = virshtest sockettest \
|
|||||||
nodeinfotest virbuftest \
|
nodeinfotest virbuftest \
|
||||||
commandtest seclabeltest \
|
commandtest seclabeltest \
|
||||||
virhashtest virnetmessagetest virnetsockettest \
|
virhashtest virnetmessagetest virnetsockettest \
|
||||||
|
viratomictest \
|
||||||
utiltest virnettlscontexttest shunloadtest \
|
utiltest virnettlscontexttest shunloadtest \
|
||||||
virtimetest viruritest virkeyfiletest \
|
virtimetest viruritest virkeyfiletest \
|
||||||
virauthconfigtest
|
virauthconfigtest
|
||||||
@ -530,6 +531,10 @@ virhashtest_SOURCES = \
|
|||||||
virhashtest.c virhashdata.h testutils.h testutils.c
|
virhashtest.c virhashdata.h testutils.h testutils.c
|
||||||
virhashtest_LDADD = $(LDADDS)
|
virhashtest_LDADD = $(LDADDS)
|
||||||
|
|
||||||
|
viratomictest_SOURCES = \
|
||||||
|
viratomictest.c viratomicdata.h testutils.h testutils.c
|
||||||
|
viratomictest_LDADD = $(LDADDS)
|
||||||
|
|
||||||
jsontest_SOURCES = \
|
jsontest_SOURCES = \
|
||||||
jsontest.c testutils.h testutils.c
|
jsontest.c testutils.h testutils.c
|
||||||
jsontest_LDADD = $(LDADDS)
|
jsontest_LDADD = $(LDADDS)
|
||||||
|
181
tests/viratomictest.c
Normal file
181
tests/viratomictest.c
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2012 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* License along with this library; If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <sched.h>
|
||||||
|
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
#include "viratomic.h"
|
||||||
|
#include "virrandom.h"
|
||||||
|
#include "threads.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
testTypes(const void *data ATTRIBUTE_UNUSED)
|
||||||
|
{
|
||||||
|
unsigned int u, u2;
|
||||||
|
int s, s2;
|
||||||
|
bool res;
|
||||||
|
|
||||||
|
#define virAssertCmpInt(a, op, b) \
|
||||||
|
if (!((a) op (b))) \
|
||||||
|
return -1;
|
||||||
|
virAtomicIntSet(&u, 5);
|
||||||
|
u2 = virAtomicIntGet(&u);
|
||||||
|
virAssertCmpInt(u2, ==, 5);
|
||||||
|
|
||||||
|
res = virAtomicIntCompareExchange(&u, 6, 7);
|
||||||
|
if (res)
|
||||||
|
return -1;
|
||||||
|
virAssertCmpInt(u, ==, 5);
|
||||||
|
|
||||||
|
virAssertCmpInt(virAtomicIntAdd(&u, 1), ==, 5);
|
||||||
|
virAssertCmpInt(u, ==, 6);
|
||||||
|
|
||||||
|
virAssertCmpInt(virAtomicIntInc(&u), ==, 7);
|
||||||
|
virAssertCmpInt(u, ==, 7);
|
||||||
|
|
||||||
|
res = virAtomicIntDecAndTest(&u);
|
||||||
|
if (res)
|
||||||
|
return -1;
|
||||||
|
virAssertCmpInt(u, ==, 6);
|
||||||
|
|
||||||
|
u2 = virAtomicIntAnd(&u, 5);
|
||||||
|
virAssertCmpInt(u2, ==, 6);
|
||||||
|
virAssertCmpInt(u, ==, 4);
|
||||||
|
|
||||||
|
u2 = virAtomicIntOr(&u, 8);
|
||||||
|
virAssertCmpInt(u2, ==, 4);
|
||||||
|
virAssertCmpInt(u, ==, 12);
|
||||||
|
|
||||||
|
u2 = virAtomicIntXor(&u, 4);
|
||||||
|
virAssertCmpInt(u2, ==, 12);
|
||||||
|
virAssertCmpInt(u, ==, 8);
|
||||||
|
|
||||||
|
virAtomicIntSet(&s, 5);
|
||||||
|
s2 = virAtomicIntGet(&s);
|
||||||
|
virAssertCmpInt(s2, ==, 5);
|
||||||
|
|
||||||
|
res = virAtomicIntCompareExchange(&s, 6, 7);
|
||||||
|
if (res)
|
||||||
|
return -1;
|
||||||
|
virAssertCmpInt(s, ==, 5);
|
||||||
|
|
||||||
|
virAtomicIntAdd(&s, 1);
|
||||||
|
virAssertCmpInt(s, ==, 6);
|
||||||
|
|
||||||
|
virAtomicIntInc(&s);
|
||||||
|
virAssertCmpInt(s, ==, 7);
|
||||||
|
|
||||||
|
res = virAtomicIntDecAndTest(&s);
|
||||||
|
if (res)
|
||||||
|
return -1;
|
||||||
|
virAssertCmpInt(s, ==, 6);
|
||||||
|
|
||||||
|
s2 = virAtomicIntAnd(&s, 5);
|
||||||
|
virAssertCmpInt(s2, ==, 6);
|
||||||
|
virAssertCmpInt(s, ==, 4);
|
||||||
|
|
||||||
|
s2 = virAtomicIntOr(&s, 8);
|
||||||
|
virAssertCmpInt(s2, ==, 4);
|
||||||
|
virAssertCmpInt(s, ==, 12);
|
||||||
|
|
||||||
|
s2 = virAtomicIntXor(&s, 4);
|
||||||
|
virAssertCmpInt(s2, ==, 12);
|
||||||
|
virAssertCmpInt(s, ==, 8);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define THREADS 10
|
||||||
|
#define ROUNDS 10000
|
||||||
|
|
||||||
|
volatile int bucket[THREADS];
|
||||||
|
volatile int atomic;
|
||||||
|
|
||||||
|
static void
|
||||||
|
thread_func(void *data)
|
||||||
|
{
|
||||||
|
int idx = (int)(long)data;
|
||||||
|
int i;
|
||||||
|
int d;
|
||||||
|
|
||||||
|
for (i = 0; i < ROUNDS; i++) {
|
||||||
|
d = virRandomBits(7);
|
||||||
|
bucket[idx] += d;
|
||||||
|
virAtomicIntAdd(&atomic, d);
|
||||||
|
#ifdef WIN32
|
||||||
|
SleepEx(0,0);
|
||||||
|
#else
|
||||||
|
sched_yield();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
testThreads(const void *data ATTRIBUTE_UNUSED)
|
||||||
|
{
|
||||||
|
int sum;
|
||||||
|
int i;
|
||||||
|
virThread threads[THREADS];
|
||||||
|
|
||||||
|
atomic = 0;
|
||||||
|
for (i = 0; i < THREADS; i++)
|
||||||
|
bucket[i] = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < THREADS; i++) {
|
||||||
|
if (virThreadCreate(&(threads[i]), true, thread_func, (void*)(long)i) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < THREADS; i++)
|
||||||
|
virThreadJoin(&threads[i]);
|
||||||
|
|
||||||
|
sum = 0;
|
||||||
|
for (i = 0; i < THREADS; i++)
|
||||||
|
sum += bucket[i];
|
||||||
|
|
||||||
|
if (sum != atomic)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mymain(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (virRandomInitialize(time(NULL)) < 0)
|
||||||
|
return -1;
|
||||||
|
if (virThreadInitialize() < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (virtTestRun("types", 1, testTypes, NULL) < 0)
|
||||||
|
ret = -1;
|
||||||
|
if (virtTestRun("threads", 1, testThreads, NULL) < 0)
|
||||||
|
ret = -1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIRT_TEST_MAIN(mymain)
|
Loading…
Reference in New Issue
Block a user