Problem: need atomic reference counting in several projects

Solution: as libzmq already provides this across all platforms,
expose an atomic counter API. I've not wrapped atomic pointers,
though someone who needs this may want to do so.
This commit is contained in:
Pieter Hintjens 2014-12-23 01:14:38 +01:00
parent 17937ffcf0
commit 2de940b422
13 changed files with 489 additions and 7 deletions

3
.gitignore vendored
View File

@ -98,6 +98,9 @@ test_capabilities
test_hwm_pubsub
test_router_mandatory_hwm
test_xpub_nodrop
test_xpub_manual
test_xpub_welcome_msg
test_atomics
tests/test*.log
tests/test*.trs
src/platform.hpp*

View File

@ -330,7 +330,8 @@ test_apps = \
tests/test_capabilities \
tests/test_xpub_nodrop \
tests/test_xpub_manual \
tests/test_xpub_welcome_msg
tests/test_xpub_welcome_msg \
tests/test_atomics
tests_test_system_SOURCES = tests/test_system.cpp
tests_test_system_LDADD = src/libzmq.la
@ -502,6 +503,9 @@ tests_test_xpub_manual_LDADD = src/libzmq.la
tests_test_xpub_welcome_msg_SOURCES = tests/test_xpub_welcome_msg.cpp
tests_test_xpub_welcome_msg_LDADD = src/libzmq.la
tests_test_atomics_SOURCES = tests/test_atomics.cpp
tests_test_atomics_LDADD = src/libzmq.la
if !ON_MINGW
test_apps += \
tests/test_shutdown_stress \

View File

@ -13,7 +13,11 @@ MAN3 = zmq_bind.3 zmq_unbind.3 zmq_connect.3 zmq_disconnect.3 zmq_close.3 \
zmq_errno.3 zmq_strerror.3 zmq_version.3 \
zmq_sendmsg.3 zmq_recvmsg.3 \
zmq_proxy.3 zmq_proxy_steerable.3 \
zmq_z85_encode.3 zmq_z85_decode.3 zmq_curve_keypair.3 zmq_has.3
zmq_z85_encode.3 zmq_z85_decode.3 zmq_curve_keypair.3 \
zmq_has.3 \
zmq_atomic_counter_new.3 zmq_atomic_counter_set.3 \
zmq_atomic_counter_inc.3 zmq_atomic_counter_dec.3 \
zmq_atomic_counter_value.3 zmq_atomic_counter_destroy.3
MAN7 = zmq.7 zmq_tcp.7 zmq_pgm.7 zmq_inproc.7 zmq_ipc.7 \
zmq_null.7 zmq_plain.7 zmq_curve.7 zmq_tipc.7

View File

@ -132,7 +132,7 @@ Sending and receiving messages::
linkzmq:zmq_recv[3]
linkzmq:zmq_send_const[3]
Monitoring socket events:
Monitoring socket events::
linkzmq:zmq_socket_monitor[3]
.Input/output multiplexing
@ -187,13 +187,11 @@ Plain-text authentication using username and password::
Elliptic curve authentication and encryption::
linkzmq:zmq_curve[7]
Generate a CURVE keypair in armored text format:
Generate a CURVE keypair in armored text format::
linkzmq:zmq_curve_keypair[3]
Convert an armored key into a 32-byte binary key:
Converting keys to/from armoured text strings::
linkzmq:zmq_z85_decode[3]
Convert a 32-byte binary CURVE key to an armored text string:
linkzmq:zmq_z85_encode[3]
@ -213,6 +211,19 @@ The _zmq_strerror()_ function is provided to translate 0MQ-specific error codes
into error message strings; for details refer to linkzmq:zmq_strerror[3].
UTILITY
-------
The following utility functions are provided:
Working with atomic counters::
linkzmq:zmq_atomic_counter_new[3]
linkzmq:zmq_atomic_counter_set[3]
linkzmq:zmq_atomic_counter_inc[3]
linkzmq:zmq_atomic_counter_dec[3]
linkzmq:zmq_atomic_counter_value[3]
linkzmq:zmq_atomic_counter_destroy[3]
MISCELLANEOUS
-------------
The following miscellaneous functions are provided:

