Thanks to all who provided feedback on the apr_atomic API
changes that I posted last week. Here's the updated patch
that I'm planning to commit; I'll wait a few days in case
more people want to comment.
Things that have changed since the original patch:
- Added a new function apr_atomic_xchg32 to exchange
two values
- Documented that the atomic functions will implement
a memory barrier internally
Changes not yet made:
- Removal of the old API (I saw two comments in favor
of this, including my own; I'll wait for an official
3 +1s and no -1s before I start deleting stuff)
- Return values for the add, subtract, and inc functions
(I haven't figured out a way to implement these
efficiently)
Brian
Index: include/apr_atomic.h
===================================================================
RCS file: /home/cvs/apr/include/apr_atomic.h,v
retrieving revision 1.52
diff -u -r1.52 apr_atomic.h
--- include/apr_atomic.h 14 Sep 2003 02:56:52 -0000 1.52
+++ include/apr_atomic.h 19 Sep 2003 04:36:17 -0000
@@ -104,24 +104,28 @@
* @param mem the pointer
* @warning on certain platforms this number is not stored
* directly in the pointer. in others it is
+ * @deprecated
*/
apr_uint32_t apr_atomic_read(volatile apr_atomic_t *mem);
/**
* set the value for atomic.
* @param mem the pointer
* @param val the value
+ * @deprecated
*/
void apr_atomic_set(volatile apr_atomic_t *mem, apr_uint32_t val);
/**
* Add 'val' to the atomic variable
* @param mem pointer to the atomic value
* @param val the addition
+ * @deprecated
*/
void apr_atomic_add(volatile apr_atomic_t *mem, apr_uint32_t val);
/**
* increment the atomic variable by 1
* @param mem pointer to the atomic value
+ * @deprecated
*/
void apr_atomic_inc(volatile apr_atomic_t *mem);
@@ -129,6 +133,7 @@
* decrement the atomic variable by 1
* @param mem pointer to the atomic value
* @return zero if the value is zero, otherwise non-zero
+ * @deprecated
*/
int apr_atomic_dec(volatile apr_atomic_t *mem);
@@ -140,10 +145,76 @@
* @param cmp the value to compare it to
* @return the old value of the atomic
* @warning do not mix apr_atomic's with the CAS function.
+ * @deprecated
* on some platforms they may be implemented by different mechanisms
*/
apr_uint32_t apr_atomic_cas(volatile apr_uint32_t *mem, long with, long cmp);
+
+/*
+ * Atomic operations on 32-bit values
+ * Note: Each of these functions internally implements a memory barrier
+ * on platforms that require it
+ */
+
+/**
+ * atomically read an apr_uint32_t from memory
+ * @param mem the pointer
+ */
+apr_uint32_t apr_atomic_read32(volatile apr_uint32_t *mem);
+
+/**
+ * atomically set an apr_uint32_t in memory
+ * @param mem pointer to the object
+ */
+void apr_atomic_set32(volatile apr_uint32_t *mem, apr_uint32_t val);
+
+/**
+ * atomically add 'val' to an apr_uint32_t
+ * @param mem pointer to the object
+ * @param val amount to add
+ */
+void apr_atomic_add32(volatile apr_uint32_t *mem, apr_uint32_t val);
+
+/**
+ * atomically subtract 'val' from an apr_uint32_t
+ * @param mem pointer to the object
+ * @param val amount to subtract
+ */
+void apr_atomic_sub32(volatile apr_uint32_t *mem, apr_uint32_t val);
+
+/**
+ * atomically increment an apr_uint32_t by 1
+ * @param mem pointer to the object
+ */
+void apr_atomic_inc32(volatile apr_uint32_t *mem);
+
+/**
+ * atomically decrement an apr_uint32_t by 1
+ * @param mem pointer to the atomic value
+ * @return zero if the value becomes zero on decrement, otherwise non-zero
+ */
+int apr_atomic_dec32(volatile apr_uint32_t *mem);
+
+/**
+ * compare an apr_uint32_t's value with 'cmp'.
+ * If they are the same swap the value with 'with'
+ * @param mem pointer to the value
+ * @param with what to swap it with
+ * @param cmp the value to compare it to
+ * @return the old value of *mem
+ */
+apr_uint32_t apr_atomic_cas32(volatile apr_uint32_t *mem, apr_uint32_t with,
+ apr_uint32_t cmp);
+
+/**
+ * exchange an apr_uint32_t's value with 'val'.
+ * @param mem pointer to the value
+ * @param val what to swap it with
+ * @return the old value of *mem
+ */
+apr_uint32_t apr_atomic_xchg32(volatile apr_uint32_t *mem, apr_uint32_t val);
+
/**
* compare the pointer's value with cmp.
* If they are the same swap the value with 'with'
@@ -172,7 +243,7 @@
#define apr_atomic_set(mem, val) InterlockedExchange(mem, val)
#define apr_atomic_read(mem) (*mem)
#define apr_atomic_cas(mem,with,cmp) InterlockedCompareExchange(mem,with,cmp)
-#define apr_atomic_init(pool) APR_SUCCESS
+ /*#define apr_atomic_init(pool) APR_SUCCESS*/
#define apr_atomic_casptr(mem,with,cmp)
InterlockedCompareExchangePointer(mem,with,cmp)
#elif defined(NETWARE)
@@ -183,7 +254,7 @@
#define apr_atomic_inc(mem) atomic_inc(mem)
#define apr_atomic_set(mem, val) (*mem = val)
#define apr_atomic_read(mem) (*mem)
-#define apr_atomic_init(pool) APR_SUCCESS
+ /*#define apr_atomic_init(pool) APR_SUCCESS*/
#define apr_atomic_cas(mem,with,cmp) atomic_cmpxchg((unsigned long
*)(mem),(unsigned long)(cmp),(unsigned long)(with))
int apr_atomic_dec(apr_atomic_t *mem);
@@ -211,6 +282,12 @@
#define apr_atomic_set(mem, val) atomic_set_int(mem, val)
#define apr_atomic_read(mem) (*mem)
+#define apr_atomic_add32(mem, val) apr_atomic_add(mem, val)
+#define apr_atomic_dec32(mem) apr_atomic_dec(mem)
+#define apr_atomic_inc32(mem) apr_atomic_inc(mem)
+#define apr_atomic_set32(mem) apr_atomic_set(mem)
+#define apr_atomic_read32(mem) apr_atomic_read(mem)
+
#elif (defined(__linux__) || defined(__EMX__)) && defined(__i386__) &&
!APR_FORCE_ATOMIC_GENERIC
#define apr_atomic_t apr_uint32_t
@@ -228,6 +305,12 @@
: "m" (*(mem)), "r" (val) \
: "memory")
+#define apr_atomic_sub(mem, val) \
+ asm volatile ("lock; subl %1, %0" \
+ : \
+ : "m" (*(mem)), "r" (val) \
+ : "memory")
+
#define apr_atomic_dec(mem) \
({ int prev; \
asm volatile ("mov $0, %%eax;\n\t" \
@@ -247,7 +330,16 @@
#define apr_atomic_set(mem, val) (*(mem) = val)
#define apr_atomic_read(mem) (*(mem))
-#define apr_atomic_init(pool) APR_SUCCESS
+
+#define apr_atomic_cas32(mem, with, cmp) apr_atomic_cas(mem, with, cmp)
+#define apr_atomic_add32(mem, val) apr_atomic_add(mem, val)
+#define apr_atomic_sub32(mem, val) apr_atomic_sub(mem, val)
+#define apr_atomic_dec32(mem) apr_atomic_dec(mem)
+#define apr_atomic_inc32(mem) apr_atomic_inc(mem)
+#define apr_atomic_set32(mem, val) apr_atomic_set(mem, val)
+#define apr_atomic_read32(mem) apr_atomic_read(mem)
+
+/*#define apr_atomic_init(pool) APR_SUCCESS*/
#elif defined(__MVS__) /* OS/390 */
@@ -261,7 +353,7 @@
#define apr_atomic_inc(mem) apr_atomic_add(mem, 1)
#define apr_atomic_dec(mem) apr_atomic_add(mem, -1)
-#define apr_atomic_init(pool) APR_SUCCESS
+/*#define apr_atomic_init(pool) APR_SUCCESS*/
/* warning: the following two operations, _read and _set, are atomic
* if the memory variables are aligned (the usual case).
@@ -322,6 +414,41 @@
#if !defined(apr_atomic_cas) && !defined(APR_OVERRIDE_ATOMIC_CAS)
apr_uint32_t apr_atomic_cas(volatile apr_uint32_t *mem,long with,long cmp);
+#define APR_ATOMIC_NEED_DEFAULT_INIT 1
+#endif
+
+#if !defined(apr_atomic_read32) && !defined(APR_OVERRIDE_ATOMIC_READ32)
+#define apr_atomic_read32(p) *p
+#endif
+
+#if !defined(apr_atomic_set32) && !defined(APR_OVERRIDE_ATOMIC_SET32)
+void apr_atomic_set32(volatile apr_uint32_t *mem, apr_uint32_t val);
+#define APR_ATOMIC_NEED_DEFAULT_INIT 1
+#endif
+
+#if !defined(apr_atomic_add32) && !defined(APR_OVERRIDE_ATOMIC_ADD32)
+void apr_atomic_add32(volatile apr_uint32_t *mem, apr_uint32_t val);
+#define APR_ATOMIC_NEED_DEFAULT_INIT 1
+#endif
+
+#if !defined(apr_atomic_inc32) && !defined(APR_OVERRIDE_ATOMIC_INC32)
+void apr_atomic_inc32(volatile apr_uint32_t *mem);
+#define APR_ATOMIC_NEED_DEFAULT_INIT 1
+#endif
+
+#if !defined(apr_atomic_dec32) && !defined(APR_OVERRIDE_ATOMIC_DEC32)
+int apr_atomic_dec32(volatile apr_uint32_t *mem);
+#define APR_ATOMIC_NEED_DEFAULT_INIT 1
+#endif
+
+#if !defined(apr_atomic_cas32) && !defined(APR_OVERRIDE_ATOMIC_CAS32)
+apr_uint32_t apr_atomic_cas32(volatile apr_uint32_t *mem, apr_uint32_t with,
+ apr_uint32_t cmp);
+#define APR_ATOMIC_NEED_DEFAULT_INIT 1
+#endif
+
+#if !defined(apr_atomic_xchg32) && !defined(APR_OVERRIDE_ATOMIC_XCHG32)
+apr_uint32_t apr_atomic_xchg32(volatile apr_uint32_t *mem, apr_uint32_t val);
#define APR_ATOMIC_NEED_DEFAULT_INIT 1
#endif
Index: atomic/unix/apr_atomic.c
===================================================================
RCS file: /home/cvs/apr/atomic/unix/apr_atomic.c,v
retrieving revision 1.22
diff -u -r1.22 apr_atomic.c
--- atomic/unix/apr_atomic.c 1 Jan 2003 00:01:41 -0000 1.22
+++ atomic/unix/apr_atomic.c 19 Sep 2003 04:36:17 -0000
@@ -184,6 +184,139 @@
}
#endif /*!defined(apr_atomic_cas) && !defined(APR_OVERRIDE_ATOMIC_CAS) */
+#if !defined(apr_atomic_add32) && !defined(APR_OVERRIDE_ATOMIC_ADD32)
+void apr_atomic_add32(volatile apr_uint32_t *mem, apr_uint32_t val)
+{
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *lock = hash_mutex[ATOMIC_HASH(mem)];
+
+ if (apr_thread_mutex_lock(lock) == APR_SUCCESS) {
+ *mem += val;
+ apr_thread_mutex_unlock(lock);
+ }
+#else
+ *mem += val;
+#endif /* APR_HAS_THREADS */
+}
+#endif /*!defined(apr_atomic_sub32) && !defined(APR_OVERRIDE_ATOMIC_SUB32) */
+
+#if !defined(apr_atomic_sub32) && !defined(APR_OVERRIDE_ATOMIC_SUB32)
+void apr_atomic_sub32(volatile apr_uint32_t *mem, apr_uint32_t val)
+{
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *lock = hash_mutex[ATOMIC_HASH(mem)];
+
+ if (apr_thread_mutex_lock(lock) == APR_SUCCESS) {
+ *mem -= val;
+ apr_thread_mutex_unlock(lock);
+ }
+#else
+ *mem -= val;
+#endif /* APR_HAS_THREADS */
+}
+#endif /*!defined(apr_atomic_sub32) && !defined(APR_OVERRIDE_ATOMIC_SUB32) */
+
+#if !defined(apr_atomic_set32) && !defined(APR_OVERRIDE_ATOMIC_SET32)
+void apr_atomic_set32(volatile apr_uint32_t *mem, apr_uint32_t val)
+{
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *lock = hash_mutex[ATOMIC_HASH(mem)];
+
+ if (apr_thread_mutex_lock(lock) == APR_SUCCESS) {
+ *mem = val;
+ apr_thread_mutex_unlock(lock);
+ }
+#else
+ *mem = val;
+#endif /* APR_HAS_THREADS */
+}
+#endif /*!defined(apr_atomic_set32) && !defined(APR_OVERRIDE_ATOMIC_SET32) */
+
+#if !defined(apr_atomic_inc32) && !defined(APR_OVERRIDE_ATOMIC_INC32)
+void apr_atomic_inc32(volatile apr_uint32_t *mem)
+{
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *lock = hash_mutex[ATOMIC_HASH(mem)];
+
+ if (apr_thread_mutex_lock(lock) == APR_SUCCESS) {
+ (*mem)++;
+ apr_thread_mutex_unlock(lock);
+ }
+#else
+ (*mem)++;
+#endif /* APR_HAS_THREADS */
+}
+#endif /*!defined(apr_atomic_inc32) && !defined(APR_OVERRIDE_ATOMIC_INC32) */
+
+#if !defined(apr_atomic_dec32) && !defined(APR_OVERRIDE_ATOMIC_DEC32)
+int apr_atomic_dec32(volatile apr_uint32_t *mem)
+{
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *lock = hash_mutex[ATOMIC_HASH(mem)];
+ apr_uint32_t new;
+
+ if (apr_thread_mutex_lock(lock) == APR_SUCCESS) {
+ (*mem)--;
+ new = *mem;
+ apr_thread_mutex_unlock(lock);
+ return new;
+ }
+#else
+ (*mem)--;
+#endif /* APR_HAS_THREADS */
+ return *mem;
+}
+#endif /*!defined(apr_atomic_dec32) && !defined(APR_OVERRIDE_ATOMIC_DEC32) */
+
+#if !defined(apr_atomic_cas32) && !defined(APR_OVERRIDE_ATOMIC_CAS32)
+apr_uint32_t apr_atomic_cas32(volatile apr_uint32_t *mem, apr_uint32_t with,
+ apr_uint32_t cmp)
+{
+ apr_uint32_t prev;
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *lock = hash_mutex[ATOMIC_HASH(mem)];
+
+ if (apr_thread_mutex_lock(lock) == APR_SUCCESS) {
+ prev = *mem;
+ if (prev == cmp) {
+ *mem = with;
+ }
+ apr_thread_mutex_unlock(lock);
+ return prev;
+ }
+ return *mem;
+#else
+ prev = *mem;
+ if (prev == cmp) {
+ *mem = with;
+ }
+ return prev;
+#endif /* APR_HAS_THREADS */
+}
+#endif /*!defined(apr_atomic_cas32) && !defined(APR_OVERRIDE_ATOMIC_CAS32) */
+
+#if !defined(apr_atomic_xchg32) && !defined(APR_OVERRIDE_ATOMIC_XCHG32)
+apr_uint32_t apr_atomic_xchg32(volatile apr_uint32_t *mem, apr_uint32_t val)
+{
+ apr_uint32_t prev;
+#if APR_HAS_THREADS
+ apr_thread_mutex_t *lock = hash_mutex[ATOMIC_HASH(mem)];
+
+ if (apr_thread_mutex_lock(lock) == APR_SUCCESS) {
+ prev = *mem;
+ *mem = val;
+ apr_thread_mutex_unlock(lock);
+ return val;
+ }
+ return *mem;
+#else
+ prev = *mem;
+ *mem = val;
+ return prev;
+#endif /* APR_HAS_THREADS */
+}
+#endif /*!defined(apr_atomic_xchg32) && !defined(APR_OVERRIDE_ATOMIC_XCHG32) */
+
#if !defined(apr_atomic_casptr) && !defined(APR_OVERRIDE_ATOMIC_CASPTR)
void *apr_atomic_casptr(volatile void **mem, void *with, const void *cmp)
{
@@ -208,4 +341,4 @@
return prev;
#endif /* APR_HAS_THREADS */
}
-#endif /*!defined(apr_atomic_cas) && !defined(APR_OVERRIDE_ATOMIC_CAS) */
+#endif /*!defined(apr_atomic_casptr) && !defined(APR_OVERRIDE_ATOMIC_CASPTR) */
Index: test/testatomic.c
===================================================================
RCS file: /home/cvs/apr/test/testatomic.c,v
retrieving revision 1.28
diff -u -r1.28 testatomic.c
--- test/testatomic.c 19 Sep 2003 04:15:52 -0000 1.28
+++ test/testatomic.c 19 Sep 2003 04:36:17 -0000
@@ -70,6 +70,7 @@
apr_pool_t *context;
apr_atomic_t y; /* atomic locks */
+apr_uint32_t y32;
static apr_status_t check_basic_atomics()
{
@@ -164,6 +165,75 @@
return APR_SUCCESS;
}
+static apr_status_t check_basic_atomics32()
+{
+ apr_uint32_t oldval;
+ apr_uint32_t casval = 0;
+
+ apr_atomic_set32(&y32, 2);
+ printf("%-60s", "testing apr_atomic_dec32");
+ if (apr_atomic_dec32(&y32) == 0) {
+ fprintf(stderr, "Failed\noldval =%d should not be zero\n",
+ apr_atomic_read32(&y32));
+ return APR_EGENERAL;
+ }
+ if (apr_atomic_dec32(&y32) != 0) {
+ fprintf(stderr, "Failed\noldval =%d should be zero\n",
+ apr_atomic_read32(&y32));
+ return APR_EGENERAL;
+ }
+ printf("OK\n");
+
+ printf("%-60s", "testing apr_atomic_cas32");
+ oldval = apr_atomic_cas32(&casval, 12, 0);
+ if (oldval != 0) {
+ fprintf(stderr, "Failed\noldval =%d should be zero\n", oldval);
+ return APR_EGENERAL;
+ }
+ printf("OK\n");
+ printf("%-60s", "testing apr_atomic_cas32 - match non-null");
+ oldval = apr_atomic_cas32(&casval, 23, 12);
+ if (oldval != 12) {
+ fprintf(stderr, "Failed\noldval =%d should be 12 y=%d\n",
+ oldval, casval);
+ return APR_EGENERAL;
+ }
+ printf("OK\n");
+ printf("%-60s", "testing apr_atomic_cas32 - no match");
+ oldval = apr_atomic_cas32(&casval, 23, 12);
+ if (oldval != 23) {
+ fprintf(stderr, "Failed\noldval =%d should be 23 y=%d\n",
+ oldval, casval);
+ return APR_EGENERAL;
+ }
+ printf("OK\n");
+
+ printf("%-60s", "testing apr_atomic_add32");
+ apr_atomic_set32(&y32, 23);
+ apr_atomic_add32(&y32, 4);
+ if ((oldval = apr_atomic_read32(&y32)) != 27) {
+ fprintf(stderr,
+ "Failed\nAtomic Add doesn't add up ;( expected 27 got %d\n",
+ oldval);
+ return APR_EGENERAL;
+ }
+
+ printf("OK\n");
+ printf("%-60s", "testing add32/inc32/sub32");
+ apr_atomic_set32(&y32, 0);
+ apr_atomic_add32(&y32, 20);
+ apr_atomic_inc32(&y32);
+ apr_atomic_sub32(&y32, 10);
+ if (apr_atomic_read32(&y32) != 11) {
+ fprintf(stderr, "Failed.\natomics do not add up: expected 11 got %d\n",
+ apr_atomic_read32(&y32));
+ return APR_EGENERAL;
+ }
+ fprintf(stdout, "OK\n");
+
+ return APR_SUCCESS;
+}
+
#if !APR_HAS_THREADS
int main(void)
{
@@ -189,6 +259,12 @@
fprintf(stderr, "Failed.\n");
exit(-1);
}
+
+ rv = check_basic_atomics32();
+ if (rv != APR_SUCCESS) {
+ fprintf(stderr, "Failed.\n");
+ exit(-1);
+ }
return 0;
}
#else /* !APR_HAS_THREADS */
@@ -301,6 +377,13 @@
exit(-1);
}
apr_atomic_set(&y, 0);
+
+ rv = check_basic_atomics32();
+ if (rv != APR_SUCCESS) {
+ fprintf(stderr, "Failed.\n");
+ exit(-1);
+ }
+
printf("%-60s", "Starting all the threads");
for (i = 0; i < NUM_THREADS; i++) {