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
312307void 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-
530466int 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 */
0 commit comments