GUACAMOLE-377: Increase maximum number of streams.

The new guac_display otherwise tends to run out of outbound, client-wide
streams.
This commit is contained in:
Michael Jumper 2024-08-17 19:53:55 -07:00
parent 3b6173f7bf
commit 40eef95f41
7 changed files with 163 additions and 33 deletions

View File

@ -132,7 +132,8 @@ guac_stream* guac_client_alloc_stream(guac_client* client) {
return NULL;
/* Allocate stream */
stream_index = guac_pool_next_int(client->__stream_pool);
stream_index = guac_pool_next_int_below_or_die(client->__stream_pool,
GUAC_CLIENT_MAX_STREAMS);
/* Initialize stream with odd index (even indices are user-level) */
allocd_stream = &(client->__output_streams[stream_index]);

View File

@ -30,7 +30,7 @@
* The maximum number of inbound or outbound streams supported by any one
* guac_client.
*/
#define GUAC_CLIENT_MAX_STREAMS 64
#define GUAC_CLIENT_MAX_STREAMS 512
/**
* The index of a closed stream.

View File

@ -102,8 +102,8 @@ void guac_pool_free(guac_pool* pool);
/**
* Returns the next available integer from the given guac_pool. All integers
* returned are non-negative, and are returned in sequences, starting from 0.
* This operation is threadsafe.
* returned are non-negative, and are returned in sequence, starting from 0.
* This operation is atomic.
*
* @param pool
* The guac_pool to retrieve an integer from.
@ -111,14 +111,65 @@ void guac_pool_free(guac_pool* pool);
* @return
* The next available integer, which may be either an integer not yet
* returned by a call to guac_pool_next_int, or an integer which was
* previously returned, but has since been freed.
* previously returned but has since been freed.
*/
int guac_pool_next_int(guac_pool* pool);
/**
* Returns the next available integer from the given guac_pool that is below
* the given limit. If no such integer can be obtained because all such
* integers are already in use, -1 will be returned instead. All integers
* successfully returned are non-negative, and are returned in sequence,
* starting from 0. This operation is atomic.
*
* @param pool
* The guac_pool to retrieve an integer from.
*
* @param limit
* The exclusive upper bound to enforce on all integers returned by this
* function. Integers of this value or greater will never be returned. If
* all other integers are already in use, -1 will be returned instead.
*
* @return
* The next available integer, which may be either an integer not yet
* returned by a call to guac_pool_next_int, or an integer which was
* previously returned but has since been freed. If all integers are
* currently in use and no integer can be returned without reaching the
* given limit, -1 is returned.
*/
int guac_pool_next_int_below(guac_pool* pool, int limit);
/**
* Returns the next available integer from the given guac_pool that is below
* the given limit. If no such integer can be obtained because all such
* integers are already in use, the current process will abort and this
* function will not return. All integers successfully returned are
* non-negative, and are returned in sequence, starting from 0. This operation
* is atomic.
*
* @param pool
* The guac_pool to retrieve an integer from.
*
* @param limit
* The exclusive upper bound to enforce on all integers returned by this
* function. Integers of this value or greater will never be returned. If
* all other integers are already in use, the current process will abort
* and this function will not return.
*
* @return
* The next available integer, which may be either an integer not yet
* returned by a call to guac_pool_next_int, or an integer which was
* previously returned but has since been freed. If all integers are
* currently in use and no integer can be returned without reaching the
* given limit, the current process will abort and this function will not
* return.
*/
int guac_pool_next_int_below_or_die(guac_pool* pool, int limit);
/**
* Frees the given integer back into the given guac_pool. The integer given
* will be available for future calls to guac_pool_next_int. This operation is
* threadsafe.
* atomic.
*
* @param pool
* The guac_pool to free the given integer into.

View File

@ -35,7 +35,7 @@
* The maximum number of inbound or outbound streams supported by any one
* guac_user.
*/
#define GUAC_USER_MAX_STREAMS 64
#define GUAC_USER_MAX_STREAMS 512
/**
* The index of a closed stream.

View File

@ -19,9 +19,11 @@
#include "config.h"
#include "guacamole/assert.h"
#include "guacamole/mem.h"
#include "guacamole/pool.h"
#include <limits.h>
#include <stdlib.h>
guac_pool* guac_pool_alloc(int size) {
@ -69,42 +71,115 @@ void guac_pool_free(guac_pool* pool) {
}
int guac_pool_next_int(guac_pool* pool) {
/**
* Returns the next available integer from the given guac_pool. All integers
* returned are non-negative, and are returned in sequence, starting from 0.
*
* Unlike the public guac_pool_next_int() function, this function is NOT atomic
* and depends on the caller having already acquired the pool's lock.
*
* @param pool
* The guac_pool to retrieve an integer from.
*
* @return
* The next available integer, which may be either an integer not yet
* returned by a call to guac_pool_next_int, or an integer which was
* previously returned but has since been freed.
*/
static int __guac_pool_next_int(guac_pool* pool) {
int value;
/* Acquire exclusive access */
pthread_mutex_lock(&(pool->__lock));
/* It's unlikely that any usage of guac_pool will ever manage to reach
* INT_MAX concurrent requests for integers, but we definitely should bail
* out if ever this does happen. Tracing this sort of issue down would be
* extremely difficult without fail-fast behavior. */
GUAC_ASSERT(pool->__next_value < INT_MAX);
GUAC_ASSERT(pool->active < INT_MAX);
pool->active++;
/* If more integers are needed, return a new one. */
if (pool->__head == NULL || pool->__next_value < pool->min_size) {
if (pool->__head == NULL || pool->__next_value < pool->min_size)
value = pool->__next_value++;
pthread_mutex_unlock(&(pool->__lock));
return value;
}
/* Otherwise, remove first integer. */
value = pool->__head->value;
/* If only one element exists, reset pool to empty. */
if (pool->__tail == pool->__head) {
guac_mem_free(pool->__head);
pool->__head = NULL;
pool->__tail = NULL;
}
/* Otherwise, advance head. */
/* Otherwise, reuse a previously freed integer */
else {
guac_pool_int* old_head = pool->__head;
pool->__head = old_head->__next;
guac_mem_free(old_head);
value = pool->__head->value;
/* If only one element exists, reset pool to empty. */
if (pool->__tail == pool->__head) {
guac_mem_free(pool->__head);
pool->__head = NULL;
pool->__tail = NULL;
}
/* Otherwise, advance head. */
else {
guac_pool_int* old_head = pool->__head;
pool->__head = old_head->__next;
guac_mem_free(old_head);
}
}
/* Return retrieved value. */
pthread_mutex_unlock(&(pool->__lock));
/* Again, this should never happen and would be a sign of some fairly
* fundamental assumption failing. It's important for such things to fail
* fast. */
GUAC_ASSERT(value >= 0);
return value;
}
int guac_pool_next_int(guac_pool* pool) {
pthread_mutex_lock(&(pool->__lock));
int value = __guac_pool_next_int(pool);
pthread_mutex_unlock(&(pool->__lock));
return value;
}
int guac_pool_next_int_below(guac_pool* pool, int limit) {
pthread_mutex_lock(&(pool->__lock));
int value;
/* Explicitly bail out now if there we would need to return a new integer,
* but can't without reaching the given limit */
if (pool->active >= limit || (pool->__next_value >= limit && pool->__head == NULL)) {
value = -1;
}
/* In all other cases, attempt to obtain the requested integer (either
* reusing a freed integer or allocating a new one), but verify that some
* fundamental misuse of guac_pool hasn't resulted in values defying
* expectations */
else {
value = __guac_pool_next_int(pool);
GUAC_ASSERT(value < limit);
}
pthread_mutex_unlock(&(pool->__lock));
return value;
}
int guac_pool_next_int_below_or_die(guac_pool* pool, int limit) {
int value = guac_pool_next_int_below(pool, limit);
/* Abort current process entirely if no integer can be obtained without
* reaching the given limit */
GUAC_ASSERT(value >= 0);
return value;
}
void guac_pool_free_int(guac_pool* pool, int value) {
@ -117,6 +192,7 @@ void guac_pool_free_int(guac_pool* pool, int value) {
/* Acquire exclusive access */
pthread_mutex_lock(&(pool->__lock));
GUAC_ASSERT(pool->active > 0);
pool->active--;
/* If pool empty, store as sole entry. */

View File

@ -112,7 +112,8 @@ guac_stream* guac_user_alloc_stream(guac_user* user) {
return NULL;
/* Allocate stream */
stream_index = guac_pool_next_int(user->__stream_pool);
stream_index = guac_pool_next_int_below_or_die(user->__stream_pool,
GUAC_USER_MAX_STREAMS);
/* Initialize stream with even index (odd indices are client-level) */
allocd_stream = &(user->__output_streams[stream_index]);
@ -146,7 +147,8 @@ guac_object* guac_user_alloc_object(guac_user* user) {
return NULL;
/* Allocate object */
object_index = guac_pool_next_int(user->__object_pool);
object_index = guac_pool_next_int_below_or_die(user->__object_pool,
GUAC_USER_MAX_OBJECTS);
/* Initialize object */
allocd_object = &(user->__objects[object_index]);

View File

@ -361,7 +361,7 @@ int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
}
/* Get file ID, init file */
file_id = guac_pool_next_int(fs->file_id_pool);
file_id = guac_pool_next_int_below_or_die(fs->file_id_pool, GUAC_RDP_FS_MAX_FILES);
file = &(fs->files[file_id]);
file->id = file_id;
file->fd = fd;