Skip to content
Snippets Groups Projects
Commit e4150145 authored by Alexandr's avatar Alexandr
Browse files

added forgotten files to quota patch

parent 8abc6295
No related branches found
No related tags found
No related merge requests found
#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 */
#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();
}
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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment