~ubuntu-branches/ubuntu/quantal/open-vm-tools/quantal-201210021442

« back to all changes in this revision

Viewing changes to services/vmtoolsd/threadPool.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-03-31 14:20:05 UTC
  • mfrom: (1.4.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110331142005-3n9red91p7ogkweo
Tags: 2011.03.28-387002-0ubuntu1
* Merge latest upstream git tag.  This has the unlocked_ioctl change
  needed to fix dkms build failures (LP: #727342)
* Changes in debian/rules:
  - work around a bug in toolbox/Makefile, where install-exec-hook is
    not happening.  This needs to get fixed the right way.
  - don't install 'vmware-user' which seems to no longer exist
  - move /etc/xdg into open-vm-toolbox (which should be done using .install)
* debian/open-vm-tools.init: add 'modprobe [-r] vmblock'. (LP: #332323)
* debian/rules and debian/open-vm-toolbox.lintian-overrides:
  - Make vmware-user-suid-wrapper suid-root (LP: #332323)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*********************************************************
 
2
 * Copyright (C) 2010 VMware, Inc. All rights reserved.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify it
 
5
 * under the terms of the GNU Lesser General Public License as published
 
6
 * by the Free Software Foundation version 2.1 and no later version.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful, but
 
9
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 
10
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
 
11
 * License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU Lesser General Public License
 
14
 * along with this program; if not, write to the Free Software Foundation, Inc.,
 
15
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
 
16
 *
 
17
 *********************************************************/
 
18
 
 
19
/**
 
20
 * @file threadPool.c
 
21
 *
 
22
 * Implementation of the shared thread pool defined in threadPool.h.
 
23
 */
 
24
 
 
25
#include <limits.h>
 
26
#include <string.h>
 
27
#include "vmware.h"
 
28
#include "toolsCoreInt.h"
 
29
#include "serviceObj.h"
 
30
#include "vmware/tools/threadPool.h"
 
31
 
 
32
#define DEFAULT_MAX_IDLE_TIME       5000
 
33
#define DEFAULT_MAX_THREADS         5
 
34
#define DEFAULT_MAX_UNUSED_THREADS  0
 
35
 
 
36
typedef struct ThreadPoolState {
 
37
   ToolsCorePool  funcs;
 
38
   gboolean       active;
 
39
   ToolsAppCtx   *ctx;
 
40
   GThreadPool   *pool;
 
41
   GQueue        *workQueue;
 
42
   GPtrArray     *threads;
 
43
   GMutex        *lock;
 
44
   guint          nextWorkId;
 
45
} ThreadPoolState;
 
46
 
 
47
 
 
48
typedef struct WorkerTask {
 
49
   guint             id;
 
50
   guint             srcId;
 
51
   ToolsCorePoolCb   cb;
 
52
   gpointer          data;
 
53
   GDestroyNotify    dtor;
 
54
} WorkerTask;
 
55
 
 
56
 
 
57
typedef struct StandaloneTask {
 
58
   gboolean          active;
 
59
   ToolsCorePoolCb   cb;
 
60
   ToolsCorePoolCb   interrupt;
 
61
   gpointer          data;
 
62
   GThread          *thread;
 
63
   GDestroyNotify    dtor;
 
64
} StandaloneTask;
 
65
 
 
66
 
 
67
static ThreadPoolState gState;
 
68
 
 
69
 
 
70
/*
 
71
 *******************************************************************************
 
72
 * ToolsCorePoolCompareTask --                                            */ /**
 
73
 *
 
74
 * Compares two WorkerTask instances.
 
75
 *
 
76
 * @param[in] p1  Pointer to WorkerTask.
 
77
 * @param[in] p2  Pointer to WorkerTask.
 
78
 *
 
79
 * @return > 0, 0, < 0  if p1's ID is less than, equal, or greater than p2's.
 
80
 *
 
81
 *******************************************************************************
 
82
 */
 
83
 
 
84
static gint
 
85
ToolsCorePoolCompareTask(gconstpointer p1,
 
86
                         gconstpointer p2)
 
87
{
 
88
   const WorkerTask *t1 = p1;
 
89
   const WorkerTask *t2 = p2;
 
90
 
 
91
   if (t1 != NULL && t2 != NULL) {
 
92
      return (t2->id - t1->id);
 
93
   }
 
94
 
 
95
   if (t1 == NULL && t2 == NULL) {
 
96
      return 0;
 
97
   }
 
98
 
 
99
   return (t1 != NULL) ? -1 : 1;
 
100
}
 
101
 
 
102
 
 
103
/*
 
104
 *******************************************************************************
 
105
 * ToolsCorePoolDestroyThread --                                          */ /**
 
106
 *
 
107
 * Releases resources associated with a StandaloneTask, joining the thread
 
108
 * that's executing it.
 
109
 *
 
110
 * @param[in] data   A StandaloneTask.
 
111
 *
 
112
 *******************************************************************************
 
113
 */
 
114
 
 
115
static void
 
116
ToolsCorePoolDestroyThread(gpointer data)
 
117
{
 
118
   StandaloneTask *task = data;
 
119
   g_thread_join(task->thread);
 
120
   if (task->dtor != NULL) {
 
121
      task->dtor(task->data);
 
122
   }
 
123
   g_free(task);
 
124
}
 
125
 
 
126
 
 
127
/*
 
128
 *******************************************************************************
 
129
 * ToolsCorePoolDestroyTask --                                            */ /**
 
130
 *
 
131
 * Frees memory associated with a WorkerTask, calling its destructor if one is
 
132
 * registered.
 
133
 *
 
134
 * @param[in] data   A WorkerTask.
 
135
 *
 
136
 *******************************************************************************
 
137
 */
 
138
 
 
139
static void
 
140
ToolsCorePoolDestroyTask(gpointer data)
 
141
{
 
142
   WorkerTask *work = data;
 
143
   if (work->dtor != NULL) {
 
144
      work->dtor(work->data);
 
145
   }
 
146
   g_free(work);
 
147
}
 
148
 
 
149
 
 
150
/*
 
151
 *******************************************************************************
 
152
 * ToolsCorePoolDoWork --                                                 */ /**
 
153
 *
 
154
 * Execute a work item.
 
155
 *
 
156
 * @param[in] data   A WorkerTask.
 
157
 *
 
158
 * @return FALSE
 
159
 *
 
160
 *******************************************************************************
 
161
 */
 
162
 
 
163
static gboolean
 
164
ToolsCorePoolDoWork(gpointer data)
 
165
{
 
166
   WorkerTask *work = data;
 
167
 
 
168
   /*
 
169
    * In single threaded mode, remove the task being executed from the queue.
 
170
    * In multi-threaded mode, the thread pool callback already did this.
 
171
    */
 
172
   if (gState.pool == NULL) {
 
173
      g_mutex_lock(gState.lock);
 
174
      g_queue_remove(gState.workQueue, work);
 
175
      g_mutex_unlock(gState.lock);
 
176
   }
 
177
 
 
178
   work->cb(gState.ctx, work->data);
 
179
   return FALSE;
 
180
}
 
181
 
 
182
 
 
183
/*
 
184
 *******************************************************************************
 
185
 * ToolsCorePoolNoOp --                                                   */ /**
 
186
 *
 
187
 * Idle callback for destroying a standalone thread. Does nothing, since the
 
188
 * actual destruction is done by ToolsCorePoolDestroyThread.
 
189
 *
 
190
 * @param[in] data   Unused.
 
191
 *
 
192
 * @return FALSE
 
193
 *
 
194
 *******************************************************************************
 
195
 */
 
196
 
 
197
static gboolean
 
198
ToolsCorePoolNoOp(gpointer data)
 
199
{
 
200
   return FALSE;
 
201
}
 
202
 
 
203
 
 
204
/*
 
205
 *******************************************************************************
 
206
 * ToolsCorePoolRunThread --                                              */ /**
 
207
 *
 
208
 * Standalone thread runner. Executes the task associated with the thread, and
 
209
 * schedule a task to clean up the thread state when done.
 
210
 *
 
211
 * @param[in] data   A StandaloneTask.
 
212
 *
 
213
 * @return NULL
 
214
 *
 
215
 *******************************************************************************
 
216
 */
 
217
 
 
218
static gpointer
 
219
ToolsCorePoolRunThread(gpointer data)
 
220
{
 
221
   StandaloneTask *task = data;
 
222
 
 
223
   task->cb(gState.ctx, task->data);
 
224
   task->active = FALSE;
 
225
 
 
226
   g_mutex_lock(gState.lock);
 
227
   /* If not active, the shutdown function will clean things up. */
 
228
   if (gState.active) {
 
229
      g_ptr_array_remove(gState.threads, task);
 
230
      g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
 
231
                      ToolsCorePoolNoOp,
 
232
                      task,
 
233
                      ToolsCorePoolDestroyThread);
 
234
   }
 
235
   g_mutex_unlock(gState.lock);
 
236
 
 
237
   return NULL;
 
238
}
 
239
 
 
240
 
 
241
/*
 
242
 *******************************************************************************
 
243
 * ToolsCorePoolRunWorker --                                              */ /**
 
244
 *
 
245
 * Thread pool callback function. Dequeues the next work item from the work
 
246
 * queue and execute it.
 
247
 *
 
248
 * @param[in] state        Description of state.
 
249
 * @param[in] clientData   Description of clientData.
 
250
 *
 
251
 *******************************************************************************
 
252
 */
 
253
 
 
254
static void
 
255
ToolsCorePoolRunWorker(gpointer state,
 
256
                       gpointer clientData)
 
257
{
 
258
   WorkerTask *work;
 
259
 
 
260
   g_mutex_lock(gState.lock);
 
261
   work = g_queue_pop_tail(gState.workQueue);
 
262
   g_mutex_unlock(gState.lock);
 
263
 
 
264
   ASSERT(work != NULL);
 
265
 
 
266
   ToolsCorePoolDoWork(work);
 
267
   ToolsCorePoolDestroyTask(work);
 
268
}
 
269
 
 
270
 
 
271
/*
 
272
 *******************************************************************************
 
273
 * ToolsCorePoolSubmit --                                                 */ /**
 
274
 *
 
275
 * Submits a new task for execution in one of the shared worker threads.
 
276
 *
 
277
 * @see ToolsCorePool_SubmitTask()
 
278
 *
 
279
 * @param[in] ctx    Application context.
 
280
 * @param[in] cb     Function to execute the task.
 
281
 * @param[in] data   Opaque data for the task.
 
282
 * @param[in] dtor   Destructor for the task data.
 
283
 *
 
284
 * @return New task's ID, or 0 on error.
 
285
 *
 
286
 *******************************************************************************
 
287
 */
 
288
 
 
289
static guint
 
290
ToolsCorePoolSubmit(ToolsAppCtx *ctx,
 
291
                    ToolsCorePoolCb cb,
 
292
                    gpointer data,
 
293
                    GDestroyNotify dtor)
 
294
{
 
295
   guint id = 0;
 
296
   WorkerTask *task = g_malloc0(sizeof *task);
 
297
 
 
298
   task->srcId = 0;
 
299
   task->cb = cb;
 
300
   task->data = data;
 
301
   task->dtor = dtor;
 
302
 
 
303
   g_mutex_lock(gState.lock);
 
304
 
 
305
   if (!gState.active) {
 
306
      g_free(task);
 
307
      goto exit;
 
308
   }
 
309
 
 
310
   /*
 
311
    * XXX: a reeeeeeeeeally long running task could cause clashes (e.g., reusing
 
312
    * the same task ID after the counter wraps). That shouldn't really happen in
 
313
    * practice (and is an abuse of the thread pool, and could cause issues if
 
314
    * someone sets the pool size to 0 or 1), but it might be good to have more
 
315
    * fail-safe code here.
 
316
    */
 
317
   if (gState.nextWorkId + 1 == UINT_MAX) {
 
318
      task->id = UINT_MAX;
 
319
      gState.nextWorkId = 0;
 
320
   } else {
 
321
      task->id = ++gState.nextWorkId;
 
322
   }
 
323
 
 
324
   id = task->id;
 
325
 
 
326
   /*
 
327
    * We always add the task to the queue, even in single threaded mode, so
 
328
    * that it can be canceled. In single threaded mode, it's unlikely someone
 
329
    * will be able to cancel it before it runs, but they can try.
 
330
    */
 
331
   g_queue_push_head(gState.workQueue, task);
 
332
 
 
333
   if (gState.pool != NULL) {
 
334
      GError *err = NULL;
 
335
 
 
336
      /* The client data pointer is bogus, just to avoid passing NULL. */
 
337
      g_thread_pool_push(gState.pool, &gState, &err);
 
338
      if (err == NULL) {
 
339
         goto exit;
 
340
      } else {
 
341
         g_warning("error sending work request, executing in service thread: %s",
 
342
                   err->message);
 
343
         g_clear_error(&err);
 
344
      }
 
345
   }
 
346
 
 
347
   /* Run the task in the service's thread. */
 
348
   task->srcId = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
 
349
                                 ToolsCorePoolDoWork,
 
350
                                 task,
 
351
                                 ToolsCorePoolDestroyTask);
 
352
 
 
353
exit:
 
354
   g_mutex_unlock(gState.lock);
 
355
   return id;
 
356
}
 
357
 
 
358
 
 
359
/*
 
360
 *******************************************************************************
 
361
 * ToolsCorePoolCancel --                                                 */ /**
 
362
 *
 
363
 * Cancels a queue task.
 
364
 *
 
365
 * @see ToolsCorePool_CancelTask()
 
366
 *
 
367
 * @param[in] id  Task ID.
 
368
 *
 
369
 *******************************************************************************
 
370
 */
 
371
 
 
372
static void
 
373
ToolsCorePoolCancel(guint id)
 
374
{
 
375
   GList *taskLnk;
 
376
   WorkerTask *task = NULL;
 
377
   WorkerTask search = { id, };
 
378
 
 
379
   g_return_if_fail(id != 0);
 
380
 
 
381
   g_mutex_lock(gState.lock);
 
382
   if (!gState.active) {
 
383
      goto exit;
 
384
   }
 
385
 
 
386
   taskLnk = g_queue_find_custom(gState.workQueue, &search, ToolsCorePoolCompareTask);
 
387
   if (taskLnk != NULL) {
 
388
      task = taskLnk->data;
 
389
      g_queue_delete_link(gState.workQueue, taskLnk);
 
390
   }
 
391
 
 
392
exit:
 
393
   g_mutex_unlock(gState.lock);
 
394
 
 
395
   if (task != NULL) {
 
396
      if (task->srcId > 0) {
 
397
         g_source_remove(task->srcId);
 
398
      } else {
 
399
         ToolsCorePoolDestroyTask(task);
 
400
      }
 
401
   }
 
402
}
 
403
 
 
404
 
 
405
/*
 
406
 *******************************************************************************
 
407
 * ToolsCorePoolStart --                                                  */ /**
 
408
 *
 
409
 * Start a new task in a dedicated thread.
 
410
 *
 
411
 * @see ToolsCorePool_StartThread()
 
412
 *
 
413
 * @param[in] ctx       Application context.
 
414
 * @param[in] cb        Callback that executes the task.
 
415
 * @param[in] interrupt Callback that interrupts the task.
 
416
 * @param[in] data      Opaque data.
 
417
 * @param[in] dtor      Destructor for the task data.
 
418
 *
 
419
 * @return TRUE iff thread was successfully started.
 
420
 *
 
421
 *******************************************************************************
 
422
 */
 
423
 
 
424
static gboolean
 
425
ToolsCorePoolStart(ToolsAppCtx *ctx,
 
426
                   ToolsCorePoolCb cb,
 
427
                   ToolsCorePoolCb interrupt,
 
428
                   gpointer data,
 
429
                   GDestroyNotify dtor)
 
430
{
 
431
   GError *err = NULL;
 
432
   StandaloneTask *task = NULL;
 
433
 
 
434
   g_mutex_lock(gState.lock);
 
435
   if (!gState.active) {
 
436
      goto exit;
 
437
   }
 
438
 
 
439
   task = g_malloc0(sizeof *task);
 
440
   task->active = TRUE;
 
441
   task->cb = cb;
 
442
   task->interrupt = interrupt;
 
443
   task->data = data;
 
444
   task->dtor = dtor;
 
445
   task->thread = g_thread_create(ToolsCorePoolRunThread, task, TRUE, &err);
 
446
 
 
447
   if (err == NULL) {
 
448
      g_ptr_array_add(gState.threads, task);
 
449
   } else {
 
450
      g_warning("failed to start thread: %s.", err->message);
 
451
      g_clear_error(&err);
 
452
      g_free(task);
 
453
      task = NULL;
 
454
   }
 
455
 
 
456
exit:
 
457
   g_mutex_unlock(gState.lock);
 
458
   return task != NULL;
 
459
}
 
460
 
 
461
 
 
462
/*
 
463
 *******************************************************************************
 
464
 * ToolsCorePool_Init --                                                  */ /**
 
465
 *
 
466
 * Initializes the shared thread pool. Reads configuration data from the
 
467
 * container-specific section of the config dictionary, so different containers
 
468
 * can have different configuration. Exports the thread pool functions through
 
469
 * the service's object.
 
470
 *
 
471
 * @param[in] ctx Application context.
 
472
 *
 
473
 *******************************************************************************
 
474
 */
 
475
 
 
476
void
 
477
ToolsCorePool_Init(ToolsAppCtx *ctx)
 
478
{
 
479
   gint maxThreads;
 
480
   GError *err = NULL;
 
481
 
 
482
   ToolsServiceProperty prop = { TOOLS_CORE_PROP_TPOOL };
 
483
 
 
484
   gState.funcs.submit = ToolsCorePoolSubmit;
 
485
   gState.funcs.cancel = ToolsCorePoolCancel;
 
486
   gState.funcs.start = ToolsCorePoolStart;
 
487
   gState.ctx = ctx;
 
488
 
 
489
   maxThreads = g_key_file_get_integer(ctx->config, ctx->name,
 
490
                                       "pool.maxThreads", &err);
 
491
   if (err != NULL) {
 
492
      maxThreads = DEFAULT_MAX_THREADS;
 
493
      g_clear_error(&err);
 
494
   }
 
495
 
 
496
   if (maxThreads > 0) {
 
497
      gState.pool = g_thread_pool_new(ToolsCorePoolRunWorker,
 
498
                                      NULL, maxThreads, FALSE, &err);
 
499
      if (err == NULL) {
 
500
#if GLIB_CHECK_VERSION(2, 10, 0)
 
501
         gint maxIdleTime;
 
502
         gint maxUnused;
 
503
 
 
504
         maxIdleTime = g_key_file_get_integer(ctx->config, ctx->name,
 
505
                                              "pool.maxIdleTime", &err);
 
506
         if (err != NULL || maxIdleTime <= 0) {
 
507
            maxIdleTime = DEFAULT_MAX_IDLE_TIME;
 
508
            g_clear_error(&err);
 
509
         }
 
510
 
 
511
         maxUnused = g_key_file_get_integer(ctx->config, ctx->name,
 
512
                                            "pool.maxUnusedThreads", &err);
 
513
         if (err != NULL || maxUnused < 0) {
 
514
            maxUnused = DEFAULT_MAX_UNUSED_THREADS;
 
515
            g_clear_error(&err);
 
516
         }
 
517
 
 
518
         g_thread_pool_set_max_idle_time(maxIdleTime);
 
519
         g_thread_pool_set_max_unused_threads(maxUnused);
 
520
#endif
 
521
      } else {
 
522
         g_warning("error initializing thread pool, running single threaded: %s",
 
523
                   err->message);
 
524
         g_clear_error(&err);
 
525
      }
 
526
   }
 
527
 
 
528
   gState.active = TRUE;
 
529
   gState.lock = g_mutex_new();
 
530
   gState.threads = g_ptr_array_new();
 
531
   gState.workQueue = g_queue_new();
 
532
 
 
533
   ToolsCoreService_RegisterProperty(ctx->serviceObj, &prop);
 
534
   g_object_set(ctx->serviceObj, TOOLS_CORE_PROP_TPOOL, &gState.funcs, NULL);
 
535
}
 
536
 
 
537
 
 
538
/*
 
539
 *******************************************************************************
 
540
 * ToolsCorePool_Shutdown --                                              */ /**
 
541
 *
 
542
 * Shuts down the shared thread pool. This function will interrupt any running
 
543
 * threads (by calling their registered interrupt function), and wait for all
 
544
 * running tasks to finish before cleaning the remaining tasks and shared state.
 
545
 *
 
546
 * @param[in] ctx Application context.
 
547
 *
 
548
 *******************************************************************************
 
549
 */
 
550
 
 
551
void
 
552
ToolsCorePool_Shutdown(ToolsAppCtx *ctx)
 
553
{
 
554
   guint i;
 
555
 
 
556
   g_mutex_lock(gState.lock);
 
557
   gState.active = FALSE;
 
558
   g_mutex_unlock(gState.lock);
 
559
 
 
560
   /* Notify all spawned threads to stop. */
 
561
   for (i = 0; i < gState.threads->len; i++) {
 
562
      StandaloneTask *task = g_ptr_array_index(gState.threads, i);
 
563
      if (task->active && task->interrupt) {
 
564
         task->interrupt(gState.ctx, task->data);
 
565
      }
 
566
   }
 
567
 
 
568
   /* Stop the thread pool. */
 
569
   if (gState.pool != NULL) {
 
570
      g_thread_pool_free(gState.pool, TRUE, TRUE);
 
571
   }
 
572
 
 
573
   /* Join all spawned threads. */
 
574
   for (i = 0; i < gState.threads->len; i++) {
 
575
      StandaloneTask *task = g_ptr_array_index(gState.threads, i);
 
576
      ToolsCorePoolDestroyThread(task);
 
577
   }
 
578
 
 
579
   /* Destroy all pending tasks. */
 
580
   while (1) {
 
581
      WorkerTask *task = g_queue_pop_tail(gState.workQueue);
 
582
      if (task != NULL) {
 
583
         ToolsCorePoolDestroyTask(task);
 
584
      } else {
 
585
         break;
 
586
      }
 
587
   }
 
588
 
 
589
   /* Cleanup. */
 
590
   g_ptr_array_free(gState.threads, TRUE);
 
591
   g_queue_free(gState.workQueue);
 
592
   g_mutex_free(gState.lock);
 
593
   memset(&gState, 0, sizeof gState);
 
594
   g_object_set(ctx->serviceObj, TOOLS_CORE_PROP_TPOOL, NULL, NULL);
 
595
}
 
596