From 2de940b4223f681c2960b099ebfa9b67398a3a41 Mon Sep 17 00:00:00 2001 From: Pieter Hintjens Date: Tue, 23 Dec 2014 01:14:38 +0100 Subject: [PATCH] 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. --- .gitignore | 3 ++ Makefile.am | 6 ++- doc/Makefile.am | 6 ++- doc/zmq.txt | 21 +++++++--- doc/zmq_atomic_counter_dec.txt | 61 ++++++++++++++++++++++++++++++ doc/zmq_atomic_counter_destroy.txt | 61 ++++++++++++++++++++++++++++++ doc/zmq_atomic_counter_inc.txt | 60 +++++++++++++++++++++++++++++ doc/zmq_atomic_counter_new.txt | 61 ++++++++++++++++++++++++++++++ doc/zmq_atomic_counter_set.txt | 60 +++++++++++++++++++++++++++++ doc/zmq_atomic_counter_value.txt | 59 +++++++++++++++++++++++++++++ include/zmq.h | 11 ++++++ src/zmq_utils.cpp | 49 ++++++++++++++++++++++++ tests/test_atomics.cpp | 38 +++++++++++++++++++ 13 files changed, 489 insertions(+), 7 deletions(-) create mode 100644 doc/zmq_atomic_counter_dec.txt create mode 100644 doc/zmq_atomic_counter_destroy.txt create mode 100644 doc/zmq_atomic_counter_inc.txt create mode 100644 doc/zmq_atomic_counter_new.txt create mode 100644 doc/zmq_atomic_counter_set.txt create mode 100644 doc/zmq_atomic_counter_value.txt create mode 100644 tests/test_atomics.cpp diff --git a/.gitignore b/.gitignore index f93b4281..a815b4b2 100644 --- a/.gitignore +++ b/.gitignore @@ -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* diff --git a/Makefile.am b/Makefile.am index ff183189..a221f5ae 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/doc/Makefile.am b/doc/Makefile.am index 0bc2e0e6..f19cba10 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -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 diff --git a/doc/zmq.txt b/doc/zmq.txt index 233bebc1..3d56e76b 100644 --- a/doc/zmq.txt +++ b/doc/zmq.txt @@ -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: diff --git a/doc/zmq_atomic_counter_dec.txt b/doc/zmq_atomic_counter_dec.txt new file mode 100644 index 00000000..164ba950 --- /dev/null +++ b/doc/zmq_atomic_counter_dec.txt @@ -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 . diff --git a/doc/zmq_atomic_counter_destroy.txt b/doc/zmq_atomic_counter_destroy.txt new file mode 100644 index 00000000..c58d1e44 --- /dev/null +++ b/doc/zmq_atomic_counter_destroy.txt @@ -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 . diff --git a/doc/zmq_atomic_counter_inc.txt b/doc/zmq_atomic_counter_inc.txt new file mode 100644 index 00000000..fccda5f2 --- /dev/null +++ b/doc/zmq_atomic_counter_inc.txt @@ -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 . diff --git a/doc/zmq_atomic_counter_new.txt b/doc/zmq_atomic_counter_new.txt new file mode 100644 index 00000000..38427ba4 --- /dev/null +++ b/doc/zmq_atomic_counter_new.txt @@ -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 . diff --git a/doc/zmq_atomic_counter_set.txt b/doc/zmq_atomic_counter_set.txt new file mode 100644 index 00000000..6df210e2 --- /dev/null +++ b/doc/zmq_atomic_counter_set.txt @@ -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 . diff --git a/doc/zmq_atomic_counter_value.txt b/doc/zmq_atomic_counter_value.txt new file mode 100644 index 00000000..66cd870d --- /dev/null +++ b/doc/zmq_atomic_counter_value.txt @@ -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 . diff --git a/include/zmq.h b/include/zmq.h index e06a35af..f56f838e 100644 --- a/include/zmq.h +++ b/include/zmq.h @@ -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. */ diff --git a/src/zmq_utils.cpp b/src/zmq_utils.cpp index 85e4c439..a04859d4 100644 --- a/src/zmq_utils.cpp +++ b/src/zmq_utils.cpp @@ -22,6 +22,8 @@ #include "clock.hpp" #include "err.hpp" #include "thread.hpp" +#include "atomic_counter.hpp" +#include "atomic_ptr.hpp" #include #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; +} diff --git a/tests/test_atomics.cpp b/tests/test_atomics.cpp new file mode 100644 index 00000000..a4dfc810 --- /dev/null +++ b/tests/test_atomics.cpp @@ -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 . +*/ + +#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; +}