48
48
#include <string.h>
49
49
50
50
/**
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
52
52
* synchronized and emptied (250 milliseconds aka 1/4 second).
53
53
*/
54
- #define GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL 250000000
54
+ #define GUAC_CLIENT_PENDING_USERS_REFRESH_INTERVAL 250
55
55
56
56
/**
57
57
* 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) {
160
160
* Promote all pending users to full users, calling the join pending handler
161
161
* before, if any.
162
162
*
163
- * @param data
163
+ * @param client
164
164
* The client for which all pending users should be promoted.
165
165
*/
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 ) {
185
167
186
168
/* Acquire the lock for reading and modifying the list of pending users */
187
169
guac_rwlock_acquire_write_lock (& (client -> __pending_users_lock ));
@@ -244,10 +226,29 @@ static void guac_client_promote_pending_users(union sigval data) {
244
226
* to ensure that all users are always on exactly one of these lists) */
245
227
guac_rwlock_release_lock (& (client -> __pending_users_lock ));
246
228
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 ;
251
252
252
253
}
253
254
@@ -295,12 +296,6 @@ guac_client* guac_client_alloc() {
295
296
guac_rwlock_init (& (client -> __users_lock ));
296
297
guac_rwlock_init (& (client -> __pending_users_lock ));
297
298
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
-
304
299
/* Set up broadcast sockets */
305
300
client -> socket = guac_socket_broadcast (client );
306
301
client -> pending_socket = guac_socket_broadcast_pending (client );
@@ -311,6 +306,9 @@ guac_client* guac_client_alloc() {
311
306
312
307
void guac_client_free (guac_client * client ) {
313
308
309
+ /* Ensure that anything waiting for the client can begin shutting down */
310
+ guac_client_stop (client );
311
+
314
312
/* Acquire write locks before referencing user pointers */
315
313
guac_rwlock_acquire_write_lock (& (client -> __pending_users_lock ));
316
314
guac_rwlock_acquire_write_lock (& (client -> __users_lock ));
@@ -323,6 +321,11 @@ void guac_client_free(guac_client* client) {
323
321
while (client -> __users != NULL )
324
322
guac_client_remove_user (client , client -> __users );
325
323
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
+
326
329
/* Release the locks */
327
330
guac_rwlock_release_lock (& (client -> __users_lock ));
328
331
guac_rwlock_release_lock (& (client -> __pending_users_lock ));
@@ -354,19 +357,6 @@ void guac_client_free(guac_client* client) {
354
357
guac_client_log (client , GUAC_LOG_ERROR , "Unable to close plugin: %s" , dlerror ());
355
358
}
356
359
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
-
370
360
/* Destroy the reentrant read-write locks */
371
361
guac_rwlock_destroy (& (client -> __users_lock ));
372
362
guac_rwlock_destroy (& (client -> __pending_users_lock ));
@@ -444,12 +434,19 @@ void guac_client_abort(guac_client* client, guac_protocol_status status,
444
434
* @param user
445
435
* The user to add to the pending list.
446
436
*/
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 ) {
449
439
450
440
/* Acquire the lock for modifying the list of pending users */
451
441
guac_rwlock_acquire_write_lock (& (client -> __pending_users_lock ));
452
442
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
+
453
450
user -> __prev = NULL ;
454
451
user -> __next = client -> __pending_users ;
455
452
@@ -466,82 +463,8 @@ static void guac_client_add_pending_user(
466
463
467
464
}
468
465
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
-
530
466
int guac_client_add_user (guac_client * client , guac_user * user , int argc , char * * argv ) {
531
467
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
-
545
468
int retval = 0 ;
546
469
547
470
/* Call handler, if defined */
0 commit comments