freeswitch/libs/libblade/src/blade_rpcmgr.c

365 lines
10 KiB
C

/*
* Copyright (c) 2017, Shane Bryldt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the name of the original author; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 THE COPYRIGHT OWNER
* 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 "blade.h"
struct blade_rpcmgr_s {
blade_handle_t *handle;
ks_hash_t *corerpcs; // method, blade_rpc_t*
ks_hash_t *protocolrpcs; // method, blade_rpc_t*
ks_hash_t *requests; // id, blade_rpc_request_t*
ks_time_t requests_ttlcheck;
ks_mutex_t* requests_ttlcheck_lock;
};
static void blade_rpcmgr_cleanup(void *ptr, void *arg, ks_pool_cleanup_action_t action, ks_pool_cleanup_type_t type)
{
blade_rpcmgr_t *brpcmgr = (blade_rpcmgr_t *)ptr;
ks_hash_iterator_t *it = NULL;
ks_assert(brpcmgr);
switch (action) {
case KS_MPCL_ANNOUNCE:
break;
case KS_MPCL_TEARDOWN:
while ((it = ks_hash_first(brpcmgr->protocolrpcs, KS_UNLOCKED)) != NULL) {
void *key = NULL;
blade_rpc_t *value = NULL;
ks_hash_this(it, (const void **)&key, NULL, (void **)&value);
ks_hash_remove(brpcmgr->protocolrpcs, key);
blade_rpc_destroy(&value); // must call destroy to close the method pool, using FREE_VALUE on the hash would attempt to free the method from the wrong pool
}
while ((it = ks_hash_first(brpcmgr->corerpcs, KS_UNLOCKED)) != NULL) {
void *key = NULL;
blade_rpc_t *value = NULL;
ks_hash_this(it, (const void **)&key, NULL, (void **)&value);
ks_hash_remove(brpcmgr->corerpcs, key);
blade_rpc_destroy(&value); // must call destroy to close the rpc pool, using FREE_VALUE on the hash would attempt to free the rpc from the wrong pool
}
break;
case KS_MPCL_DESTROY:
break;
}
}
KS_DECLARE(ks_status_t) blade_rpcmgr_create(blade_rpcmgr_t **brpcmgrP, blade_handle_t *bh)
{
ks_pool_t *pool = NULL;
blade_rpcmgr_t *brpcmgr = NULL;
ks_assert(brpcmgrP);
ks_pool_open(&pool);
ks_assert(pool);
brpcmgr = ks_pool_alloc(pool, sizeof(blade_rpcmgr_t));
brpcmgr->handle = bh;
ks_hash_create(&brpcmgr->corerpcs, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_RWLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY, pool);
ks_assert(brpcmgr->corerpcs);
ks_hash_create(&brpcmgr->protocolrpcs, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_RWLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY, pool);
ks_assert(brpcmgr->protocolrpcs);
ks_hash_create(&brpcmgr->requests, KS_HASH_MODE_CASE_INSENSITIVE, KS_HASH_FLAG_RWLOCK | KS_HASH_FLAG_DUP_CHECK | KS_HASH_FLAG_FREE_KEY, pool);
ks_assert(brpcmgr->requests);
ks_mutex_create(&brpcmgr->requests_ttlcheck_lock, KS_MUTEX_FLAG_NON_RECURSIVE, pool);
ks_assert(brpcmgr->requests_ttlcheck_lock);
ks_pool_set_cleanup(brpcmgr, NULL, blade_rpcmgr_cleanup);
*brpcmgrP = brpcmgr;
return KS_STATUS_SUCCESS;
}
KS_DECLARE(ks_status_t) blade_rpcmgr_destroy(blade_rpcmgr_t **brpcmgrP)
{
blade_rpcmgr_t *brpcmgr = NULL;
ks_pool_t *pool;
ks_assert(brpcmgrP);
ks_assert(*brpcmgrP);
brpcmgr = *brpcmgrP;
*brpcmgrP = NULL;
pool = ks_pool_get(brpcmgr);
ks_pool_close(&pool);
return KS_STATUS_SUCCESS;
}
KS_DECLARE(blade_handle_t *) blade_rpcmgr_handle_get(blade_rpcmgr_t *brpcmgr)
{
ks_assert(brpcmgr);
return brpcmgr->handle;
}
KS_DECLARE(blade_rpc_t *) blade_rpcmgr_corerpc_lookup(blade_rpcmgr_t *brpcmgr, const char *method)
{
blade_rpc_t *brpc = NULL;
ks_assert(brpcmgr);
ks_assert(method);
brpc = (blade_rpc_t *)ks_hash_search(brpcmgr->corerpcs, (void *)method, KS_READLOCKED);
// @todo if (brpc) blade_rpc_read_lock(brpc);
ks_hash_read_unlock(brpcmgr->corerpcs);
return brpc;
}
KS_DECLARE(ks_status_t) blade_rpcmgr_corerpc_add(blade_rpcmgr_t *brpcmgr, blade_rpc_t *brpc)
{
char *key = NULL;
ks_assert(brpcmgr);
ks_assert(brpc);
key = ks_pstrdup(ks_pool_get(brpcmgr), blade_rpc_method_get(brpc));
ks_hash_insert(brpcmgr->corerpcs, (void *)key, (void *)brpc);
ks_log(KS_LOG_DEBUG, "CoreRPC Added: %s\n", key);
return KS_STATUS_SUCCESS;
}
KS_DECLARE(ks_status_t) blade_rpcmgr_corerpc_remove(blade_rpcmgr_t *brpcmgr, blade_rpc_t *brpc)
{
const char *method = NULL;
ks_assert(brpcmgr);
ks_assert(brpc);
method = blade_rpc_method_get(brpc);
ks_hash_remove(brpcmgr->corerpcs, (void *)method);
ks_log(KS_LOG_DEBUG, "CoreRPC Removed: %s\n", method);
return KS_STATUS_SUCCESS;
}
KS_DECLARE(blade_rpc_t *) blade_rpcmgr_protocolrpc_lookup(blade_rpcmgr_t *brpcmgr, const char *method, const char *protocol)
{
blade_rpc_t *brpc = NULL;
char *key = NULL;
ks_assert(brpcmgr);
ks_assert(method);
ks_assert(protocol);
key = ks_psprintf(ks_pool_get(brpcmgr), "%s:%s", protocol, method);
brpc = ks_hash_search(brpcmgr->protocolrpcs, (void *)key, KS_READLOCKED);
// @todo if (brpc) blade_rpc_read_lock(brpc);
ks_hash_read_unlock(brpcmgr->protocolrpcs);
ks_pool_free(&key);
return brpc;
}
KS_DECLARE(ks_status_t) blade_rpcmgr_protocolrpc_add(blade_rpcmgr_t *brpcmgr, blade_rpc_t *brpc)
{
const char *method = NULL;
const char *protocol = NULL;
char *key = NULL;
ks_assert(brpcmgr);
ks_assert(brpc);
method = blade_rpc_method_get(brpc);
ks_assert(method);
protocol = blade_rpc_protocol_get(brpc);
ks_assert(protocol);
key = ks_psprintf(ks_pool_get(brpcmgr), "%s:%s", protocol, method);
ks_assert(key);
ks_hash_insert(brpcmgr->protocolrpcs, (void *)key, (void *)brpc);
ks_log(KS_LOG_DEBUG, "ProtocolRPC Added: %s\n", key);
return KS_STATUS_SUCCESS;
}
KS_DECLARE(ks_status_t) blade_rpcmgr_protocolrpc_remove(blade_rpcmgr_t *brpcmgr, blade_rpc_t *brpc)
{
const char *method = NULL;
const char *protocol = NULL;
char *key = NULL;
ks_assert(brpcmgr);
ks_assert(brpc);
method = blade_rpc_method_get(brpc);
ks_assert(method);
protocol = blade_rpc_protocol_get(brpc);
ks_assert(protocol);
key = ks_psprintf(ks_pool_get(brpcmgr), "%s:%s", protocol, method);
ks_assert(key);
ks_hash_remove(brpcmgr->protocolrpcs, (void *)key);
ks_log(KS_LOG_DEBUG, "ProtocolRPC Removed: %s\n", key);
ks_pool_free(&key);
return KS_STATUS_SUCCESS;
}
KS_DECLARE(blade_rpc_request_t *) blade_rpcmgr_request_lookup(blade_rpcmgr_t *brpcmgr, const char *id)
{
blade_rpc_request_t *brpcreq = NULL;
ks_assert(brpcmgr);
ks_assert(id);
brpcreq = (blade_rpc_request_t *)ks_hash_search(brpcmgr->requests, (void *)id, KS_READLOCKED);
// @todo if (brpcreq) blade_rpc_request_read_lock(brpcreq);
ks_hash_read_unlock(brpcmgr->requests);
return brpcreq;
}
KS_DECLARE(ks_status_t) blade_rpcmgr_request_add(blade_rpcmgr_t *brpcmgr, blade_rpc_request_t *brpcreq)
{
char *key = NULL;
ks_assert(brpcmgr);
ks_assert(brpcreq);
key = ks_pstrdup(ks_pool_get(brpcmgr), blade_rpc_request_messageid_get(brpcreq));
ks_hash_insert(brpcmgr->requests, (void *)key, (void *)brpcreq);
ks_log(KS_LOG_DEBUG, "Request Added: %s\n", key);
return KS_STATUS_SUCCESS;
}
KS_DECLARE(ks_status_t) blade_rpcmgr_request_remove(blade_rpcmgr_t *brpcmgr, blade_rpc_request_t *brpcreq)
{
const char *id = NULL;
ks_assert(brpcmgr);
ks_assert(brpcreq);
id = blade_rpc_request_messageid_get(brpcreq);
ks_hash_remove(brpcmgr->requests, (void *)id);
ks_log(KS_LOG_DEBUG, "Request Removed: %s\n", id);
return KS_STATUS_SUCCESS;
}
KS_DECLARE(ks_status_t) blade_rpcmgr_request_timeouts(blade_rpcmgr_t *brpcmgr)
{
blade_session_t *loopback = NULL;
ks_assert(brpcmgr);
// All this stuff is to ensure that the requests hash is not locked up when it does not need to be,
// and this will also ensure that sessions will not lock up if any other session is trying to deal
// with timeouts already
if (ks_mutex_trylock(brpcmgr->requests_ttlcheck_lock) != KS_STATUS_SUCCESS) return KS_STATUS_SUCCESS;
if (brpcmgr->requests_ttlcheck > ks_time_now()) {
ks_mutex_unlock(brpcmgr->requests_ttlcheck_lock);
return KS_STATUS_SUCCESS;
}
ks_hash_write_lock(brpcmgr->requests);
// Give a one second delay between timeout checking
brpcmgr->requests_ttlcheck = ks_time_now() + KS_USEC_PER_SEC;
ks_mutex_unlock(brpcmgr->requests_ttlcheck_lock);
// Now find all the expired requests and send out loopback error responses, which will invoke request removal when received and processed
for (ks_hash_iterator_t *it = ks_hash_first(brpcmgr->requests, KS_UNLOCKED); it; it = ks_hash_next(&it)) {
const char *key = NULL;
blade_rpc_request_t *value = NULL;
cJSON *res = NULL;
ks_hash_this(it, (const void **)&key, NULL, (void **)&value);
if (!blade_rpc_request_expired(value)) continue;
if (!loopback) loopback = blade_sessionmgr_loopback_lookup(blade_handle_sessionmgr_get(brpcmgr->handle));
ks_log(KS_LOG_DEBUG, "Request (%s) TTL timeout\n", key);
blade_rpc_error_raw_create(&res, NULL, blade_rpc_request_messageid_get(value), -32000, "Request timed out");
// @todo may want to include requester-nodeid and responder-nodeid into the error block when they are present in the original request
// even though a response or error without them is treated as locally processed, it may be useful to know who the request was attempted with
// when multiple options are available such as a blade.execute where the protocol has multiple possible controllers to pick from
blade_session_send(loopback, res, 0, NULL, NULL);
cJSON_Delete(res);
}
if (loopback) blade_session_read_unlock(loopback);
ks_hash_write_unlock(brpcmgr->requests);
return KS_STATUS_SUCCESS;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/