Implement ticker.

Implement ticker, which provides a simple API for ticking off some
number of events before indicating that the ticker has hit its limit.
diff --git a/Makefile.in b/Makefile.in
index e314a6f..f3c2e4b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -85,8 +85,8 @@
 	$(srcroot)src/extent.c $(srcroot)src/hash.c $(srcroot)src/huge.c \
 	$(srcroot)src/mb.c $(srcroot)src/mutex.c $(srcroot)src/pages.c \
 	$(srcroot)src/prof.c $(srcroot)src/quarantine.c $(srcroot)src/rtree.c \
-	$(srcroot)src/stats.c $(srcroot)src/tcache.c $(srcroot)src/time.c \
-	$(srcroot)src/tsd.c $(srcroot)src/util.c
+	$(srcroot)src/stats.c $(srcroot)src/tcache.c $(srcroot)src/ticker.c \
+	$(srcroot)src/time.c $(srcroot)src/tsd.c $(srcroot)src/util.c
 ifeq ($(enable_valgrind), 1)
 C_SRCS += $(srcroot)src/valgrind.c
 endif
@@ -143,6 +143,7 @@
 	$(srcroot)test/unit/SFMT.c \
 	$(srcroot)test/unit/size_classes.c \
 	$(srcroot)test/unit/stats.c \
+	$(srcroot)test/unit/ticker.c \
 	$(srcroot)test/unit/time.c \
 	$(srcroot)test/unit/tsd.c \
 	$(srcroot)test/unit/util.c \
diff --git a/include/jemalloc/internal/jemalloc_internal.h.in b/include/jemalloc/internal/jemalloc_internal.h.in
index e7bc4c8..12d51be 100644
--- a/include/jemalloc/internal/jemalloc_internal.h.in
+++ b/include/jemalloc/internal/jemalloc_internal.h.in
@@ -361,6 +361,7 @@
 #include "jemalloc/internal/util.h"
 #include "jemalloc/internal/atomic.h"
 #include "jemalloc/internal/prng.h"
+#include "jemalloc/internal/ticker.h"
 #include "jemalloc/internal/ckh.h"
 #include "jemalloc/internal/size_classes.h"
 #include "jemalloc/internal/stats.h"
@@ -390,6 +391,7 @@
 #include "jemalloc/internal/util.h"
 #include "jemalloc/internal/atomic.h"
 #include "jemalloc/internal/prng.h"
+#include "jemalloc/internal/ticker.h"
 #include "jemalloc/internal/ckh.h"
 #include "jemalloc/internal/size_classes.h"
 #include "jemalloc/internal/stats.h"
@@ -476,6 +478,7 @@
 #include "jemalloc/internal/util.h"
 #include "jemalloc/internal/atomic.h"
 #include "jemalloc/internal/prng.h"
+#include "jemalloc/internal/ticker.h"
 #include "jemalloc/internal/ckh.h"
 #include "jemalloc/internal/size_classes.h"
 #include "jemalloc/internal/stats.h"
@@ -505,6 +508,7 @@
 #include "jemalloc/internal/util.h"
 #include "jemalloc/internal/atomic.h"
 #include "jemalloc/internal/prng.h"
+#include "jemalloc/internal/ticker.h"
 #include "jemalloc/internal/ckh.h"
 #include "jemalloc/internal/size_classes.h"
 #include "jemalloc/internal/stats.h"
diff --git a/include/jemalloc/internal/private_symbols.txt b/include/jemalloc/internal/private_symbols.txt
index 4c40af6..216367e 100644
--- a/include/jemalloc/internal/private_symbols.txt
+++ b/include/jemalloc/internal/private_symbols.txt
@@ -460,8 +460,11 @@
 tcache_stats_merge
 thread_allocated_cleanup
 thread_deallocated_cleanup
+ticker_copy
 ticker_init
+ticker_read
 ticker_tick
+ticker_ticks
 time_add
 time_compare
 time_copy