View File

@ -0,0 +1,61 @@
zmq_atomic_counter_dec(3)
=========================
NAME
----
zmq_atomic_counter_dec - decrement an atomic counter
SYNOPSIS
--------
*bool zmq_atomic_counter_dec (void *counter);*
DESCRIPTION
-----------
The _zmq_atomic_counter_dec_ function decrements an atomic counter in
a threadsafe fashion. This function uses platform specific atomic
operations.
RETURN VALUE
------------
The _zmq_atomic_counter_dec()_ function returns true if the counter is
greater than zero after decrementing, or false if the counter reached
zero.
EXAMPLE
-------
.Test code for atomic counters
----
void *counter = zmq_atomic_counter_new ();
assert (zmq_atomic_counter_value (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 1);
assert (zmq_atomic_counter_inc (counter) == 2);
assert (zmq_atomic_counter_value (counter) == 3);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_set (counter, 2);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_destroy (&counter);
----
SEE ALSO
--------
linkzmq:zmq_atomic_counter_new[3]
linkzmq:zmq_atomic_counter_set[3]
linkzmq:zmq_atomic_counter_inc[3]
linkzmq:zmq_atomic_counter_value[3]
linkzmq:zmq_atomic_counter_destroy[3]
AUTHORS
-------
This page was written by the 0MQ community. To make a change please
read the 0MQ Contribution Policy at <http://www.zeromq.org/docs:contributing>.

View File

@ -0,0 +1,61 @@
zmq_atomic_counter_destroy(3)
=============================
NAME
----
zmq_atomic_counter_destroy - destroy an atomic counter
SYNOPSIS
--------
*void zmq_atomic_counter_destroy (void **counter_p);*
DESCRIPTION
-----------
The _zmq_atomic_counter_destroy_ function destroys an atomic counter and
nullifies its reference. Pass the address of an atomic counter (void **)
rather than the counter itself. You must destroy all counters that you
create, to avoid memory leakage. This function uses platform specific
atomic operations.
RETURN VALUE
------------
The _zmq_atomic_counter_destroy()_ function has no return value.
EXAMPLE
-------
.Test code for atomic counters
----
void *counter = zmq_atomic_counter_new ();
assert (zmq_atomic_counter_value (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 1);
assert (zmq_atomic_counter_inc (counter) == 2);
assert (zmq_atomic_counter_value (counter) == 3);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_set (counter, 2);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_destroy (&counter);
----
SEE ALSO
--------
linkzmq:zmq_atomic_counter_new[3]
linkzmq:zmq_atomic_counter_set[3]
linkzmq:zmq_atomic_counter_inc[3]
linkzmq:zmq_atomic_counter_dec[3]
linkzmq:zmq_atomic_counter_value[3]
AUTHORS
-------
This page was written by the 0MQ community. To make a change please
read the 0MQ Contribution Policy at <http://www.zeromq.org/docs:contributing>.

View File

@ -0,0 +1,60 @@
zmq_atomic_counter_inc(3)
=========================
NAME
----
zmq_atomic_counter_inc - increment an atomic counter
SYNOPSIS
--------
*int zmq_atomic_counter_inc (void *counter);*
DESCRIPTION
-----------
The _zmq_atomic_counter_inc_ function increments an atomic counter in a
threadsafe fashion. This function uses platform specific atomic
operations.
RETURN VALUE
------------
The _zmq_atomic_counter_inc()_ function returns the old value of the
counter, before incrementing.
EXAMPLE
-------
.Test code for atomic counters
----
void *counter = zmq_atomic_counter_new ();
assert (zmq_atomic_counter_value (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 1);
assert (zmq_atomic_counter_inc (counter) == 2);
assert (zmq_atomic_counter_value (counter) == 3);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_set (counter, 2);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_destroy (&counter);
----
SEE ALSO
--------
linkzmq:zmq_atomic_counter_new[3]
linkzmq:zmq_atomic_counter_set[3]
linkzmq:zmq_atomic_counter_dec[3]
linkzmq:zmq_atomic_counter_value[3]
linkzmq:zmq_atomic_counter_destroy[3]
AUTHORS
-------
This page was written by the 0MQ community. To make a change please
read the 0MQ Contribution Policy at <http://www.zeromq.org/docs:contributing>.

View File

@ -0,0 +1,61 @@
zmq_atomic_counter_new(3)
=========================
NAME
----
zmq_atomic_counter_new - create a new atomic counter
SYNOPSIS
--------
*void *zmq_atomic_counter_new (void);*
DESCRIPTION
-----------
The _zmq_atomic_counter_new_ function creates a new atomic counter. You
can use this in multithreaded applications to do, for example, reference
counting of shared objects. The atomic counter is at least 32 bits large.
This function uses platform specific atomic operations.
RETURN VALUE
------------
The _zmq_atomic_counter_new()_ function returns the new atomic counter
if successful. Otherwise it returns NULL.
EXAMPLE
-------
.Test code for atomic counters
----
void *counter = zmq_atomic_counter_new ();
assert (zmq_atomic_counter_value (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 1);
assert (zmq_atomic_counter_inc (counter) == 2);
assert (zmq_atomic_counter_value (counter) == 3);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_set (counter, 2);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_destroy (&counter);
----
SEE ALSO
--------
linkzmq:zmq_atomic_counter_set[3]
linkzmq:zmq_atomic_counter_inc[3]
linkzmq:zmq_atomic_counter_dec[3]
linkzmq:zmq_atomic_counter_value[3]
linkzmq:zmq_atomic_counter_destroy[3]
AUTHORS
-------
This page was written by the 0MQ community. To make a change please
read the 0MQ Contribution Policy at <http://www.zeromq.org/docs:contributing>.

View File

@ -0,0 +1,60 @@
zmq_atomic_counter_set(3)
=========================
NAME
----
zmq_atomic_counter_set - set atomic counter to new value
SYNOPSIS
--------
*void zmq_atomic_counter_set (void *counter, int value);*
DESCRIPTION
-----------
The _zmq_atomic_counter_set_ function sets the counter to a new value,
in a threadsafe fashion. The largest value that is guaranteed to work
across all platforms is 2^31-1. This function uses platform specific
atomic operations.
RETURN VALUE
------------
The _zmq_atomic_counter_set()_ function has no return value.
EXAMPLE
-------
.Test code for atomic counters
----
void *counter = zmq_atomic_counter_new ();
assert (zmq_atomic_counter_value (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 1);
assert (zmq_atomic_counter_inc (counter) == 2);
assert (zmq_atomic_counter_value (counter) == 3);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_set (counter, 2);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_destroy (&counter);
----
SEE ALSO
--------
linkzmq:zmq_atomic_counter_new[3]
linkzmq:zmq_atomic_counter_inc[3]
linkzmq:zmq_atomic_counter_dec[3]
linkzmq:zmq_atomic_counter_value[3]
linkzmq:zmq_atomic_counter_destroy[3]
AUTHORS
-------
This page was written by the 0MQ community. To make a change please
read the 0MQ Contribution Policy at <http://www.zeromq.org/docs:contributing>.

View File

@ -0,0 +1,59 @@
zmq_atomic_counter_value(3)
===========================
NAME
----
zmq_atomic_counter_value - return value of atomic counter
SYNOPSIS
--------
*int zmq_atomic_counter_value (void *counter);*
DESCRIPTION
-----------
The _zmq_atomic_counter_value_ function returns the value of an atomic
counter. This function uses platform specific atomic operations.
RETURN VALUE
------------
The _zmq_atomic_counter_value()_ function returns the new atomic counter
if successful. Otherwise it returns NULL.
EXAMPLE
-------
.Test code for atomic counters
----
void *counter = zmq_atomic_counter_new ();
assert (zmq_atomic_counter_value (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 1);
assert (zmq_atomic_counter_inc (counter) == 2);
assert (zmq_atomic_counter_value (counter) == 3);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_set (counter, 2);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_destroy (&counter);
----
SEE ALSO
--------
linkzmq:zmq_atomic_counter_new[3]
linkzmq:zmq_atomic_counter_set[3]
linkzmq:zmq_atomic_counter_inc[3]
linkzmq:zmq_atomic_counter_dec[3]
linkzmq:zmq_atomic_counter_destroy[3]
AUTHORS
-------
This page was written by the 0MQ community. To make a change please
read the 0MQ Contribution Policy at <http://www.zeromq.org/docs:contributing>.

View File

@ -433,6 +433,17 @@ ZMQ_EXPORT uint8_t *zmq_z85_decode (uint8_t *dest, const char *string);
/* Returns 0 on success. */
ZMQ_EXPORT int zmq_curve_keypair (char *z85_public_key, char *z85_secret_key);
/******************************************************************************/
/* Atomic utility methods */
/******************************************************************************/
ZMQ_EXPORT void *zmq_atomic_counter_new (void);
ZMQ_EXPORT void zmq_atomic_counter_set (void *counter, int value);
ZMQ_EXPORT int zmq_atomic_counter_inc (void *counter);
ZMQ_EXPORT bool zmq_atomic_counter_dec (void *counter);
ZMQ_EXPORT int zmq_atomic_counter_value (void *counter);
ZMQ_EXPORT void zmq_atomic_counter_destroy (void **counter_p);
/******************************************************************************/
/* These functions are not documented by man pages -- use at your own risk. */

View File

@ -22,6 +22,8 @@
#include "clock.hpp"
#include "err.hpp"
#include "thread.hpp"
#include "atomic_counter.hpp"
#include "atomic_ptr.hpp"
#include <assert.h>
#include "../include/zmq_utils.h"
@ -204,3 +206,50 @@ int zmq_curve_keypair (char *z85_public_key, char *z85_secret_key)
return -1;
#endif
}
// --------------------------------------------------------------------------
// Initialize a new atomic counter, which is set to zero
void *zmq_atomic_counter_new (void)
{
zmq::atomic_counter_t *counter = new zmq::atomic_counter_t;
alloc_assert (counter);
return counter;
}
// Se the value of the atomic counter
void zmq_atomic_counter_set (void *counter_, int value_)
{
((zmq::atomic_counter_t *) counter_)->set (value_);
}
// Increment the atomic counter, and return the old value
int zmq_atomic_counter_inc (void *counter_)
{
return ((zmq::atomic_counter_t *) counter_)->add (1);
}
// Decrement the atomic counter and return true if still > zero
bool zmq_atomic_counter_dec (void *counter_)
{
return ((zmq::atomic_counter_t *) counter_)->sub (1);
}
// Return actual value of atomic counter
int zmq_atomic_counter_value (void *counter_)
{
return ((zmq::atomic_counter_t *) counter_)->get ();
}
// Destroy atomic counter, and set reference to NULL
void zmq_atomic_counter_destroy (void **counter_p_)
{
delete ((zmq::atomic_counter_t *) *counter_p_);
*counter_p_ = NULL;
}

38
tests/test_atomics.cpp Normal file
View File

@ -0,0 +1,38 @@
/*
Copyright (c) 2007-2014 Contributors as noted in the AUTHORS file
This file is part of 0MQ.
0MQ 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 3 of the License, or
(at your option) any later version.
0MQ 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 program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "testutil.hpp"
int main (void)
{
void *counter = zmq_atomic_counter_new ();
assert (zmq_atomic_counter_value (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 0);
assert (zmq_atomic_counter_inc (counter) == 1);
assert (zmq_atomic_counter_inc (counter) == 2);
assert (zmq_atomic_counter_value (counter) == 3);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_set (counter, 2);
assert (zmq_atomic_counter_dec (counter) == true);
assert (zmq_atomic_counter_dec (counter) == false);
zmq_atomic_counter_destroy (&counter);
return 0;
}