1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gnome-vfs-job-queue.c - Job queue for asynchronous GnomeVFSJobs
3 (version for POSIX threads).
5 Copyright (C) 2001 Free Software Foundation
7 The Gnome Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
12 The Gnome Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public
18 License along with the Gnome Library; see the file COPYING.LIB. If not,
19 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.
22 Author: László Péter <laca@ireland.sun.com> */
25 #include "gnome-vfs-job-queue.h"
26 #include "gnome-vfs-job-slave.h"
27 #include <libgnomevfs/gnome-vfs-job-limit.h>
29 #include <glib/gtree.h>
35 #define Q_DEBUG(x) g_print x
40 /* See the comment at job_can_start () for
41 an explanation of the following macros */
42 #ifndef DEFAULT_THREAD_COUNT_LIMIT
43 #define DEFAULT_THREAD_COUNT_LIMIT 10
46 #define LIMIT_FUNCTION_LOWER_BOUND 2 /* must NOT be more than DEFAULT_THREAD_COUNT_LIMIT */
47 #define LIMIT_FUNCTION_SPEED 7 /* must be more than 0 */
49 #if LIMIT_FUNCTION_LOWER_BOUND > DEFAULT_THREAD_COUNT_LIMIT
50 #error LIMIT_FUNCTION_LOWER_BOUND must not be more than DEFAULT_THREAD_COUNT_LIMIT
53 #if LIMIT_FUNCTION_SPEED <= 0
54 #error LIMIT_FUNCTION_SPEED must be more than 0
57 /* The maximum number of threads to use for async ops */
58 static int thread_count_limit;
60 /* This is the maximum number of threads reserved for higher priority jobs */
61 static float max_decrease;
63 typedef GTree JobQueueType;
65 /* This mutex protects these */
66 static GStaticMutex job_queue_lock = G_STATIC_MUTEX_INIT;
67 static JobQueueType *job_queue;
68 static int running_job_count;
71 static int job_queue_length;
75 typedef struct JobQueueKey {
81 key_compare (gconstpointer cast_to_key1, gconstpointer cast_to_key2, gpointer user_data)
83 JobQueueKey *key1 = (JobQueueKey *)cast_to_key1;
84 JobQueueKey *key2 = (JobQueueKey *)cast_to_key2;
86 /* Lower priority job comes first */
87 if (key1->priority > key2->priority) {
91 if (key1->priority < key2->priority) {
95 /* If the 2 priorities are the same then the
96 job with the lower job_id comes first.
98 job_ids are positive so this won't overflow.
100 return key1->job_id - key2->job_id;
104 value_destroy (gpointer cast_to_job)
106 _gnome_vfs_job_destroy ((GnomeVFSJob *)cast_to_job);
109 static JobQueueType *
112 return g_tree_new_full (key_compare, NULL, g_free, value_destroy);
116 job_queue_destroy (void)
118 g_tree_destroy (job_queue);
123 job_queue_add (GnomeVFSJob *job)
125 JobQueueKey *key = g_new (JobQueueKey, 1);
126 key->job_id = ++job_id;
127 key->priority = job->priority;
129 g_tree_insert (job_queue, key, job);
136 find_first_value (gpointer key, gpointer value, gpointer data)
138 *((GnomeVFSJob **)data) = value;
143 job_queue_get_first (void)
145 GnomeVFSJob *job = NULL;
148 g_tree_foreach (job_queue, find_first_value, &job);
155 find_first_key (gpointer key, gpointer value, gpointer data)
157 *((JobQueueKey **)data) = key;
162 job_queue_delete_first (void)
164 JobQueueKey *key = NULL;
166 g_tree_foreach (job_queue, find_first_key, &key);
167 g_tree_steal (job_queue, key);
176 _gnome_vfs_job_queue_init (void)
178 static gboolean queue_initialized = FALSE;
180 if (queue_initialized != TRUE) {
181 Q_DEBUG (("initializing the job queue (thread limit: %d)\n", DEFAULT_THREAD_COUNT_LIMIT));
182 thread_count_limit = DEFAULT_THREAD_COUNT_LIMIT;
183 max_decrease = (float)thread_count_limit - LIMIT_FUNCTION_LOWER_BOUND;
184 job_queue = job_queue_new ();
185 queue_initialized = TRUE;
189 /* This function implements a scheduling policy where a certain number
190 of threads is reserved for high priority jobs so they can start
191 immediately if needed. The lower the priority of the running jobs
192 the more threads are reserved. So the actual limit on running jobs
193 is a function of the priority of the job to be started.
194 This function converges to LIMIT_FUNCTION_LOWER_BOUND (i.e. this
195 will be the limit belonging to the lowest priority jobs.)
196 The speed of convergence is determined by LIMIT_FUNCTION_SPEED.
197 For negative priority jobs the limit equals to thread_count_limit.
199 Note that thread_count_limit can be queried/set runtime using the
200 gnome_vfs_async_job_{get,set}_limit functions.
202 The formula is as follows:
204 max_decrease = thread_count_limit - LIMIT_FUNCTION_LOWER_BOUND
206 This is the maximum difference between the limit function and the
210 max jobs = thread_count_limit - floor (--------------------------)
211 LIMIT_FUNCTION_SPEED + p
213 This table shows some limits belonging to the default parameters:
215 priority of the | max number
216 job to start | of jobs
217 -----------------+-----------
228 For example a job with a priority of 3 will NOT be started if
229 there are at least 8 jobs already running.
232 job_can_start (int priority)
234 int transformed_priority;
237 /* Move the highest priority to the zero point */
238 transformed_priority = priority + GNOME_VFS_PRIORITY_MIN;
240 if (running_job_count >= thread_count_limit) {
241 /* Max number of jobs are already running */
243 } else if (transformed_priority >= 0) {
244 /* Let's not allow low (i.e. positive) priority jobs to use up all the threads.
245 We reserve some threads for higher priority jobs.
246 The lower the priority to more threads are reserved.
248 The actual limit should the the thread count limit less a proportion
249 of the maximum decrease.
252 actual_limit = thread_count_limit - (int)(max_decrease * transformed_priority /
253 (LIMIT_FUNCTION_SPEED + transformed_priority));
255 if (actual_limit <= running_job_count) {
263 _gnome_vfs_job_queue_run (void)
265 GnomeVFSJob *job_to_run;
267 g_static_mutex_lock (&job_queue_lock);
270 Q_DEBUG (("job finished;\t\t\t\t %d jobs running, %d waiting\n",
274 job_to_run = job_queue_get_first ();
275 if (job_to_run != NULL) {
276 /* The queue is not empty */
277 if (job_can_start (job_to_run->priority)) {
279 job_queue_delete_first ();
280 Q_DEBUG (("taking a %2d priority job from the queue;"
281 " %d jobs running, %d waiting\n",
282 job_to_run->priority,
285 g_static_mutex_unlock (&job_queue_lock);
286 _gnome_vfs_job_create_slave (job_to_run);
288 g_static_mutex_unlock (&job_queue_lock);
289 Q_DEBUG (("waiting job is too low priority (%2d) to start;"
290 " %d jobs running, %d waiting\n",
291 job_to_run->priority,
296 g_static_mutex_unlock (&job_queue_lock);
297 Q_DEBUG (("the queue is empty;\t\t\t %d jobs running\n", running_job_count));
302 _gnome_vfs_job_schedule (GnomeVFSJob *job)
304 g_static_mutex_lock (&job_queue_lock);
305 if (!job_can_start (job->priority)) {
307 Q_DEBUG (("adding a %2d priority job to the queue;"
308 "\t %d jobs running, %d waiting\n",
312 g_static_mutex_unlock (&job_queue_lock);
315 Q_DEBUG (("starting a %2d priority job;\t\t %d jobs running, %d waiting\n",
319 g_static_mutex_unlock (&job_queue_lock);
320 _gnome_vfs_job_create_slave (job);
326 * gnome_vfs_async_set_job_limit:
327 * @limit: maximuum number of allowable threads
329 * Restrict the number of worker threads used by Async operations
333 gnome_vfs_async_set_job_limit (int limit)
335 if (limit < LIMIT_FUNCTION_LOWER_BOUND) {
336 g_warning ("Attempt to set the thread_count_limit below %d",
337 LIMIT_FUNCTION_LOWER_BOUND);
340 g_static_mutex_lock (&job_queue_lock);
341 thread_count_limit = limit;
342 max_decrease = (float)thread_count_limit - LIMIT_FUNCTION_LOWER_BOUND;
343 Q_DEBUG (("changing the thread count limit to %d\n", limit));
344 g_static_mutex_unlock (&job_queue_lock);
348 * gnome_vfs_async_get_job_limit:
350 * Get the current maximuum allowable number of
351 * worker threads for Asynch operations.
353 * Return value: current maximuum number of threads
356 gnome_vfs_async_get_job_limit (void)
358 return thread_count_limit;
362 _gnome_vfs_job_queue_shutdown (void)
364 g_static_mutex_lock (&job_queue_lock);
366 job_queue_destroy ();
368 g_static_mutex_unlock (&job_queue_lock);