diff --git a/include/jemalloc/internal/ticker.h b/include/jemalloc/internal/ticker.h
new file mode 100644
index 0000000..4696e56
--- /dev/null
+++ b/include/jemalloc/internal/ticker.h
@@ -0,0 +1,75 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+typedef struct ticker_s ticker_t;
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+struct ticker_s {
+	int32_t	tick;
+	int32_t	nticks;
+};
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#ifndef JEMALLOC_ENABLE_INLINE
+void	ticker_init(ticker_t *ticker, int32_t nticks);
+void	ticker_copy(ticker_t *ticker, const ticker_t *other);
+int32_t	ticker_read(const ticker_t *ticker);
+bool	ticker_ticks(ticker_t *ticker, int32_t nticks);
+bool	ticker_tick(ticker_t *ticker);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TICKER_C_))
+JEMALLOC_INLINE void
+ticker_init(ticker_t *ticker, int32_t nticks)
+{
+
+	ticker->tick = nticks;
+	ticker->nticks = nticks;
+}
+
+JEMALLOC_INLINE void
+ticker_copy(ticker_t *ticker, const ticker_t *other)
+{
+
+	*ticker = *other;
+}
+
+JEMALLOC_INLINE int32_t
+ticker_read(const ticker_t *ticker)
+{
+
+	return (ticker->tick);
+}
+
+JEMALLOC_INLINE bool
+ticker_ticks(ticker_t *ticker, int32_t nticks)
+{
+
+	if (unlikely(ticker->tick < nticks)) {
+		ticker->tick = ticker->nticks;
+		return (true);
+	}
+	ticker->tick -= nticks;
+	return(false);
+}
+
+JEMALLOC_INLINE bool
+ticker_tick(ticker_t *ticker)
+{
+
+	return (ticker_ticks(ticker, 1));
+}
+#endif
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
diff --git a/src/ticker.c b/src/ticker.c
new file mode 100644
index 0000000..db09024
--- /dev/null
+++ b/src/ticker.c
@@ -0,0 +1,2 @@
+#define	JEMALLOC_TICKER_C_
+#include "jemalloc/internal/jemalloc_internal.h"
diff --git a/test/unit/ticker.c b/test/unit/ticker.c
new file mode 100644
index 0000000..e737020
--- /dev/null
+++ b/test/unit/ticker.c
@@ -0,0 +1,76 @@
+#include "test/jemalloc_test.h"
+
+TEST_BEGIN(test_ticker_tick)
+{
+#define	NREPS 2
+#define	NTICKS 3
+	ticker_t ticker;
+	int32_t i, j;
+
+	ticker_init(&ticker, NTICKS);
+	for (i = 0; i < NREPS; i++) {
+		for (j = 0; j < NTICKS; j++) {
+			assert_u_eq(ticker_read(&ticker), NTICKS - j,
+			    "Unexpected ticker value (i=%d, j=%d)", i, j);
+			assert_false(ticker_tick(&ticker),
+			    "Unexpected ticker fire (i=%d, j=%d)", i, j);
+		}
+		assert_u32_eq(ticker_read(&ticker), 0,
+		    "Expected ticker depletion");
+		assert_true(ticker_tick(&ticker),
+		    "Expected ticker fire (i=%d)", i);
+		assert_u32_eq(ticker_read(&ticker), NTICKS,
+		    "Expected ticker reset");
+	}
+#undef NTICKS
+}
+TEST_END
+
+TEST_BEGIN(test_ticker_ticks)
+{
+#define	NTICKS 3
+	ticker_t ticker;
+
+	ticker_init(&ticker, NTICKS);
+
+	assert_u_eq(ticker_read(&ticker), NTICKS, "Unexpected ticker value");
+	assert_false(ticker_ticks(&ticker, NTICKS), "Unexpected ticker fire");
+	assert_u_eq(ticker_read(&ticker), 0, "Unexpected ticker value");
+	assert_true(ticker_ticks(&ticker, NTICKS), "Expected ticker fire");
+	assert_u_eq(ticker_read(&ticker), NTICKS, "Unexpected ticker value");
+
+	assert_true(ticker_ticks(&ticker, NTICKS + 1), "Expected ticker fire");
+	assert_u_eq(ticker_read(&ticker), NTICKS, "Unexpected ticker value");
+#undef NTICKS
+}
+TEST_END
+
+TEST_BEGIN(test_ticker_copy)
+{
+#define	NTICKS 3
+	ticker_t ta, tb;
+
+	ticker_init(&ta, NTICKS);
+	ticker_copy(&tb, &ta);
+	assert_u_eq(ticker_read(&tb), NTICKS, "Unexpected ticker value");
+	assert_true(ticker_ticks(&tb, NTICKS + 1), "Expected ticker fire");
+	assert_u_eq(ticker_read(&tb), NTICKS, "Unexpected ticker value");
+
+	ticker_tick(&ta);
+	ticker_copy(&tb, &ta);
+	assert_u_eq(ticker_read(&tb), NTICKS - 1, "Unexpected ticker value");
+	assert_true(ticker_ticks(&tb, NTICKS), "Expected ticker fire");
+	assert_u_eq(ticker_read(&tb), NTICKS, "Unexpected ticker value");
+#undef NTICKS
+}
+TEST_END
+
+int
+main(void)
+{
+
+	return (test(
+	    test_ticker_tick,
+	    test_ticker_ticks,
+	    test_ticker_copy));
+}