From e41501456c84b11877460f9421d6a66e7e8bb754 Mon Sep 17 00:00:00 2001
From: Alexandr <a.lyapunov@corp.mail.ru>
Date: Wed, 29 Oct 2014 11:41:20 +0300
Subject: [PATCH] added forgotten files to quota patch

---
 src/lib/small/quota.h  | 183 +++++++++++++++++++++++++++++++++++++++++
 test/unit/quota.cc     |  95 +++++++++++++++++++++
 test/unit/quota.result |   6 ++
 3 files changed, 284 insertions(+)
 create mode 100644 src/lib/small/quota.h
 create mode 100644 test/unit/quota.cc
 create mode 100644 test/unit/quota.result

diff --git a/src/lib/small/quota.h b/src/lib/small/quota.h
new file mode 100644
index 0000000000..cf9faf2c64
--- /dev/null
+++ b/src/lib/small/quota.h
@@ -0,0 +1,183 @@
+#ifndef INCLUDES_TARANTOOL_SMALL_QUOTA_H
+#define INCLUDES_TARANTOOL_SMALL_QUOTA_H
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+enum {
+	QUOTA_MAX_ALLOC = 0xFFFFFFFFull * 1024u,
+	QUOTA_GRANULARITY = 1024
+};
+
+/** A basic limit on memory usage */
+struct quota {
+	/* high order dword is total available memory and low order dword
+	 * - currently used memory both in QUOTA_GRANULARITY units
+	 */
+	uint64_t value;
+};
+
+/**
+ * Initialize quota with gived memory limit
+ */
+static inline void
+quota_init(struct quota *quota, size_t total)
+{
+	uint64_t new_total_in_granul = (total + (QUOTA_GRANULARITY - 1))
+				        / QUOTA_GRANULARITY;
+	quota->value = new_total_in_granul << 32;
+}
+
+/**
+ * Provide wrappers around gcc built-ins for now.
+ * These built-ins work with all numeric types - may not
+ * be the case when another implementation is used.
+ * Private use only.
+ */
+#define atomic_cas(a, b, c) __sync_val_compare_and_swap(a, b, c)
+
+/**
+ * Get current quota limit
+ */
+static inline size_t
+quota_get_total(const struct quota *quota)
+{
+	return (quota->value >> 32) * QUOTA_GRANULARITY;
+}
+
+/**
+ * Get current quota usage
+ */
+static inline size_t
+quota_get_used(const struct quota *quota)
+{
+	return (quota->value & 0xFFFFFFFFu) * QUOTA_GRANULARITY;
+}
+
+static inline void
+quota_get_total_and_used(struct quota *quota, size_t *total, size_t *used)
+{
+	uint64_t value = quota->value;
+	*total = (value >> 32) * QUOTA_GRANULARITY;
+	*used = (value & 0xFFFFFFFFu) * QUOTA_GRANULARITY;
+}
+
+/**
+ * Set quota memory limit.
+ * returns 0 on success
+ * returns -1 on error - if it's not possible to decrease limit
+ * due to greater current usage
+ */
+static inline int
+quota_set(struct quota *quota, size_t new_total)
+{
+	uint32_t new_total_in_granul = (new_total + (QUOTA_GRANULARITY - 1))
+				   / QUOTA_GRANULARITY;
+	while (1) {
+		uint64_t old_value = quota->value;
+		/* uint32_t cur_total = old_value >> 32; */
+		uint32_t cur_used = old_value & 0xFFFFFFFFu;
+		if (new_total_in_granul < cur_used)
+			return  -1;
+		uint64_t new_value = (((uint64_t)new_total_in_granul) << 32)
+				     | cur_used;
+		if (atomic_cas(&quota->value, old_value, new_value) == old_value)
+			break;
+	}
+	return 0;
+}
+
+/**
+ * Use up a quota
+ * returns 0 on success
+ * returns -1 on error - if quota limit reached
+ */
+static inline int
+quota_use(struct quota *quota, size_t size)
+{
+	uint32_t size_in_granul = (size + (QUOTA_GRANULARITY - 1))
+				  / QUOTA_GRANULARITY;
+	assert(size_in_granul);
+	while (1) {
+		uint64_t old_value = quota->value;
+		uint32_t cur_total = old_value >> 32;
+		uint32_t old_used = old_value & 0xFFFFFFFFu;
+
+		uint32_t new_used = old_used + size_in_granul;
+		assert(new_used > old_used);
+
+		if (new_used > cur_total)
+			return -1;
+
+		uint64_t new_value = (((uint64_t)cur_total) << 32)
+				     | new_used;
+
+		if (atomic_cas(&quota->value, old_value, new_value) == old_value)
+			break;
+	}
+	return 0;
+}
+
+/** Release used memory */
+static inline void
+quota_release(struct quota *quota, size_t size)
+{
+	uint32_t size_in_granul = (size + (QUOTA_GRANULARITY - 1))
+				  / QUOTA_GRANULARITY;
+	assert(size_in_granul);
+	while (1) {
+		uint64_t old_value = quota->value;
+		uint32_t cur_total = old_value >> 32;
+		uint32_t old_used = old_value & 0xFFFFFFFFu;
+
+		assert(size_in_granul <= old_used);
+		uint32_t new_used = old_used - size_in_granul;
+
+		uint64_t new_value = (((uint64_t)cur_total) << 32)
+				     | new_used;
+
+		if (atomic_cas(&quota->value, old_value, new_value) == old_value)
+			break;
+	}
+}
+
+#undef atomic_cas
+
+#if defined(__cplusplus)
+} /* extern "C" { */
+#endif /* defined(__cplusplus) */
+#endif /* INCLUDES_TARANTOOL_SMALL_QUOTA_H */
diff --git a/test/unit/quota.cc b/test/unit/quota.cc
new file mode 100644
index 0000000000..301d2b5c77
--- /dev/null
+++ b/test/unit/quota.cc
@@ -0,0 +1,95 @@
+#include "small/quota.h"
+
+#include <pthread.h>
+#include "test.h"
+
+struct quota quota;
+
+const size_t THREAD_CNT = 10;
+const size_t RUN_CNT = 128 * 1024;
+
+struct thread_data {
+	long use_change;
+	long last_lim_set;
+	long use_change_success;
+	long lim_change_success;
+};
+
+pthread_t threads[THREAD_CNT];
+thread_data datum[THREAD_CNT];
+
+void *thread_routine(void *vparam)
+{
+	struct thread_data *data = (struct thread_data *)vparam;
+	size_t check_fail_count = 0;
+	bool allocated = false;
+	size_t allocated_size = 0;
+	for (size_t i = 0; i < RUN_CNT; i++) {
+		{
+			size_t total, used;
+			quota_get_total_and_used(&quota, &total, &used);
+			if (used > total)
+				check_fail_count++;
+		}
+		long rnd = ((rand() & 0xFFFFFFF) +  1) * QUOTA_GRANULARITY;
+		int succ = quota_set(&quota, (size_t)rnd);
+		if (succ == 0) {
+			data->last_lim_set = rnd;
+			data->lim_change_success++;
+		}
+		if (allocated) {
+			quota_release(&quota, allocated_size);
+			allocated = false;
+			data->use_change = 0;
+			data->use_change_success++;
+		} else {
+			allocated_size = (((long)rand()) & 0xFFFFFFl) + 1;
+			if (quota_use(&quota, allocated_size) == 0) {
+				allocated = true;
+				data->use_change = allocated_size;
+				data->use_change_success++;
+			}
+		}
+	}
+	return (void *)check_fail_count;
+}
+
+int
+main(int n, char **a)
+{
+	(void)n;
+	(void)a;
+	quota_init(&quota, 0);
+
+	plan(5);
+
+	for (size_t i = 0; i < THREAD_CNT; i++) {
+		pthread_create(threads + i, 0, thread_routine, (void *)(datum + i));
+	}
+
+	size_t check_fail_count = 0;
+	for (size_t i = 0; i < THREAD_CNT; i++) {
+		void *ret;
+		check_fail_count += (size_t)pthread_join(threads[i], &ret);
+	}
+
+	bool one_set_successed = false;
+	size_t total_alloc = 0;
+	long set_success_count = 0;
+	long use_success_count = 0;
+	for (size_t i = 0; i < THREAD_CNT; i++) {
+		if (datum[i].last_lim_set == quota_get_total(&quota))
+			one_set_successed = true;
+		total_alloc += ((datum[i].use_change + QUOTA_GRANULARITY - 1) / QUOTA_GRANULARITY) * QUOTA_GRANULARITY;
+		use_success_count += datum[i].use_change_success;
+		set_success_count += datum[i].lim_change_success;
+	}
+
+	ok(check_fail_count == 0, "no fails detected");
+	ok(one_set_successed, "one of thread limit set is final");
+	ok(total_alloc == quota_get_used(&quota), "total alloc match");
+	ok(use_success_count > THREAD_CNT * RUN_CNT * .9, "uses are mosly successful");
+	ok(set_success_count > THREAD_CNT * RUN_CNT * .9, "sets are mosly successful");
+
+	return check_plan();
+}
diff --git a/test/unit/quota.result b/test/unit/quota.result
new file mode 100644
index 0000000000..84fddb5dab
--- /dev/null
+++ b/test/unit/quota.result
@@ -0,0 +1,6 @@
+1..5
+ok 1 - no fails detected
+ok 2 - one of thread limit set is final
+ok 3 - total alloc match
+ok 4 - uses are mosly successful
+ok 5 - sets are mosly successful
-- 
GitLab