freeswitch/src/switch_core_port_allocator.c

211 lines
5.4 KiB
C

/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Anthony Minessale II <anthm@freeswitch.org>
* Michael Jerris <mike@jerris.com>
* Paul D. Tinsley <pdt at jackhammer.org>
*
*
* switch_core_port_allocator.c -- Main Core Library (port allocator)
*
*/
#include <switch.h>
#include "private/switch_core_pvt.h"
struct switch_core_port_allocator {
switch_port_t start;
switch_port_t end;
switch_port_t next;
int8_t *track;
uint32_t track_len;
uint32_t track_used;
switch_port_flag_t flags;
switch_mutex_t *mutex;
switch_memory_pool_t *pool;
};
SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start,
switch_port_t end, switch_port_flag_t flags, switch_core_port_allocator_t **new_allocator)
{
switch_status_t status;
switch_memory_pool_t *pool;
switch_core_port_allocator_t *alloc;
int even, odd;
if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) {
return status;
}
if (!(alloc = switch_core_alloc(pool, sizeof(*alloc)))) {
switch_core_destroy_memory_pool(&pool);
return SWITCH_STATUS_MEMERR;
}
alloc->flags = flags;
even = switch_test_flag(alloc, SPF_EVEN);
odd = switch_test_flag(alloc, SPF_ODD);
if (!(even && odd)) {
if (even) {
if ((start % 2) != 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding odd start port %d to %d\n", start, start + 1);
start++;
}
if ((end % 2) != 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding odd end port %d to %d\n", end, end - 1);
end--;
}
} else if (odd) {
if ((start % 2) == 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding even start port %d to %d\n", start, start + 1);
start++;
}
if ((end % 2) == 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding even end port %d to %d\n", end, end - 1);
end--;
}
}
}
alloc->track_len = (end - start) + 2;
if (!(even && odd)) {
alloc->track_len /= 2;
}
alloc->track = switch_core_alloc(pool, (alloc->track_len + 2) * sizeof(switch_byte_t));
alloc->start = start;
alloc->next = start;
alloc->end = end;
switch_mutex_init(&alloc->mutex, SWITCH_MUTEX_NESTED, pool);
alloc->pool = pool;
*new_allocator = alloc;
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_request_port(switch_core_port_allocator_t *alloc, switch_port_t *port_ptr)
{
switch_port_t port = 0;
switch_status_t status = SWITCH_STATUS_FALSE;
int even = switch_test_flag(alloc, SPF_EVEN);
int odd = switch_test_flag(alloc, SPF_ODD);
switch_mutex_lock(alloc->mutex);
srand((unsigned) ((unsigned) (intptr_t) port_ptr + (unsigned) (intptr_t) switch_thread_self() + switch_micro_time_now()));
while (alloc->track_used < alloc->track_len) {
uint32_t index;
uint32_t tries = 0;
/* randomly pick a port */
index = rand() % alloc->track_len;
/* if it is used walk up the list to find a free one */
while (alloc->track[index] && tries < alloc->track_len) {
tries++;
if (alloc->track[index] < 0) {
alloc->track[index]++;
}
if (++index >= alloc->track_len) {
index = 0;
}
}
if (tries < alloc->track_len) {
alloc->track[index] = 1;
alloc->track_used++;
status = SWITCH_STATUS_SUCCESS;
if ((even && odd)) {
port = (switch_port_t) (index + alloc->start);
} else {
port = (switch_port_t) (index + (alloc->start / 2));
port *= 2;
}
goto end;
}
}
end:
switch_mutex_unlock(alloc->mutex);
if (status == SWITCH_STATUS_SUCCESS) {
*port_ptr = port;
} else {
*port_ptr = 0;
}
return status;
}
SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_free_port(switch_core_port_allocator_t *alloc, switch_port_t port)
{
switch_status_t status = SWITCH_STATUS_FALSE;
int even = switch_test_flag(alloc, SPF_EVEN);
int odd = switch_test_flag(alloc, SPF_ODD);
int index = port - alloc->start;
if (!(even && odd)) {
index /= 2;
}
switch_mutex_lock(alloc->mutex);
if (alloc->track[index] > 0) {
alloc->track[index] = -4;
alloc->track_used--;
status = SWITCH_STATUS_SUCCESS;
}
switch_mutex_unlock(alloc->mutex);
return status;
}
SWITCH_DECLARE(void) switch_core_port_allocator_destroy(switch_core_port_allocator_t **alloc)
{
switch_memory_pool_t *pool = (*alloc)->pool;
switch_core_destroy_memory_pool(&pool);
*alloc = NULL;
}
/* 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:
*/