Skip to content

Commit e08b670

Browse files
committed
GUACAMOLE-377: Migrate from timer to thread for pending users.
1 parent b70d601 commit e08b670

File tree

2 files changed

+51
-131
lines changed

2 files changed

+51
-131
lines changed

src/libguac/client.c

+44-121
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@
4848
#include <string.h>
4949

5050
/**
51-
* The number of nanoseconds between times that the pending users list will be
51+
* The number of milliseconds between times that the pending users list will be
5252
* synchronized and emptied (250 milliseconds aka 1/4 second).
5353
*/
54-
#define GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL 250000000
54+
#define GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL 250
5555

5656
/**
5757
* A value that indicates that the pending users timer has yet to be
@@ -160,28 +160,10 @@ void guac_client_free_stream(guac_client* client, guac_stream* stream) {
160160
* Promote all pending users to full users, calling the join pending handler
161161
* before, if any.
162162
*
163-
* @param data
163+
* @param client
164164
* The client for which all pending users should be promoted.
165165
*/
166-
static void guac_client_promote_pending_users(union sigval data) {
167-
168-
guac_client* client = (guac_client*) data.sival_ptr;
169-
170-
pthread_mutex_lock(&(client->__pending_users_timer_mutex));
171-
172-
/* Check if the previous instance of this handler is still running */
173-
int already_running = (
174-
client->__pending_users_timer_state
175-
== GUAC_CLIENT_PENDING_TIMER_TRIGGERED);
176-
177-
/* Mark the handler as running if it isn't already */
178-
client->__pending_users_timer_state = GUAC_CLIENT_PENDING_TIMER_TRIGGERED;
179-
180-
pthread_mutex_unlock(&(client->__pending_users_timer_mutex));
181-
182-
/* Do not start the handler if the previous instance is still running */
183-
if (already_running)
184-
return;
166+
static void guac_client_promote_pending_users(guac_client* client) {
185167

186168
/* Acquire the lock for reading and modifying the list of pending users */
187169
guac_rwlock_acquire_write_lock(&(client->__pending_users_lock));
@@ -244,10 +226,29 @@ static void guac_client_promote_pending_users(union sigval data) {
244226
* to ensure that all users are always on exactly one of these lists) */
245227
guac_rwlock_release_lock(&(client->__pending_users_lock));
246228

247-
/* Mark the handler as complete so the next instance can run */
248-
pthread_mutex_lock(&(client->__pending_users_timer_mutex));
249-
client->__pending_users_timer_state = GUAC_CLIENT_PENDING_TIMER_REGISTERED;
250-
pthread_mutex_unlock(&(client->__pending_users_timer_mutex));
229+
}
230+
231+
/**
232+
* Thread that periodically checks for users that have requested to join the
233+
* current connection (pending users). The check is performed every
234+
* GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL milliseconds.
235+
*
236+
* @param data
237+
* A pointer to the guac_client associated with the connection.
238+
*
239+
* @return
240+
* Always NULL.
241+
*/
242+
static void* guac_client_pending_users_thread(void* data) {
243+
244+
guac_client* client = (guac_client*) data;
245+
246+
while (client->state == GUAC_CLIENT_RUNNING) {
247+
guac_client_promote_pending_users(client);
248+
guac_timestamp_msleep(GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL);
249+
}
250+
251+
return NULL;
251252

252253
}
253254

@@ -295,12 +296,6 @@ guac_client* guac_client_alloc() {
295296
guac_rwlock_init(&(client->__users_lock));
296297
guac_rwlock_init(&(client->__pending_users_lock));
297298

298-
/* The timer will be lazily created in the child process */
299-
client->__pending_users_timer_state = GUAC_CLIENT_PENDING_TIMER_UNREGISTERED;
300-
301-
/* Set up the pending user promotion mutex */
302-
pthread_mutex_init(&(client->__pending_users_timer_mutex), NULL);
303-
304299
/* Set up broadcast sockets */
305300
client->socket = guac_socket_broadcast(client);
306301
client->pending_socket = guac_socket_broadcast_pending(client);
@@ -311,6 +306,9 @@ guac_client* guac_client_alloc() {
311306

312307
void guac_client_free(guac_client* client) {
313308

309+
/* Ensure that anything waiting for the client can begin shutting down */
310+
guac_client_stop(client);
311+
314312
/* Acquire write locks before referencing user pointers */
315313
guac_rwlock_acquire_write_lock(&(client->__pending_users_lock));
316314
guac_rwlock_acquire_write_lock(&(client->__users_lock));
@@ -323,6 +321,11 @@ void guac_client_free(guac_client* client) {
323321
while (client->__users != NULL)
324322
guac_client_remove_user(client, client->__users);
325323

324+
/* Clean up the thread monitoring for new pending users, if it's been
325+
* started */
326+
if (client->__pending_users_thread_started)
327+
pthread_join(client->__pending_users_thread, NULL);
328+
326329
/* Release the locks */
327330
guac_rwlock_release_lock(&(client->__users_lock));
328331
guac_rwlock_release_lock(&(client->__pending_users_lock));
@@ -354,19 +357,6 @@ void guac_client_free(guac_client* client) {
354357
guac_client_log(client, GUAC_LOG_ERROR, "Unable to close plugin: %s", dlerror());
355358
}
356359

357-
/* Find out if the pending user promotion timer was ever started */
358-
pthread_mutex_lock(&(client->__pending_users_timer_mutex));
359-
int was_started = (
360-
client->__pending_users_timer_state
361-
!= GUAC_CLIENT_PENDING_TIMER_UNREGISTERED);
362-
pthread_mutex_unlock(&(client->__pending_users_timer_mutex));
363-
364-
/* If the timer was registered, stop it before destroying the lock */
365-
if (was_started)
366-
timer_delete(client->__pending_users_timer);
367-
368-
pthread_mutex_destroy(&(client->__pending_users_timer_mutex));
369-
370360
/* Destroy the reentrant read-write locks */
371361
guac_rwlock_destroy(&(client->__users_lock));
372362
guac_rwlock_destroy(&(client->__pending_users_lock));
@@ -444,12 +434,19 @@ void guac_client_abort(guac_client* client, guac_protocol_status status,
444434
* @param user
445435
* The user to add to the pending list.
446436
*/
447-
static void guac_client_add_pending_user(
448-
guac_client* client, guac_user* user) {
437+
static void guac_client_add_pending_user(guac_client* client,
438+
guac_user* user) {
449439

450440
/* Acquire the lock for modifying the list of pending users */
451441
guac_rwlock_acquire_write_lock(&(client->__pending_users_lock));
452442

443+
/* Set up the pending user promotion mutex */
444+
if (!client->__pending_users_thread_started) {
445+
pthread_create(&client->__pending_users_thread, NULL,
446+
guac_client_pending_users_thread, (void*) client);
447+
client->__pending_users_thread_started = 1;
448+
}
449+
453450
user->__prev = NULL;
454451
user->__next = client->__pending_users;
455452

@@ -466,82 +463,8 @@ static void guac_client_add_pending_user(
466463

467464
}
468465

469-
/**
470-
* Periodically promote pending users to full users. Returns zero if the timer
471-
* is already running, or successfully created, or a non-zero value if the
472-
* timer could not be created and started.
473-
*
474-
* @param client
475-
* The guac client for which the new timer should be started, if not
476-
* already running.
477-
*
478-
* @return
479-
* Zero if the timer was successfully created and started, or a negative
480-
* value otherwise.
481-
*/
482-
static int guac_client_start_pending_users_timer(guac_client* client) {
483-
484-
pthread_mutex_lock(&(client->__pending_users_timer_mutex));
485-
486-
/* Return success if the timer is already created and running */
487-
if (client->__pending_users_timer_state
488-
!= GUAC_CLIENT_PENDING_TIMER_UNREGISTERED) {
489-
pthread_mutex_unlock(&(client->__pending_users_timer_mutex));
490-
return 0;
491-
}
492-
493-
/* Configure the timer to synchronize and clear the pending users */
494-
struct sigevent signal_config = {
495-
.sigev_notify = SIGEV_THREAD,
496-
.sigev_notify_function = guac_client_promote_pending_users,
497-
.sigev_value = { .sival_ptr = client }};
498-
499-
/* Create a timer to synchronize any pending users periodically */
500-
if (timer_create(
501-
CLOCK_MONOTONIC,
502-
&signal_config,
503-
&(client->__pending_users_timer))) {
504-
pthread_mutex_unlock(&(client->__pending_users_timer_mutex));
505-
return 1;
506-
}
507-
508-
/* Configure the pending users timer to run on the defined interval */
509-
struct itimerspec time_config = {
510-
.it_interval = { .tv_nsec = GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL },
511-
.it_value = { .tv_nsec = GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL }
512-
};
513-
514-
/* Start the timer */
515-
if (timer_settime(
516-
client->__pending_users_timer, 0, &time_config, NULL) < 0) {
517-
timer_delete(client->__pending_users_timer);
518-
pthread_mutex_unlock(&(client->__pending_users_timer_mutex));
519-
return 1;
520-
}
521-
522-
/* Mark the timer as registered but not yet running */
523-
client->__pending_users_timer_state = GUAC_CLIENT_PENDING_TIMER_REGISTERED;
524-
525-
pthread_mutex_unlock(&(client->__pending_users_timer_mutex));
526-
return 0;
527-
528-
}
529-
530466
int guac_client_add_user(guac_client* client, guac_user* user, int argc, char** argv) {
531467

532-
/* Create and start the timer if it hasn't already been initialized */
533-
if (guac_client_start_pending_users_timer(client)) {
534-
535-
/**
536-
*
537-
* If the timer could not be created, do not add the user - they cannot
538-
* be synchronized without the timer.
539-
*/
540-
guac_client_log(client, GUAC_LOG_ERROR,
541-
"Could not start pending user timer: %s.", strerror(errno));
542-
return 1;
543-
}
544-
545468
int retval = 0;
546469

547470
/* Call handler, if defined */

src/libguac/guacamole/client.h

+7-10
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ struct guac_client {
182182

183183
/**
184184
* Lock which is acquired when the pending users list is being manipulated,
185-
* or when the pending users list is being iterated.
185+
* or iterated, or when checking/altering the
186+
* __pending_users_thread_started flag.
186187
*/
187188
guac_rwlock __pending_users_lock;
188189

@@ -192,18 +193,14 @@ struct guac_client {
192193
* use within the client. This will be NULL until the first user joins
193194
* the connection, as it is lazily instantiated at that time.
194195
*/
195-
timer_t __pending_users_timer;
196+
pthread_t __pending_users_thread;
196197

197198
/**
198-
* A flag storing the current state of the pending users timer.
199+
* Whether the pending users thread has started for this guac_client. The
200+
* __pending_users_lock must be acquired before checking or altering this
201+
* value.
199202
*/
200-
int __pending_users_timer_state;
201-
202-
/**
203-
* A mutex that must be acquired before modifying or checking the value of
204-
* the timer state.
205-
*/
206-
pthread_mutex_t __pending_users_timer_mutex;
203+
int __pending_users_thread_started;
207204

208205
/**
209206
* The first user within the list of connected users who have not yet had

0 commit comments

Comments
 (0)