parent
e0d9c0d602
commit
72ec9a432f
@ -1,79 +1,28 @@
|
||||
#ifndef _LIGHTTPD_THROTTLE_H_
|
||||
#define _LIGHTTPD_THROTTLE_H_
|
||||
|
||||
#define THROTTLE_GRANULARITY 200 /* defines how frequently (in milliseconds) a magazine is refilled */
|
||||
|
||||
/* this makro converts a li_tstamp to a gint. this is needed for atomic access. millisecond precision, can hold two weeks max */
|
||||
#define THROTTLE_EVTSTAMP_TO_GINT(x) ((gint) ((x - ((gint)x - (gint)x % (3600*24*14))) * 1000))
|
||||
|
||||
typedef struct liThrottleState liThrottleState;
|
||||
|
||||
/* vrequest data */
|
||||
#if 0
|
||||
/* I/O throttling */
|
||||
gboolean throttled; /* TRUE if vrequest is throttled */
|
||||
struct {
|
||||
gint magazine; /* currently available for use */
|
||||
#include <lighttpd/base.h>
|
||||
|
||||
struct {
|
||||
liThrottlePool *ptr; /* NULL if not in any throttling pool */
|
||||
GList lnk;
|
||||
GQueue *queue;
|
||||
gint magazine;
|
||||
} pool;
|
||||
struct {
|
||||
liThrottlePool *ptr;
|
||||
GList lnk;
|
||||
GQueue *queue;
|
||||
gint magazine;
|
||||
} ip;
|
||||
struct {
|
||||
gint rate; /* maximum transfer rate in bytes per second, 0 if unlimited */
|
||||
ev_tstamp last_update;
|
||||
} con;
|
||||
liWaitQueueElem wqueue_elem;
|
||||
} throttle;
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
LI_THROTTLE_POOL_NAME,
|
||||
LI_THROTTLE_POOL_IP
|
||||
} liThrottlePoolType;
|
||||
|
||||
struct liThrottlePool {
|
||||
/* global per pool */
|
||||
liThrottlePoolType type;
|
||||
union {
|
||||
GString *name;
|
||||
liSocketAddress addr;
|
||||
} data;
|
||||
gint rate; /* bytes/s */
|
||||
gint refcount;
|
||||
gint rearming; /* atomic access, 1 if a worker is currently rearming the magazine */
|
||||
gint last_rearm; /* gint for atomic access. represents a ((gint)ev_tstamp*1000) */
|
||||
#define THROTTLE_GRANULARITY 200 /* defines how frequently (in milliseconds) a magazine is refilled */
|
||||
|
||||
/* local per worker */
|
||||
gint *worker_magazine;
|
||||
gint *worker_last_rearm;
|
||||
gint *worker_num_cons_queued;
|
||||
GQueue** worker_queues;
|
||||
};
|
||||
typedef void (*liThrottleNotifyCB)(liThrottleState *state, gpointer data);
|
||||
|
||||
struct liThrottleParam {
|
||||
gint rate;
|
||||
guint burst;
|
||||
};
|
||||
LI_API liThrottleState* li_throttle_new();
|
||||
LI_API void li_throttle_set(liWorker *wrk, liThrottleState *state, guint rate, guint burst);
|
||||
LI_API void li_throttle_free(liWorker *wrk, liThrottleState *state);
|
||||
|
||||
LI_API void li_throttle_reset(liVRequest *vr);
|
||||
LI_API void li_throttle_cb(liWaitQueue *wq, gpointer data);
|
||||
LI_API guint li_throttle_query(liWorker *wrk, liThrottleState *state, guint interested, liThrottleNotifyCB notify_callback, gpointer data);
|
||||
LI_API void li_throttle_update(liThrottleState *state, guint used);
|
||||
|
||||
LI_API liThrottlePool *li_throttle_pool_new(liServer *srv, liThrottlePoolType type, gpointer param, guint rate);
|
||||
LI_API void li_throttle_pool_free(liServer *srv, liThrottlePool *pool);
|
||||
LI_API liThrottlePool* li_throttle_pool_new(liServer *srv, guint rate, guint burst);
|
||||
LI_API void li_throttle_pool_acquire(liThrottlePool *pool);
|
||||
LI_API void li_throttle_pool_release(liThrottlePool *pool, liServer *srv);
|
||||
|
||||
LI_API void li_throttle_pool_acquire(liVRequest *vr, liThrottlePool *pool);
|
||||
LI_API void li_throttle_pool_release(liVRequest *vr, liThrottlePool *pool);
|
||||
/* returns whether pool was actually added (otherwise it already was added) */
|
||||
LI_API gboolean li_throttle_add_pool(liWorker *wrk, liThrottleState *state, liThrottlePool *pool);
|
||||
LI_API void li_throttle_remove_pool(liWorker *wrk, liThrottleState *state, liThrottlePool *pool);
|
||||
|
||||
/* update throttle data: notify it that we sent <transferred> bytes, and that we never send more than write_max at once */
|
||||
LI_API void li_throttle_update(liVRequest *vr, goffset transferred, goffset write_max);
|
||||
/* internal for worker waitqueue setup */
|
||||
LI_API void li_throttle_waitqueue_cb(liWaitQueue *wq, gpointer data);
|
||||
|
||||
#endif
|
||||
|
@ -1,374 +1,370 @@
|
||||
/*
|
||||
* Implemented with token bucket algorithm.
|
||||
* On average, the rate of bytes/s never exceeds the specified limit
|
||||
* but allows small bursts of previously unused bandwidth (max rate*2 for 1 second).
|
||||
*/
|
||||
|
||||
#include <lighttpd/base.h>
|
||||
#include <lighttpd/throttle.h>
|
||||
#include <math.h>
|
||||
|
||||
static void li_throttle_pool_rearm(liWorker *wrk, liThrottlePool *pool);
|
||||
/* max amount of bytes we release in one query */
|
||||
#define THROTTLE_MAX_STEP (64*1024)
|
||||
/* even if the magazine is empty release "overload" bytes to get requests started */
|
||||
#define THROTTLE_OVERLOAD (8*1024)
|
||||
|
||||
liThrottlePool *li_throttle_pool_new(liServer *srv, liThrottlePoolType type, gpointer param, guint rate) {
|
||||
liThrottlePool *pool;
|
||||
guint i;
|
||||
/* debug */
|
||||
#if 0
|
||||
#define STRINGIFY(x) #x
|
||||
#define _STRINGIFY(x) STRINGIFY(x)
|
||||
#define throttle_debug(...) fprintf(stderr, "throttle.c:" _STRINGIFY(__LINE__) ": " __VA_ARGS__)
|
||||
#else
|
||||
#define throttle_debug(...) do { } while (0)
|
||||
#endif
|
||||
|
||||
g_mutex_lock(srv->action_mutex);
|
||||
/* rates all in bytes/sec */
|
||||
|
||||
if (type == LI_THROTTLE_POOL_NAME) {
|
||||
/* named pool */
|
||||
GString *name = param;
|
||||
/* check if we already have a pool with that name */
|
||||
for (i = 0; i < srv->throttle_pools->len; i++) {
|
||||
pool = g_array_index(srv->throttle_pools, liThrottlePool*, i);
|
||||
typedef struct liThrottlePoolState liThrottlePoolState;
|
||||
typedef struct liThrottlePoolWorkerState liThrottlePoolWorkerState;
|
||||
|
||||
if (g_string_equal(pool->data.name, name)) {
|
||||
g_atomic_int_inc(&pool->refcount);
|
||||
g_mutex_unlock(srv->action_mutex);
|
||||
g_string_free(name, TRUE);
|
||||
return pool;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* IP address pool */
|
||||
liSocketAddress *remote_addr = param;
|
||||
|
||||
if (remote_addr->addr->plain.sa_family == AF_INET)
|
||||
pool = li_radixtree_lookup_exact(srv->throttle_ip_pools, &remote_addr->addr->ipv4.sin_addr.s_addr, 32);
|
||||
else
|
||||
pool = li_radixtree_lookup_exact(srv->throttle_ip_pools, &remote_addr->addr->ipv6.sin6_addr.s6_addr, 128);
|
||||
|
||||
if (pool) {
|
||||
g_mutex_unlock(srv->action_mutex);
|
||||
return pool;
|
||||
}
|
||||
}
|
||||
struct liThrottlePoolState {
|
||||
liThrottlePool *pool;
|
||||
GList pool_link;
|
||||
|
||||
if (rate == 0) {
|
||||
g_mutex_unlock(srv->action_mutex);
|
||||
return NULL;
|
||||
}
|
||||
/* currently available for use */
|
||||
gint magazine;
|
||||
};
|
||||
|
||||
pool = g_slice_new0(liThrottlePool);
|
||||
pool->type = type;
|
||||
pool->rate = rate;
|
||||
pool->last_rearm = THROTTLE_EVTSTAMP_TO_GINT(ev_time());
|
||||
|
||||
/*
|
||||
* We if we are not in LI_SERVER_INIT state, we can initialize the queues directly.
|
||||
* Otherwise they'll get initialized in the worker prepare callback.
|
||||
*/
|
||||
if (g_atomic_int_get(&srv->state) != LI_SERVER_INIT) {
|
||||
pool->worker_magazine = g_new0(gint, srv->worker_count);
|
||||
pool->worker_last_rearm = g_new0(gint, srv->worker_count);
|
||||
pool->worker_num_cons_queued = g_new0(gint, srv->worker_count);
|
||||
pool->worker_queues = g_new0(GQueue*, srv->worker_count);
|
||||
|
||||
for (i = 0; i < srv->worker_count; i++) {
|
||||
pool->worker_queues[i] = g_queue_new();
|
||||
pool->worker_last_rearm[i] = pool->last_rearm;
|
||||
}
|
||||
}
|
||||
struct liThrottleState {
|
||||
gint magazine; /* currently available for use */
|
||||
|
||||
if (type == LI_THROTTLE_POOL_NAME) {
|
||||
pool->refcount = 1;
|
||||
pool->data.name = param;
|
||||
g_array_append_val(srv->throttle_pools, pool);
|
||||
} else {
|
||||
liSocketAddress *remote_addr = param;
|
||||
pool->data.addr = li_sockaddr_dup(*remote_addr);
|
||||
|
||||
if (remote_addr->addr->plain.sa_family == AF_INET)
|
||||
li_radixtree_insert(srv->throttle_ip_pools, &remote_addr->addr->ipv4.sin_addr.s_addr, 32, pool);
|
||||
else
|
||||
li_radixtree_insert(srv->throttle_ip_pools, &remote_addr->addr->ipv6.sin6_addr.s6_addr, 128, pool);
|
||||
}
|
||||
guint interested;
|
||||
liWaitQueueElem wqueue_elem;
|
||||
liThrottleNotifyCB notify_callback;
|
||||
|
||||
g_mutex_unlock(srv->action_mutex);
|
||||
/* max values for this single state */
|
||||
gint single_magazine;
|
||||
guint single_rate, single_burst;
|
||||
guint single_last_rearm;
|
||||
|
||||
return pool;
|
||||
}
|
||||
/* shared pools */
|
||||
GPtrArray *pools; /* <liThrottlePoolState> */
|
||||
};
|
||||
|
||||
void li_throttle_pool_free(liServer *srv, liThrottlePool *pool) {
|
||||
guint i;
|
||||
struct liThrottlePoolWorkerState {
|
||||
gint magazine;
|
||||
guint last_rearm;
|
||||
guint connections; /* waiting.length; needed for atomic access */
|
||||
GQueue waiting; /* <liThrottlePoolState.pool_link> waiting to get filled */
|
||||
};
|
||||
|
||||
if (!g_atomic_int_dec_and_test(&pool->refcount))
|
||||
return;
|
||||
struct liThrottlePool {
|
||||
int refcount;
|
||||
|
||||
g_mutex_lock(srv->action_mutex);
|
||||
GMutex *rearm_mutex;
|
||||
guint rate, burst;
|
||||
guint last_rearm;
|
||||
|
||||
if (pool->type == LI_THROTTLE_POOL_NAME) {
|
||||
for (i = 0; i < srv->throttle_pools->len; i++) {
|
||||
if (pool == g_array_index(srv->throttle_pools, liThrottlePool*, i)) {
|
||||
g_array_remove_index_fast(srv->throttle_pools, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
liThrottlePoolWorkerState *workers;
|
||||
};
|
||||
|
||||
g_string_free(pool->data.name, TRUE);
|
||||
} else {
|
||||
if (pool->data.addr.addr->plain.sa_family == AF_INET)
|
||||
li_radixtree_remove(srv->throttle_ip_pools, &pool->data.addr.addr->ipv4.sin_addr.s_addr, 32);
|
||||
else
|
||||
li_radixtree_remove(srv->throttle_ip_pools, &pool->data.addr.addr->ipv6.sin6_addr.s6_addr, 128);
|
||||
li_sockaddr_clear(&pool->data.addr);
|
||||
}
|
||||
g_mutex_unlock(srv->action_mutex);
|
||||
static guint msec_timestamp(li_tstamp now) {
|
||||
return (1000u * (guint64) floor(now)) + (guint64)(1000.0 * fmod(now, 1.0));
|
||||
}
|
||||
|
||||
if (pool->worker_queues) {
|
||||
for (i = 0; i < srv->worker_count; i++) {
|
||||
g_queue_free(pool->worker_queues[i]);
|
||||
}
|
||||
static void S_throttle_pool_rearm_workers(liThrottlePool *pool, guint worker_count, guint time_diff) {
|
||||
guint i;
|
||||
gint64 connections = 0;
|
||||
gint64 wrk_connections[worker_count];
|
||||
gint64 fill;
|
||||
|
||||
g_free(pool->worker_magazine);
|
||||
g_free(pool->worker_last_rearm);
|
||||
g_free(pool->worker_num_cons_queued);
|
||||
g_free(pool->worker_queues);
|
||||
for (i = 0; i < worker_count; ++i) {
|
||||
wrk_connections[i] = g_atomic_int_get((gint*) &pool->workers[i].connections);
|
||||
connections += wrk_connections[i];
|
||||
}
|
||||
|
||||
g_slice_free(liThrottlePool, pool);
|
||||
}
|
||||
if (0 == connections) return;
|
||||
|
||||
void li_throttle_pool_acquire(liVRequest *vr, liThrottlePool *pool) {
|
||||
/* already in this pool */
|
||||
if (vr->throttle.pool.ptr == pool || vr->throttle.ip.ptr == pool)
|
||||
return;
|
||||
time_diff = MIN(time_diff, 1000);
|
||||
|
||||
g_atomic_int_inc(&pool->refcount);
|
||||
fill = MIN((guint64) pool->burst, ((guint64) pool->rate * time_diff) / 1000u);
|
||||
|
||||
if (pool->type == LI_THROTTLE_POOL_NAME) {
|
||||
if (vr->throttle.pool.ptr != NULL) {
|
||||
/* already in a different pool */
|
||||
li_throttle_pool_release(vr, vr->throttle.pool.ptr);
|
||||
}
|
||||
throttle_debug("rearm workers: refill %i after %u (or more) msecs (rate %u, burst %u)\n",
|
||||
(guint) fill, (guint) time_diff, pool->rate, pool->burst);
|
||||
|
||||
vr->throttle.pool.ptr = pool;
|
||||
} else {
|
||||
vr->throttle.ip.ptr = pool;
|
||||
for (i = 0; i < worker_count; ++i) {
|
||||
gint wrk_fill;
|
||||
if (0 == wrk_connections[i]) continue;
|
||||
wrk_fill = (fill * wrk_connections[i]) / connections;
|
||||
throttle_debug("rearm worker %u: refill %u\n", i, wrk_fill);
|
||||
g_atomic_int_add(&pool->workers[i].magazine, wrk_fill);
|
||||
}
|
||||
|
||||
vr->throttled = TRUE;
|
||||
|
||||
li_throttle_pool_rearm(vr->wrk, pool);
|
||||
li_throttle_update(vr, 0, 0);
|
||||
}
|
||||
|
||||
void li_throttle_pool_release(liVRequest *vr, liThrottlePool *pool) {
|
||||
if (pool->type == LI_THROTTLE_POOL_NAME) {
|
||||
if (vr->throttle.pool.queue) {
|
||||
g_atomic_int_add(&pool->worker_num_cons_queued[vr->wrk->ndx], -1);
|
||||
g_queue_unlink(vr->throttle.pool.queue, &vr->throttle.pool.lnk);
|
||||
vr->throttle.pool.queue = NULL;
|
||||
}
|
||||
static void throttle_pool_rearm(liWorker *wrk, liThrottlePool *pool, guint now) {
|
||||
liThrottlePoolWorkerState *wpool = &pool->workers[wrk->ndx];
|
||||
guint last = g_atomic_int_get((gint*) &pool->last_rearm);
|
||||
guint time_diff = now - last;
|
||||
|
||||
vr->throttle.pool.magazine = 0;
|
||||
vr->throttle.pool.ptr = NULL;
|
||||
} else {
|
||||
if (vr->throttle.ip.queue) {
|
||||
g_atomic_int_add(&pool->worker_num_cons_queued[vr->wrk->ndx], -1);
|
||||
g_queue_unlink(vr->throttle.ip.queue, &vr->throttle.ip.lnk);
|
||||
vr->throttle.ip.queue = NULL;
|
||||
}
|
||||
if (G_UNLIKELY(time_diff >= THROTTLE_GRANULARITY)) {
|
||||
g_mutex_lock(pool->rearm_mutex);
|
||||
/* check again */
|
||||
last = g_atomic_int_get((gint*) &pool->last_rearm);
|
||||
time_diff = now - last;
|
||||
if (G_LIKELY(time_diff >= THROTTLE_GRANULARITY)) {
|
||||
S_throttle_pool_rearm_workers(pool, wrk->srv->worker_count, time_diff);
|
||||
g_atomic_int_set((gint*) &pool->last_rearm, now);
|
||||
}
|
||||
g_mutex_unlock(pool->rearm_mutex);
|
||||
}
|
||||
|
||||
vr->throttle.ip.magazine = 0;
|
||||
vr->throttle.ip.ptr = NULL;
|
||||
if (G_UNLIKELY(wpool->last_rearm < last)) {
|
||||
/* distribute wpool->magazine */
|
||||
GList *lnk;
|
||||
guint connections = wpool->connections;
|
||||
gint magazine = g_atomic_int_get(&wpool->magazine);
|
||||
gint supply = magazine / connections;
|
||||
g_atomic_int_add(&wpool->magazine, -supply * connections);
|
||||
wpool->last_rearm = now;
|
||||
|
||||
throttle_debug("throttle_pool_rearm: distribute supply %i on each of %i connections\n",
|
||||
supply, connections);
|
||||
|
||||
if (0 == supply) return;
|
||||
|
||||
g_atomic_int_set((gint*) &wpool->connections, 0);
|
||||
while (NULL != (lnk = g_queue_pop_head_link(&wpool->waiting))) {
|
||||
liThrottlePoolState *pstate = LI_CONTAINER_OF(lnk, liThrottlePoolState, pool_link);
|
||||
pstate->magazine += supply;
|
||||
lnk->data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li_throttle_pool_free(vr->wrk->srv, pool);
|
||||
static void throttle_register(liThrottlePoolWorkerState *pwstate, liThrottlePoolState *pstate) {
|
||||
if (NULL == pstate->pool_link.data) {
|
||||
g_queue_push_tail_link(&pwstate->waiting, &pstate->pool_link);
|
||||
pstate->pool_link.data = &pwstate->waiting;
|
||||
g_atomic_int_inc((gint*) &pwstate->connections);
|
||||
}
|
||||
}
|
||||
static void throttle_unregister(liThrottlePoolWorkerState *pwstate, liThrottlePoolState *pstate) {
|
||||
if (NULL != pstate->pool_link.data) {
|
||||
g_queue_unlink(&pwstate->waiting, &pstate->pool_link);
|
||||
pstate->pool_link.data = NULL;
|
||||
g_atomic_int_add((gint*) &pwstate->connections, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static void li_throttle_pool_rearm(liWorker *wrk, liThrottlePool *pool) {
|
||||
gint time_diff, supply, num_cons, magazine;
|
||||
guint i;
|
||||
GQueue *queue;
|
||||
GList *lnk, *lnk_next;
|
||||
guint now = THROTTLE_EVTSTAMP_TO_GINT(li_cur_ts(wrk));
|
||||
guint li_throttle_query(liWorker *wrk, liThrottleState *state, guint interested, liThrottleNotifyCB notify_callback, gpointer data) {
|
||||
guint now = msec_timestamp(li_cur_ts(wrk));
|
||||
gint fill, pool_fill;
|
||||
guint i, len;
|
||||
|
||||
time_diff = now - pool->worker_last_rearm[wrk->ndx];
|
||||
/* overflow after 31 days... */
|
||||
if (G_UNLIKELY(time_diff < 0))
|
||||
time_diff = 1000;
|
||||
if (NULL == state) return interested;
|
||||
|
||||
if (G_LIKELY(time_diff < THROTTLE_GRANULARITY) && G_LIKELY(time_diff > 0))
|
||||
return;
|
||||
state->notify_callback = NULL;
|
||||
state->wqueue_elem.data = NULL;
|
||||
|
||||
/* milliseconds since last global rearm */
|
||||
time_diff = now - g_atomic_int_get(&pool->last_rearm);
|
||||
if (G_UNLIKELY(time_diff < 0))
|
||||
time_diff = 1000;
|
||||
throttle_debug("li_throttle_query[%u]: interested %i, magazine %i\n", now, interested, state->magazine);
|
||||
|
||||
/* check if we have to rearm any magazines */
|
||||
if (G_UNLIKELY(time_diff >= THROTTLE_GRANULARITY)) {
|
||||
/* spinlock while we rearm the magazines */
|
||||
if (g_atomic_int_compare_and_exchange(&pool->rearming, 0, 1)) {
|
||||
gint worker_num_cons[wrk->srv->worker_count];
|
||||
|
||||
/* calculate total cons, take a safe "snapshot" */
|
||||
num_cons = 0;
|
||||
for (i = 0; i < wrk->srv->worker_count; i++) {
|
||||
worker_num_cons[i] = g_atomic_int_get(&pool->worker_num_cons_queued[i]);
|
||||
num_cons += worker_num_cons[i];
|
||||
}
|
||||
if (interested > THROTTLE_MAX_STEP) interested = THROTTLE_MAX_STEP;
|
||||
|
||||
if (num_cons) {
|
||||
/* rearm the worker magazines */
|
||||
supply = ((pool->rate / 1000) * MIN(time_diff, 1000)) / num_cons;
|
||||
for (i = 0; i < wrk->srv->worker_count; i++) {
|
||||
if (worker_num_cons[i] == 0)
|
||||
continue;
|
||||
if ((gint) interested <= state->magazine + THROTTLE_OVERLOAD) return interested;
|
||||
|
||||
/* also try to balance negative magazine */
|
||||
fill = interested - state->magazine;
|
||||
if (state->single_rate != 0) {
|
||||
if (now - state->single_last_rearm >= THROTTLE_GRANULARITY) {
|
||||
guint single_fill = ((guint64) state->single_rate) * 1000u / (now - state->single_last_rearm);
|
||||
state->single_last_rearm = now;
|
||||
if (state->single_burst - state->single_magazine < single_fill) {
|
||||
state->single_magazine = state->single_burst;
|
||||
} else {
|
||||
state->single_magazine += single_fill;
|
||||
}
|
||||
}
|
||||
if (fill > state->single_magazine) fill = state->single_magazine;
|
||||
throttle_debug("single_magazine: %i\n", state->single_magazine);
|
||||
}
|
||||
|
||||
g_atomic_int_add(&pool->worker_magazine[i], supply * worker_num_cons[i]);
|
||||
/* pool_fill <= fill in the loop */
|
||||
pool_fill = fill;
|
||||
for (i = 0, len = state->pools->len; i < len; ++i) {
|
||||
liThrottlePoolState *pstate = g_ptr_array_index(state->pools, i);
|
||||
liThrottlePool *pool = pstate->pool;
|
||||
liThrottlePoolWorkerState *pwstate = &pool->workers[wrk->ndx];
|
||||
if (fill > pstate->magazine) {
|
||||
throttle_register(pwstate, pstate);
|
||||
throttle_pool_rearm(wrk, pool, now);
|
||||
if (fill > pstate->magazine) {
|
||||
throttle_register(pwstate, pstate);
|
||||
if (pool_fill > pstate->magazine) {
|
||||
pool_fill = pstate->magazine;
|
||||
}
|
||||
}
|
||||
}
|
||||
throttle_debug("pool %i magazine: %i\n", i, state->single_magazine);
|
||||
}
|
||||
|
||||
throttle_debug("query refill: %i\n", pool_fill);
|
||||
|
||||
g_atomic_int_set(&pool->last_rearm, now);
|
||||
g_atomic_int_set(&pool->rearming, 0);
|
||||
} else {
|
||||
/* wait for pool rearm to finish */
|
||||
while (g_atomic_int_get(&pool->rearming) == 1) { }
|
||||
if (pool_fill > 0) {
|
||||
if (state->single_rate != 0) {
|
||||
state->single_magazine -= pool_fill;
|
||||
}
|
||||
for (i = 0, len = state->pools->len; i < len; ++i) {
|
||||
liThrottlePoolState *pstate = g_ptr_array_index(state->pools, i);
|
||||
pstate->magazine -= pool_fill;
|
||||
}
|
||||
state->magazine += pool_fill;
|
||||
}
|
||||
|
||||
if (state->magazine + THROTTLE_OVERLOAD <= 0) {
|
||||
throttle_debug("query queueing\n");
|
||||
state->wqueue_elem.data = data;
|
||||
state->notify_callback = notify_callback;
|
||||
state->interested = interested;
|
||||
if (!state->wqueue_elem.queued) {
|
||||
li_waitqueue_push(&wrk->throttle_queue, &state->wqueue_elem);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* select current queue */
|
||||
queue = pool->worker_queues[wrk->ndx];
|
||||
throttle_debug("query success: %i\n", state->magazine + THROTTLE_OVERLOAD);
|
||||
|
||||
if (queue->length) {
|
||||
g_atomic_int_set(&pool->worker_num_cons_queued[wrk->ndx], 0);
|
||||
if ((gint) interested <= state->magazine + THROTTLE_OVERLOAD) return interested;
|
||||
return state->magazine + THROTTLE_OVERLOAD;
|
||||
}
|
||||
|
||||
magazine = g_atomic_int_get(&pool->worker_magazine[wrk->ndx]);
|
||||
g_atomic_int_add(&pool->worker_magazine[wrk->ndx], -magazine);
|
||||
supply = magazine / queue->length;
|
||||
void li_throttle_update(liThrottleState *state, guint used) {
|
||||
state->magazine -= used;
|
||||
}
|
||||
|
||||
/* rearm connections */
|
||||
for (lnk = g_queue_peek_head_link(queue); lnk != NULL; lnk = lnk_next) {
|
||||
if (pool->type == LI_THROTTLE_POOL_NAME) {
|
||||
((liVRequest*)lnk->data)->throttle.pool.magazine += supply;
|
||||
((liVRequest*)lnk->data)->throttle.pool.queue = NULL;
|
||||
} else {
|
||||
((liVRequest*)lnk->data)->throttle.ip.magazine += supply;
|
||||
((liVRequest*)lnk->data)->throttle.ip.queue = NULL;
|
||||
}
|
||||
lnk_next = lnk->next;
|
||||
lnk->next = NULL;
|
||||
lnk->prev = NULL;
|
||||
}
|
||||
void li_throttle_pool_acquire(liThrottlePool *pool) {
|
||||
assert(g_atomic_int_get(&pool->refcount) > 0);
|
||||
g_atomic_int_inc(&pool->refcount);
|
||||
}
|
||||
|
||||
/* clear current connection queue */
|
||||
g_queue_init(queue);
|
||||
void li_throttle_pool_release(liThrottlePool *pool, liServer *srv) {
|
||||
assert(g_atomic_int_get(&pool->refcount) > 0);
|
||||
if (g_atomic_int_dec_and_test(&pool->refcount)) {
|
||||
g_mutex_free(pool->rearm_mutex);
|
||||
pool->rearm_mutex = NULL;
|
||||
if (NULL != pool->workers) {
|
||||
g_slice_free1(sizeof(liThrottlePoolWorkerState) * srv->worker_count, pool->workers);
|
||||
pool->workers = NULL;
|
||||
}
|
||||
}
|
||||
|
||||