| /*
* Sai server - ./src/server/s-resource.c
*
* Copyright (C) 2019 - 2021 Andy Green <andy@warmcat.com>
*
* This library 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:
* version 2.1 of the License.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Serverside resource allocation implementation
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include "s-private.h"
sai_resource_wellknown_t *
sais_resource_wellknown_by_name(sais_t *sais, const char *name)
{
lws_start_foreach_dll(struct lws_dll2 *, p,
sais->resource_wellknown_owner.head) {
sai_resource_wellknown_t *wk;
wk = lws_container_of(p, sai_resource_wellknown_t, list);
if (!strcmp(name, wk->name))
return wk;
} lws_end_foreach_dll(p);
return NULL;
}
sai_resource_requisition_t *
sais_resource_lookup_lease_by_cookie(sais_t *sais, const char *cookie)
{
lws_start_foreach_dll(struct lws_dll2 *, p,
sais->resource_wellknown_owner.head) {
sai_resource_wellknown_t *wk;
wk = lws_container_of(p, sai_resource_wellknown_t, list);
lws_start_foreach_dll(struct lws_dll2 *, p1,
wk->owner_leased.head) {
sai_resource_requisition_t *rr = lws_container_of(p1,
sai_resource_requisition_t,
list_resource_queued_leased);
if (!strcmp(cookie, rr->cookie))
return rr;
} lws_end_foreach_dll(p1);
} lws_end_foreach_dll(p);
return NULL;
}
void
sais_resource_rr_destroy(sai_resource_requisition_t *rr)
{
sai_resource_wellknown_t *wk;
wk = lws_container_of(rr->list_resource_wellknown.owner,
sai_resource_wellknown_t, owner);
if (rr->allocated_since_time) {
assert((time_t)wk->allocated >= (time_t)rr->amount);
wk->allocated = wk->allocated - (long)rr->amount;
lwsl_notice("%s: freeing lease -> %lu/%lu\n", __func__,
wk->allocated, wk->budget);
}
lws_sul_cancel(&rr->sul_expiry);
lws_dll2_remove(&rr->list_resource_wellknown);
lws_dll2_remove(&rr->list_resource_queued_leased);
lws_dll2_remove(&rr->list_pss);
free(rr);
sais_resource_check_if_can_accept_queued(wk);
}
/*
* Eradicate any request, from its cookie
*/
void
sais_resource_destroy_queued_by_cookie(sais_t *sais, const char *cookie)
{
lws_start_foreach_dll(struct lws_dll2 *, p,
sais->resource_wellknown_owner.head) {
sai_resource_wellknown_t *wk;
wk = lws_container_of(p, sai_resource_wellknown_t, list);
lws_start_foreach_dll(struct lws_dll2 *, p1,
wk->owner_queued.head) {
sai_resource_requisition_t *rr = lws_container_of(p1,
sai_resource_requisition_t,
list_resource_queued_leased);
if (!strcmp(cookie, rr->cookie)) {
sais_resource_rr_destroy(rr);
return;
}
} lws_end_foreach_dll(p1);
} lws_end_foreach_dll(p);
}
static void
sais_res_expiry(lws_sorted_usec_list_t *sul)
{
sai_resource_requisition_t *rr = lws_container_of(sul,
sai_resource_requisition_t, sul_expiry);
lwsl_notice("%s: lease expired\n", __func__);
sais_resource_rr_destroy(rr);
}
/*
* If we made some space in the resource, lease to as many queued guys that
* will fit now, respecting their queuing order and amount they want
*/
void
sais_resource_check_if_can_accept_queued(sai_resource_wellknown_t *wk)
{
assert(wk->allocated <= wk->budget);
assert(wk->allocated >= 0);
if (wk->budget == wk->allocated)
return;
while (wk->owner_queued.count) {
sai_resource_requisition_t *rr =
lws_container_of(wk->owner_queued.head,
sai_resource_requisition_t,
list_resource_queued_leased);
struct pss *pss = lws_container_of(rr->list_pss.owner,
struct pss, res_owner);
sai_resource_msg_t *m;
if (wk->budget - wk->allocated < (long)rr->amount)
/*
* Have to be allocated in the queued order, so not
* being able to do this one blocks everything else
*/
return;
wk->allocated = wk->allocated + (long)rr->amount;
lwsl_notice("%s: leased %u to %s, -> %lu/%lu\n", __func__,
rr->amount, rr->cookie,
wk->allocated, wk->budget);
rr->allocated_since_time = time(NULL);
lws_dll2_remove(&rr->list_resource_queued_leased);
lws_dll2_add_tail(&rr->list_resource_queued_leased,
&wk->owner_leased);
lws_sul_schedule(wk->cx, 0, &rr->sul_expiry, sais_res_expiry,
rr->lease_secs * LWS_US_PER_SEC);
/*
* We need to create the acceptance message
*/
m = malloc(sizeof(*m) + LWS_PRE + 256);
if (!m)
return;
memset(m, 0, sizeof(*m));
m->msg = (const char *)&m[1] + LWS_PRE;
m->len = (size_t)lws_snprintf((char *)&m[1] + LWS_PRE, 256,
"{\"schema\":\"com-warmcat-sai-resource\","
"\"cookie\":\"%s\","
"\"amount\":%u}", rr->cookie, rr->amount);
lws_dll2_add_tail(&m->list, &pss->res_pending_reply_owner);
lws_callback_on_writable(pss->wsi);
}
}
void
sais_resource_wellknown_remove_pss(sais_t *sais, struct pss *pss)
{
/*
* Destroy every pending resource request that belongs to this pss
*/
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
pss->res_owner.head) {
sai_resource_requisition_t *rr;
rr = lws_container_of(p, sai_resource_requisition_t, list_pss);
assert(rr->list_resource_wellknown.owner);
sais_resource_rr_destroy(rr);
} lws_end_foreach_dll_safe(p, p1);
/*
* Clean up any pending return resource JSON on the pss we're not
* going to get a chance to send any more
*/
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
pss->res_pending_reply_owner.head) {
sai_resource_msg_t *rm;
rm = lws_container_of(p, sai_resource_msg_t, list);
lws_dll2_remove(&rm->list);
free(rm);
} lws_end_foreach_dll_safe(p, p1);
}
|