ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-job.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /*
2 gnome-vfs-job.c - Jobs for asynchronous operation of the GNOME Virtual File
3 System (version for POSIX threads).
4
5    Copyright (C) 1999 Free Software Foundation
6    Copyright (C) 2000 Eazel
7
8    The Gnome Library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Library General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12
13    The Gnome Library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Library General Public License for more details.
17
18    You should have received a copy of the GNU Library General Public
19    License along with the Gnome Library; see the file COPYING.LIB.  If not,
20    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21    Boston, MA 02111-1307, USA.
22
23    Authors: 
24         Ettore Perazzoli <ettore@gnu.org> 
25         Pavel Cisler <pavel@eazel.com> 
26         Darin Adler <darin@eazel.com> 
27
28    */
29
30 #include <config.h>
31 #include "gnome-vfs-job.h"
32
33 #include "gnome-vfs-async-job-map.h"
34 #include "gnome-vfs-job-slave.h"
35 #include "gnome-vfs-job-queue.h"
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <glib/gmessages.h>
39 #include <glib/gstrfuncs.h>
40 #include <libgnomevfs/gnome-vfs-cancellable-ops.h>
41 #include <libgnomevfs/gnome-vfs-context.h>
42 #include <libgnomevfs/gnome-vfs-i18n.h>
43 #include <libgnomevfs/gnome-vfs-backend.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 static GStaticPrivate job_private = G_STATIC_PRIVATE_INIT;
48
49 #if GNOME_VFS_JOB_DEBUG
50
51 char *job_debug_types[] = {
52         "open", "open as channel",
53         "create", "create symbolic link",
54         "create as channel", "close",
55         "read", "write", "read write done",
56         "load directory", "find directory",
57         "xfer", "get file info", "set file info",
58         "module callback", "file control",
59         "**error**"
60 };
61
62 /* FIXME bugzilla.eazel.com 1130
63  * - this is should use the correct static mutex initialization macro.
64  * However glibconfig.h is broken and the supplied macro gives a warning.
65  * Since this id debug only, just use what the macro should be here.
66  * even though it is not portable.
67  */
68 GStaticMutex debug_mutex = G_STATIC_MUTEX_INIT;
69 #endif
70
71 static int job_count = 0;
72
73 static void     gnome_vfs_op_destroy                (GnomeVFSOp           *op);
74 static void     _gnome_vfs_job_destroy_notify_result (GnomeVFSNotifyResult *notify_result);
75 static gboolean dispatch_job_callback               (gpointer              data);
76 static gboolean dispatch_sync_job_callback          (gpointer              data);
77
78 static void     clear_current_job                   (void);
79 static void     set_current_job                     (GnomeVFSJob *context);
80
81 static void
82 set_fl (int fd, int flags)
83 {
84         int val;
85
86         val = fcntl (fd, F_GETFL, 0);
87         if (val < 0) {
88                 g_warning ("fcntl() F_GETFL failed: %s", strerror (errno));
89                 return;
90         }
91
92         val |= flags;
93         
94         val = fcntl (fd, F_SETFL, val);
95         if (val < 0) {
96                 g_warning ("fcntl() F_SETFL failed: %s", strerror (errno));
97                 return;
98         }
99 }
100
101 static void
102 clr_fl (int fd, int flags)
103 {
104         int val;
105
106         val = fcntl (fd, F_GETFL, 0);
107         if (val < 0) {
108                 g_warning ("fcntl() F_GETFL failed: %s", strerror (errno));
109                 return;
110         }
111
112         val &= ~flags;
113         
114         val = fcntl (fd, F_SETFL, val);
115         if (val < 0) {
116                 g_warning ("fcntl() F_SETFL failed: %s", strerror (errno));
117                 return;
118         }
119 }
120
121 /*
122  *   Find out whether or not a given job should be left in
123  * the job map, preserving it's open VFS handle, since we
124  * can do more operations on it later.
125  */
126 gboolean
127 _gnome_vfs_job_complete (GnomeVFSJob *job)
128 {
129         g_assert (job->op != NULL);
130         
131         switch (job->op->type) {
132         case GNOME_VFS_OP_OPEN:
133         case GNOME_VFS_OP_OPEN_AS_CHANNEL:
134         case GNOME_VFS_OP_CREATE:
135         case GNOME_VFS_OP_CREATE_AS_CHANNEL:
136         case GNOME_VFS_OP_CREATE_SYMBOLIC_LINK:
137                 /* if job got cancelled, no close expected */
138                 return job->cancelled || job->failed;
139
140         case GNOME_VFS_OP_READ:
141         case GNOME_VFS_OP_WRITE:
142                 g_assert_not_reached();
143                 return FALSE;
144         case GNOME_VFS_OP_READ_WRITE_DONE:
145         case GNOME_VFS_OP_FILE_CONTROL:
146                 return FALSE;
147         
148         default:
149                 return TRUE;
150         }
151 }
152
153 /* This notifies the master thread asynchronously, without waiting for an
154  * acknowledgment.
155  */
156 static void
157 job_oneway_notify (GnomeVFSJob *job, GnomeVFSNotifyResult *notify_result)
158 {
159         if (_gnome_vfs_async_job_add_callback (job, notify_result)) {
160                 JOB_DEBUG (("job %u, callback %u type '%s'",
161                             GPOINTER_TO_UINT (notify_result->job_handle),
162                             notify_result->callback_id,
163                             JOB_DEBUG_TYPE (job->op->type)));
164         
165                 g_idle_add (dispatch_job_callback, notify_result);
166         } else {
167                 JOB_DEBUG (("Barfing on oneway cancel %u (%d) type '%s'",
168                             GPOINTER_TO_UINT (notify_result->job_handle),
169                             job->op->type, JOB_DEBUG_TYPE (job->op->type)));
170                 _gnome_vfs_job_destroy_notify_result (notify_result);
171         }
172 }
173
174 /* This notifies the master threads, waiting until it acknowledges the
175    notification.  */
176 static void
177 job_notify (GnomeVFSJob *job, GnomeVFSNotifyResult *notify_result)
178 {
179         if (!_gnome_vfs_async_job_add_callback (job, notify_result)) {
180                 JOB_DEBUG (("Barfing on sync cancel %u (%d)",
181                             GPOINTER_TO_UINT (notify_result->job_handle),
182                             job->op->type));
183                 _gnome_vfs_job_destroy_notify_result (notify_result);
184                 return;
185         }
186
187         /* Send the notification.  This will wake up the master thread, which
188          * will in turn signal the notify condition.
189          */
190         g_idle_add (dispatch_sync_job_callback, notify_result);
191
192         JOB_DEBUG (("Wait notify condition %u", GPOINTER_TO_UINT (notify_result->job_handle)));
193         /* Wait for the notify condition.  */
194         g_cond_wait (job->notify_ack_condition, job->job_lock);
195
196         JOB_DEBUG (("Got notify ack condition %u", GPOINTER_TO_UINT (notify_result->job_handle)));
197 }
198
199 static void
200 dispatch_open_callback (GnomeVFSNotifyResult *notify_result)
201 {
202         (* notify_result->specifics.open.callback) (notify_result->job_handle,
203                                                     notify_result->specifics.open.result,
204                                                     notify_result->specifics.open.callback_data);
205 }
206
207 static void
208 dispatch_create_callback (GnomeVFSNotifyResult *notify_result)
209 {
210         (* notify_result->specifics.create.callback) (notify_result->job_handle,
211                                                       notify_result->specifics.create.result,
212                                                       notify_result->specifics.create.callback_data);
213 }
214
215 static void
216 dispatch_open_as_channel_callback (GnomeVFSNotifyResult *notify_result)
217 {
218         (* notify_result->specifics.open_as_channel.callback) (notify_result->job_handle,
219                                                                notify_result->specifics.open_as_channel.channel,
220                                                                notify_result->specifics.open_as_channel.result,
221                                                                notify_result->specifics.open_as_channel.callback_data);
222 }
223
224 static void
225 dispatch_create_as_channel_callback (GnomeVFSNotifyResult *notify_result)
226 {
227         (* notify_result->specifics.create_as_channel.callback) (notify_result->job_handle,
228                                                                  notify_result->specifics.create_as_channel.channel,
229                                                                  notify_result->specifics.create_as_channel.result,
230                                                                  notify_result->specifics.create_as_channel.callback_data);
231 }
232
233 static void
234 dispatch_close_callback (GnomeVFSNotifyResult *notify_result)
235 {
236         (* notify_result->specifics.close.callback) (notify_result->job_handle,
237                                                      notify_result->specifics.close.result,
238                                                      notify_result->specifics.close.callback_data);
239 }
240
241 static void
242 dispatch_read_callback (GnomeVFSNotifyResult *notify_result)
243 {
244         (* notify_result->specifics.read.callback) (notify_result->job_handle,
245                                                     notify_result->specifics.read.result,
246                                                     notify_result->specifics.read.buffer,
247                                                     notify_result->specifics.read.num_bytes,
248                                                     notify_result->specifics.read.bytes_read,
249                                                     notify_result->specifics.read.callback_data);
250 }
251
252 static void
253 dispatch_write_callback (GnomeVFSNotifyResult *notify_result)
254 {
255         (* notify_result->specifics.write.callback) (notify_result->job_handle,
256                                                      notify_result->specifics.write.result,
257                                                      notify_result->specifics.write.buffer,
258                                                      notify_result->specifics.write.num_bytes,
259                                                      notify_result->specifics.write.bytes_written,
260                                                      notify_result->specifics.write.callback_data);
261 }
262
263 static void
264 dispatch_load_directory_callback (GnomeVFSNotifyResult *notify_result)
265 {
266         (* notify_result->specifics.load_directory.callback) (notify_result->job_handle,
267                                                               notify_result->specifics.load_directory.result,
268                                                               notify_result->specifics.load_directory.list,
269                                                               notify_result->specifics.load_directory.entries_read,
270                                                               notify_result->specifics.load_directory.callback_data);
271 }
272
273 static void
274 dispatch_get_file_info_callback (GnomeVFSNotifyResult *notify_result)
275 {
276         (* notify_result->specifics.get_file_info.callback) (notify_result->job_handle,
277                                                              notify_result->specifics.get_file_info.result_list,
278                                                              notify_result->specifics.get_file_info.callback_data);
279 }
280
281 static void
282 dispatch_find_directory_callback (GnomeVFSNotifyResult *notify_result)
283 {
284         (* notify_result->specifics.find_directory.callback) (notify_result->job_handle,
285                                                               notify_result->specifics.find_directory.result_list,
286                                                               notify_result->specifics.find_directory.callback_data);
287 }
288
289 static void
290 dispatch_set_file_info_callback (GnomeVFSNotifyResult *notify_result)
291 {
292         gboolean new_info_is_valid;
293
294         new_info_is_valid = notify_result->specifics.set_file_info.set_file_info_result == GNOME_VFS_OK
295                 && notify_result->specifics.set_file_info.get_file_info_result == GNOME_VFS_OK;
296                 
297         (* notify_result->specifics.set_file_info.callback) (notify_result->job_handle,
298                                                              notify_result->specifics.set_file_info.set_file_info_result,
299                                                              new_info_is_valid ? notify_result->specifics.set_file_info.info : NULL,
300                                                              notify_result->specifics.set_file_info.callback_data);
301 }
302
303 static void
304 dispatch_xfer_callback (GnomeVFSNotifyResult *notify_result, gboolean cancelled)
305 {
306         if (cancelled) {
307                 /* make the xfer operation stop */
308                 notify_result->specifics.xfer.reply = 0;
309                 return;
310         }
311         
312         notify_result->specifics.xfer.reply = (* notify_result->specifics.xfer.callback) (
313                                                             notify_result->job_handle,
314                                                             notify_result->specifics.xfer.progress_info,
315                                                             notify_result->specifics.xfer.callback_data);
316 }
317
318 static void
319 dispatch_module_callback (GnomeVFSNotifyResult *notify_result)
320 {
321         notify_result->specifics.callback.callback (notify_result->specifics.callback.in,
322                                                     notify_result->specifics.callback.in_size,
323                                                     notify_result->specifics.callback.out,
324                                                     notify_result->specifics.callback.out_size,
325                                                     notify_result->specifics.callback.user_data,
326                                                     notify_result->specifics.callback.response,
327                                                     notify_result->specifics.callback.response_data);
328 }
329
330 static void
331 dispatch_file_control_callback (GnomeVFSNotifyResult *notify_result)
332 {
333         notify_result->specifics.file_control.callback (notify_result->job_handle,
334                                                         notify_result->specifics.file_control.result,
335                                                         notify_result->specifics.file_control.operation_data,
336                                                         notify_result->specifics.file_control.callback_data);
337 }
338
339 static void
340 empty_close_callback (GnomeVFSAsyncHandle *handle,
341                       GnomeVFSResult result,
342                       gpointer callback_data)
343 {
344 }
345
346 static void
347 handle_cancelled_open (GnomeVFSJob *job)
348 {
349         /* schedule a silent close to make sure the handle does not leak */
350         _gnome_vfs_job_set (job, GNOME_VFS_OP_CLOSE, 
351                            (GFunc) empty_close_callback, NULL);
352         _gnome_vfs_job_go (job);
353 }
354
355 static void
356 free_get_file_info_notify_result (GnomeVFSGetFileInfoOpResult *notify_result)
357 {
358         GList *p;
359         GnomeVFSGetFileInfoResult *result_item;
360         
361         for (p = notify_result->result_list; p != NULL; p = p->next) {
362                 result_item = p->data;
363
364                 gnome_vfs_uri_unref (result_item->uri);
365                 gnome_vfs_file_info_unref (result_item->file_info);
366                 g_free (result_item);
367         }
368         g_list_free (notify_result->result_list);
369 }
370
371 static void
372 free_find_directory_notify_result (GnomeVFSFindDirectoryOpResult *notify_result)
373 {
374         GList *p;
375         GnomeVFSFindDirectoryResult *result_item;
376
377         for (p = notify_result->result_list; p != NULL; p = p->next) {
378                 result_item = p->data;
379
380                 if (result_item->uri != NULL) {
381                         gnome_vfs_uri_unref (result_item->uri);
382                 }
383                 g_free (result_item);
384         }
385         g_list_free (notify_result->result_list);
386 }
387
388 static void
389 _gnome_vfs_job_destroy_notify_result (GnomeVFSNotifyResult *notify_result)
390 {
391         JOB_DEBUG (("%u", notify_result->callback_id));
392
393         switch (notify_result->type) {
394         case GNOME_VFS_OP_CLOSE:
395         case GNOME_VFS_OP_CREATE:
396         case GNOME_VFS_OP_CREATE_AS_CHANNEL:
397         case GNOME_VFS_OP_CREATE_SYMBOLIC_LINK:
398         case GNOME_VFS_OP_WRITE:
399         case GNOME_VFS_OP_OPEN:
400         case GNOME_VFS_OP_OPEN_AS_CHANNEL:
401         case GNOME_VFS_OP_READ:
402                 g_free (notify_result);
403                 break;
404                 
405         case GNOME_VFS_OP_FILE_CONTROL:
406                 if (notify_result->specifics.file_control.operation_data_destroy_func) {
407                         notify_result->specifics.file_control.operation_data_destroy_func (
408                                             notify_result->specifics.file_control.operation_data);
409                 }
410                 g_free (notify_result);
411                 break;
412                 
413         case GNOME_VFS_OP_FIND_DIRECTORY:
414                 free_find_directory_notify_result (&notify_result->specifics.find_directory);
415                 g_free (notify_result);
416                 break;
417                 
418         case GNOME_VFS_OP_GET_FILE_INFO:
419                 free_get_file_info_notify_result (&notify_result->specifics.get_file_info);
420                 g_free (notify_result);
421                 break;
422                 
423         case GNOME_VFS_OP_SET_FILE_INFO:
424                 gnome_vfs_file_info_unref (notify_result->specifics.set_file_info.info);
425                 g_free (notify_result);
426                 break;
427                 
428         case GNOME_VFS_OP_LOAD_DIRECTORY:
429                 gnome_vfs_file_info_list_free (notify_result->specifics.load_directory.list);
430                 g_free (notify_result);
431                 break;
432
433         case GNOME_VFS_OP_XFER:
434                 /* the XFER result is allocated on the stack */
435                 break;
436                 
437         case GNOME_VFS_OP_MODULE_CALLBACK:
438                 /* the MODULE_CALLBACK result is allocated on the stack */
439                 break;
440         
441         default:
442                 g_assert_not_reached ();
443                 break;
444         }
445 }
446
447 /* Entry point for sync notification callback */
448 static gboolean
449 dispatch_sync_job_callback (gpointer data)
450 {
451         GnomeVFSNotifyResult *notify_result;
452         GnomeVFSJob *job;
453         gboolean valid;
454         gboolean cancelled;
455
456         notify_result = (GnomeVFSNotifyResult *) data;
457
458         _gnome_vfs_async_job_callback_valid (notify_result->callback_id, &valid, &cancelled);
459
460         /* Even though the notify result is owned by the async thread and persists
461          * all through the notification, we still keep it in the job map to
462          * make cancellation easier.
463          */
464         _gnome_vfs_async_job_remove_callback (notify_result->callback_id);
465
466         g_assert (valid);
467
468         switch (notify_result->type) {
469         case GNOME_VFS_OP_CREATE_AS_CHANNEL:
470                 dispatch_create_as_channel_callback (notify_result);
471                 break;
472         case GNOME_VFS_OP_OPEN_AS_CHANNEL:
473                 dispatch_open_as_channel_callback (notify_result);
474                 break;          
475         case GNOME_VFS_OP_XFER:
476                 dispatch_xfer_callback (notify_result, cancelled);
477                 break;
478         case GNOME_VFS_OP_MODULE_CALLBACK:
479                 dispatch_module_callback (notify_result);
480                 break;
481         default:
482                 g_assert_not_reached ();
483                 break;
484         }
485         
486         _gnome_vfs_async_job_map_lock ();
487         job = _gnome_vfs_async_job_map_get_job (notify_result->job_handle);
488         g_mutex_lock (job->job_lock);
489         _gnome_vfs_async_job_map_unlock ();
490         
491         g_assert (job != NULL);
492         
493         JOB_DEBUG (("signalling %u", GPOINTER_TO_UINT (notify_result->job_handle)));
494         
495         /* Signal the async thread that we are done with the notification. */
496         g_cond_signal (job->notify_ack_condition);
497         g_mutex_unlock (job->job_lock);
498
499         return FALSE;
500 }
501
502 /* Entry point for async notification callback */
503 static gboolean
504 dispatch_job_callback (gpointer data)
505
506 {
507         GnomeVFSNotifyResult *notify_result;
508         GnomeVFSJob *job;
509         gboolean valid;
510         gboolean cancelled;
511         
512         notify_result = (GnomeVFSNotifyResult *) data;
513
514         JOB_DEBUG (("%u type '%s'", GPOINTER_TO_UINT (notify_result->job_handle),
515                     JOB_DEBUG_TYPE (notify_result->type)));
516         
517         _gnome_vfs_async_job_callback_valid (notify_result->callback_id, &valid, &cancelled);
518         _gnome_vfs_async_job_remove_callback (notify_result->callback_id);
519
520         if (!valid) {
521                 /* this can happen when gnome vfs is shutting down */
522                 JOB_DEBUG (("shutting down: callback %u no longer valid",
523                             notify_result->callback_id));
524                 _gnome_vfs_job_destroy_notify_result (notify_result);
525                 return FALSE;
526         }
527         
528         if (cancelled) {
529                 /* cancel the job in progress */
530                 JOB_DEBUG (("cancelling job %u %u",
531                             GPOINTER_TO_UINT (notify_result->job_handle),
532                             notify_result->callback_id));
533
534                 _gnome_vfs_async_job_map_lock ();
535
536                 job = _gnome_vfs_async_job_map_get_job (notify_result->job_handle);
537                 
538                 if (job != NULL) {
539                         g_mutex_lock (job->job_lock);
540
541                         switch (job->op->type) {
542                         case GNOME_VFS_OP_OPEN:
543                         case GNOME_VFS_OP_OPEN_AS_CHANNEL:
544                         case GNOME_VFS_OP_CREATE:
545                         case GNOME_VFS_OP_CREATE_AS_CHANNEL:
546                         case GNOME_VFS_OP_CREATE_SYMBOLIC_LINK:
547                                 if (job->handle) {
548                                         g_mutex_unlock (job->job_lock);
549                                         handle_cancelled_open (job);
550                                         JOB_DEBUG (("handle cancel open job %u",
551                                                     GPOINTER_TO_UINT (notify_result->job_handle)));
552                                         break;
553                                 } /* else drop through */
554                         default:
555                                 /* Remove job from the job map. */
556                                 _gnome_vfs_async_job_map_remove_job (job);
557                                 g_mutex_unlock (job->job_lock);
558                                 break;
559                         }
560                 }
561         
562                 _gnome_vfs_async_job_map_unlock ();
563                 _gnome_vfs_job_destroy_notify_result (notify_result);
564                 return FALSE;
565         }
566         
567                 
568         JOB_DEBUG (("executing callback %u", GPOINTER_TO_UINT (notify_result->job_handle)));    
569
570         switch (notify_result->type) {
571         case GNOME_VFS_OP_CLOSE:
572                 dispatch_close_callback (notify_result);
573                 break;
574         case GNOME_VFS_OP_CREATE:
575                 dispatch_create_callback (notify_result);
576                 break;
577         case GNOME_VFS_OP_CREATE_AS_CHANNEL:
578                 dispatch_create_as_channel_callback (notify_result);
579                 break;
580         case GNOME_VFS_OP_CREATE_SYMBOLIC_LINK:
581                 dispatch_create_callback (notify_result);
582                 break;
583         case GNOME_VFS_OP_FIND_DIRECTORY:
584                 dispatch_find_directory_callback (notify_result);
585                 break;
586         case GNOME_VFS_OP_GET_FILE_INFO:
587                 dispatch_get_file_info_callback (notify_result);
588                 break;
589         case GNOME_VFS_OP_LOAD_DIRECTORY:
590                 dispatch_load_directory_callback (notify_result);
591                 break;
592         case GNOME_VFS_OP_OPEN:
593                 dispatch_open_callback (notify_result);
594                 break;
595         case GNOME_VFS_OP_OPEN_AS_CHANNEL:
596                 dispatch_open_as_channel_callback (notify_result);
597                 break;
598         case GNOME_VFS_OP_READ:
599                 dispatch_read_callback (notify_result);
600                 break;
601         case GNOME_VFS_OP_SET_FILE_INFO:
602                 dispatch_set_file_info_callback (notify_result);
603                 break;
604         case GNOME_VFS_OP_WRITE:
605                 dispatch_write_callback (notify_result);
606                 break;
607         case GNOME_VFS_OP_FILE_CONTROL:
608                 dispatch_file_control_callback (notify_result);
609                 break;
610         default:
611                 g_assert_not_reached ();
612                 break;
613         }
614
615         JOB_DEBUG (("dispatch callback - done %u", GPOINTER_TO_UINT (notify_result->job_handle)));
616         _gnome_vfs_job_destroy_notify_result (notify_result);
617
618         return FALSE;
619 }
620
621 void
622 _gnome_vfs_job_set (GnomeVFSJob *job,
623                    GnomeVFSOpType type,
624                    GFunc callback,
625                    gpointer callback_data)
626 {
627         GnomeVFSOp *op;
628
629         op = g_new (GnomeVFSOp, 1);
630         op->type = type;
631         op->callback = callback;
632         op->callback_data = callback_data;
633         op->context = gnome_vfs_context_new ();
634         op->stack_info = _gnome_vfs_module_callback_get_stack_info ();
635
636         g_assert (gnome_vfs_context_get_cancellation (op->context) != NULL);
637
638         JOB_DEBUG (("locking access lock %u, op %d", GPOINTER_TO_UINT (job->job_handle), type));
639
640         g_mutex_lock (job->job_lock);
641
642         gnome_vfs_op_destroy (job->op);
643         job->op = op;
644         job->cancelled = FALSE;
645
646         g_mutex_unlock (job->job_lock);
647
648         JOB_DEBUG (("%u op type %d, op %p", GPOINTER_TO_UINT (job->job_handle),
649                 job->op->type, job->op));
650 }
651
652 GnomeVFSJob *
653 _gnome_vfs_job_new (GnomeVFSOpType type, int priority, GFunc callback, gpointer callback_data)
654 {
655         GnomeVFSJob *new_job;
656         
657         new_job = g_new0 (GnomeVFSJob, 1);
658
659         new_job->job_lock = g_mutex_new ();
660         new_job->notify_ack_condition = g_cond_new ();
661         new_job->priority = priority;
662
663         /* Add the new job into the job hash table. This also assigns
664          * the job a unique id
665          */
666         _gnome_vfs_async_job_map_add_job (new_job);
667         _gnome_vfs_job_set (new_job, type, callback, callback_data);
668
669         job_count++;
670
671         return new_job;
672 }
673
674 void
675 _gnome_vfs_job_destroy (GnomeVFSJob *job)
676 {
677         JOB_DEBUG (("destroying job %u", GPOINTER_TO_UINT (job->job_handle)));
678
679         gnome_vfs_op_destroy (job->op);
680
681         g_mutex_free (job->job_lock);
682         g_cond_free (job->notify_ack_condition);
683
684         memset (job, 0xaa, sizeof (GnomeVFSJob));
685
686         g_free (job);
687         job_count--;
688
689         JOB_DEBUG (("job %u terminated cleanly", GPOINTER_TO_UINT (job->job_handle)));
690 }
691
692 int
693 gnome_vfs_job_get_count (void)
694 {
695         return job_count;
696 }
697
698 static void
699 gnome_vfs_op_destroy (GnomeVFSOp *op)
700 {
701         if (op == NULL) {
702                 return;
703         }
704         
705         switch (op->type) {
706         case GNOME_VFS_OP_CREATE:
707                 if (op->specifics.create.uri != NULL) {
708                         gnome_vfs_uri_unref (op->specifics.create.uri);
709                 }
710                 break;
711         case GNOME_VFS_OP_CREATE_AS_CHANNEL:
712                 if (op->specifics.create_as_channel.uri != NULL) {
713                         gnome_vfs_uri_unref (op->specifics.create_as_channel.uri);
714                 }
715                 break;
716         case GNOME_VFS_OP_CREATE_SYMBOLIC_LINK:
717                 gnome_vfs_uri_unref (op->specifics.create_symbolic_link.uri);
718                 g_free (op->specifics.create_symbolic_link.uri_reference);
719                 break;
720         case GNOME_VFS_OP_FIND_DIRECTORY:
721                 gnome_vfs_uri_list_free (op->specifics.find_directory.uris);
722                 break;
723         case GNOME_VFS_OP_GET_FILE_INFO:
724                 gnome_vfs_uri_list_free (op->specifics.get_file_info.uris);
725                 break;
726         case GNOME_VFS_OP_LOAD_DIRECTORY:
727                 if (op->specifics.load_directory.uri != NULL) {
728                         gnome_vfs_uri_unref (op->specifics.load_directory.uri);
729                 }
730                 break;
731         case GNOME_VFS_OP_OPEN:
732                 if (op->specifics.open.uri != NULL) {
733                         gnome_vfs_uri_unref (op->specifics.open.uri);
734                 }
735                 break;
736         case GNOME_VFS_OP_OPEN_AS_CHANNEL:
737                 if (op->specifics.open_as_channel.uri != NULL) {
738                         gnome_vfs_uri_unref (op->specifics.open_as_channel.uri);
739                 }
740                 break;
741         case GNOME_VFS_OP_SET_FILE_INFO:
742                 gnome_vfs_uri_unref (op->specifics.set_file_info.uri);
743                 gnome_vfs_file_info_unref (op->specifics.set_file_info.info);
744                 break;
745         case GNOME_VFS_OP_XFER:
746                 gnome_vfs_uri_list_free (op->specifics.xfer.source_uri_list);
747                 gnome_vfs_uri_list_free (op->specifics.xfer.target_uri_list);
748                 break;
749         case GNOME_VFS_OP_READ:
750         case GNOME_VFS_OP_WRITE:
751         case GNOME_VFS_OP_CLOSE:
752         case GNOME_VFS_OP_READ_WRITE_DONE:
753                 break;
754         case GNOME_VFS_OP_FILE_CONTROL:
755                 g_free (op->specifics.file_control.operation);
756                 break;
757         default:
758                 g_warning (_("Unknown op type %u"), op->type);
759         }
760         
761         g_assert (gnome_vfs_context_get_cancellation (op->context) != NULL);
762         
763         gnome_vfs_context_free (op->context);
764         _gnome_vfs_module_callback_free_stack_info (op->stack_info);
765         
766         g_free (op);
767 }
768
769 void
770 _gnome_vfs_job_go (GnomeVFSJob *job)
771 {
772         JOB_DEBUG (("new job %u, op %d, type '%s' unlocking job lock",
773                     GPOINTER_TO_UINT (job->job_handle), job->op->type,
774                     JOB_DEBUG_TYPE (job->op->type)));
775
776         /* Fire up the async job thread. */
777         if (!_gnome_vfs_job_schedule (job)) {
778                 g_warning ("Cannot schedule this job.");
779                 _gnome_vfs_job_destroy (job);
780                 return;
781         }
782 }
783
784 #define DEFAULT_BUFFER_SIZE 16384
785
786 static void
787 serve_channel_read (GnomeVFSHandle *handle,
788                     GIOChannel *channel_in,
789                     GIOChannel *channel_out,
790                     gulong advised_block_size,
791                     GnomeVFSContext *context)
792 {
793         gpointer buffer;
794         guint filled_bytes_in_buffer;
795         guint written_bytes_in_buffer;
796         guint current_buffer_size;
797         
798         if (advised_block_size == 0) {
799                 advised_block_size = DEFAULT_BUFFER_SIZE;
800         }
801
802         current_buffer_size = advised_block_size;
803         buffer = g_malloc(current_buffer_size);
804         filled_bytes_in_buffer = 0;
805         written_bytes_in_buffer = 0;
806
807         while (1) {
808                 GnomeVFSResult result;
809                 GIOStatus io_result;
810                 GnomeVFSFileSize bytes_read;
811                 
812         restart_toplevel_loop:
813                 
814                 g_assert(filled_bytes_in_buffer <= current_buffer_size);
815                 g_assert(written_bytes_in_buffer == 0);
816                 
817                 result = gnome_vfs_read_cancellable (handle,
818                                                      (char *) buffer + filled_bytes_in_buffer,
819                                                      MIN (advised_block_size, (current_buffer_size
820                                                         - filled_bytes_in_buffer)),
821                                                      &bytes_read, context);
822
823                 if (result == GNOME_VFS_ERROR_INTERRUPTED) {
824                         continue;
825                 } else if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
826                         goto end;
827                 }
828         
829                 filled_bytes_in_buffer += bytes_read;
830                 
831                 if (filled_bytes_in_buffer == 0) {
832                         goto end;
833                 }
834                 
835                 g_assert(written_bytes_in_buffer <= filled_bytes_in_buffer);
836
837                 if (gnome_vfs_context_check_cancellation(context)) {
838                         goto end;
839                 }
840
841                 while (written_bytes_in_buffer < filled_bytes_in_buffer) {
842                         gsize bytes_written;
843                         
844                         /* channel_out is nonblocking; if we get
845                            EAGAIN (G_IO_STATUS_AGAIN) then we tried to
846                            write but the pipe was full. In this case, we
847                            want to enlarge our buffer and go back to
848                            reading for one iteration, so we can keep
849                            collecting data while the main thread is
850                            busy. */
851                         
852                         io_result = g_io_channel_write_chars
853                                 (channel_out,
854                                  (char *) buffer + written_bytes_in_buffer,
855                                  filled_bytes_in_buffer - written_bytes_in_buffer,
856                                  &bytes_written, NULL);
857                         
858                         written_bytes_in_buffer += bytes_written;
859
860                         if (gnome_vfs_context_check_cancellation(context)) {
861                                 goto end;
862                         }
863                         
864                         if (io_result == G_IO_STATUS_AGAIN) {
865                                 /* if bytes_read == 0 then we reached
866                                    EOF so there's no point reading
867                                    again. So turn off nonblocking and
868                                    do a blocking write next time through. */
869                                 if (bytes_read == 0) {
870                                         int fd;
871
872                                         fd = g_io_channel_unix_get_fd (channel_out);
873                                         
874                                         clr_fl (fd, O_NONBLOCK);
875                                 } else {
876                                         if (written_bytes_in_buffer > 0) {
877                                                 /* Need to shift the unwritten bytes
878                                                    to the start of the buffer */
879                                                 g_memmove(buffer,
880                                                           (char *) buffer + written_bytes_in_buffer,
881                                                           filled_bytes_in_buffer - written_bytes_in_buffer);
882                                                 filled_bytes_in_buffer =
883                                                         filled_bytes_in_buffer - written_bytes_in_buffer;
884                                                 
885                                                 written_bytes_in_buffer = 0;
886                                         }
887                                         
888                                         /* If the buffer is more than half
889                                            full, double its size */
890                                         if (filled_bytes_in_buffer * 2 > current_buffer_size) {
891                                                 current_buffer_size *= 2;
892                                                 buffer = g_realloc(buffer, current_buffer_size);
893                                         }
894
895                                         /* Leave this loop, start reading again */
896                                         goto restart_toplevel_loop;
897
898                                 } /* end of else (bytes_read != 0) */
899                                 
900                         } else if (io_result != G_IO_STATUS_NORMAL || bytes_written == 0) {
901                                 goto end;
902                         }
903                 }
904
905                 g_assert(written_bytes_in_buffer == filled_bytes_in_buffer);
906                 
907                 /* Reset, we wrote everything */
908                 written_bytes_in_buffer = 0;
909                 filled_bytes_in_buffer = 0;
910         }
911
912  end:
913         g_free (buffer);
914         g_io_channel_shutdown (channel_out, TRUE, NULL);
915         g_io_channel_unref (channel_out);
916         g_io_channel_unref (channel_in);
917 }
918
919 static void
920 serve_channel_write (GnomeVFSHandle *handle,
921                      GIOChannel *channel_in,
922                      GIOChannel *channel_out,
923                      GnomeVFSContext *context)
924 {
925         gchar buffer[DEFAULT_BUFFER_SIZE];
926         guint buffer_size;
927
928         buffer_size = DEFAULT_BUFFER_SIZE;
929
930         while (1) {
931                 GnomeVFSResult result;
932                 GIOStatus io_result;
933                 gsize bytes_read;
934                 gsize bytes_to_write;
935                 GnomeVFSFileSize bytes_written;
936                 gchar *p;
937
938                 io_result = g_io_channel_read_chars (channel_in, buffer, buffer_size,
939                                                      &bytes_read, NULL);
940
941                 if (io_result == G_IO_STATUS_AGAIN)
942                         continue;
943                 if (io_result != G_IO_STATUS_NORMAL || bytes_read == 0)
944                         goto end;
945
946                 p = buffer;
947                 bytes_to_write = bytes_read;
948                 while (bytes_to_write > 0) {
949                         result = gnome_vfs_write_cancellable (handle,
950                                                               p,
951                                                               bytes_to_write,
952                                                               &bytes_written,
953                                                               context);
954                         if (result == GNOME_VFS_ERROR_INTERRUPTED) {
955                                 continue;
956                         }
957                         
958                         if (result != GNOME_VFS_OK || bytes_written == 0) {
959                                 goto end;
960                         }
961
962                         p += bytes_written;
963                         bytes_to_write -= bytes_written;
964                 }
965         }
966
967  end:
968         g_io_channel_shutdown (channel_in, TRUE, NULL);
969         g_io_channel_unref (channel_in);
970         g_io_channel_unref (channel_out);
971 }
972
973 /* Job execution.  This is performed by the slave thread.  */
974
975 static void
976 execute_open (GnomeVFSJob *job)
977 {
978         GnomeVFSResult result;
979         GnomeVFSHandle *handle;
980         GnomeVFSOpenOp *open_op;
981         GnomeVFSNotifyResult *notify_result;
982
983         open_op = &job->op->specifics.open;
984
985         if (open_op->uri == NULL) {
986                 result = GNOME_VFS_ERROR_INVALID_URI;
987         } else {
988                 result = gnome_vfs_open_uri_cancellable (&handle, open_op->uri,
989                                                           open_op->open_mode,
990                                                           job->op->context);
991                 job->handle = handle;
992         }
993         
994         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
995         notify_result->job_handle = job->job_handle;
996         notify_result->type = job->op->type;
997         notify_result->specifics.open.result = result;
998         notify_result->specifics.open.callback = (GnomeVFSAsyncOpenCallback) job->op->callback;
999         notify_result->specifics.open.callback_data = job->op->callback_data;
1000
1001         if (result != GNOME_VFS_OK) {
1002                 /* if the open failed, just drop the job */
1003                 job->failed = TRUE;
1004         }
1005         
1006         job_oneway_notify (job, notify_result);
1007 }
1008
1009 static void
1010 execute_open_as_channel (GnomeVFSJob *job)
1011 {
1012         GnomeVFSResult result;
1013         GnomeVFSHandle *handle;
1014         GnomeVFSOpenAsChannelOp *open_as_channel_op;
1015         GnomeVFSOpenMode open_mode;
1016         GIOChannel *channel_in, *channel_out;
1017         gint pipefd[2];
1018         GnomeVFSNotifyResult *notify_result;
1019
1020         open_as_channel_op = &job->op->specifics.open_as_channel;
1021
1022         if (open_as_channel_op->uri == NULL) {
1023                 result = GNOME_VFS_ERROR_INVALID_URI;
1024         } else {
1025                 result = gnome_vfs_open_uri_cancellable
1026                         (&handle,
1027                          open_as_channel_op->uri,
1028                          open_as_channel_op->open_mode,
1029                          job->op->context);
1030         }
1031
1032         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1033         notify_result->job_handle = job->job_handle;
1034         notify_result->type = job->op->type;
1035         notify_result->specifics.open_as_channel.result = result;
1036         notify_result->specifics.open_as_channel.callback =
1037                 (GnomeVFSAsyncOpenAsChannelCallback) job->op->callback;
1038         notify_result->specifics.open_as_channel.callback_data = job->op->callback_data;
1039
1040         if (result != GNOME_VFS_OK) {
1041                 /* if the open failed, just drop the job */
1042                 job->failed = TRUE;
1043                 job_oneway_notify (job, notify_result);
1044                 return;
1045         }
1046
1047         if (pipe (pipefd) < 0) {
1048                 g_warning (_("Cannot create pipe for open GIOChannel: %s"),
1049                            g_strerror (errno));
1050                 notify_result->specifics.open_as_channel.result = GNOME_VFS_ERROR_INTERNAL;
1051                 /* if the open failed, just drop the job */
1052                 job->failed = TRUE;
1053                 job_oneway_notify (job, notify_result);
1054                 return;
1055         }
1056
1057         /* Set up the pipe for nonblocking writes, so if the main
1058          * thread is blocking for some reason the slave can keep
1059          * reading data.
1060          */
1061         set_fl (pipefd[1], O_NONBLOCK);
1062         
1063         channel_in = g_io_channel_unix_new (pipefd[0]);
1064         channel_out = g_io_channel_unix_new (pipefd[1]);
1065
1066         open_mode = open_as_channel_op->open_mode;
1067         
1068         if (open_mode & GNOME_VFS_OPEN_READ) {
1069                 notify_result->specifics.open_as_channel.channel = channel_in;
1070         } else {
1071                 notify_result->specifics.open_as_channel.channel = channel_out;
1072         }
1073
1074         notify_result->specifics.open_as_channel.result = GNOME_VFS_OK;
1075
1076         job_notify (job, notify_result);
1077
1078         if (open_mode & GNOME_VFS_OPEN_READ) {
1079                 serve_channel_read (handle, channel_in, channel_out,
1080                                     open_as_channel_op->advised_block_size,
1081                                     job->op->context);
1082         } else {
1083                 serve_channel_write (handle, channel_in, channel_out,
1084                                      job->op->context);
1085         }
1086 }
1087
1088 static void
1089 execute_create (GnomeVFSJob *job)
1090 {
1091         GnomeVFSResult result;
1092         GnomeVFSHandle *handle;
1093         GnomeVFSCreateOp *create_op;
1094         GnomeVFSNotifyResult *notify_result;
1095
1096         create_op = &job->op->specifics.create;
1097
1098         if (create_op->uri == NULL) {
1099                 result = GNOME_VFS_ERROR_INVALID_URI;
1100         } else {
1101                 result = gnome_vfs_create_uri_cancellable
1102                         (&handle,
1103                          create_op->uri,
1104                          create_op->open_mode,
1105                          create_op->exclusive,
1106                          create_op->perm,
1107                          job->op->context);
1108                 
1109                 job->handle = handle;
1110         }
1111
1112         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1113         notify_result->job_handle = job->job_handle;
1114         notify_result->type = job->op->type;
1115         notify_result->specifics.create.result = result;
1116         notify_result->specifics.create.callback = (GnomeVFSAsyncCreateCallback) job->op->callback;
1117         notify_result->specifics.create.callback_data = job->op->callback_data;
1118
1119         if (result != GNOME_VFS_OK) {
1120                 /* if the open failed, just drop the job */
1121                 job->failed = TRUE;
1122         }
1123
1124         job_oneway_notify (job, notify_result);
1125 }
1126
1127 static void
1128 execute_create_symbolic_link (GnomeVFSJob *job)
1129 {
1130         GnomeVFSResult result;
1131         GnomeVFSCreateLinkOp *create_op;
1132         GnomeVFSNotifyResult *notify_result;
1133
1134         create_op = &job->op->specifics.create_symbolic_link;
1135
1136         result = gnome_vfs_create_symbolic_link_cancellable
1137                 (create_op->uri,
1138                  create_op->uri_reference,
1139                  job->op->context);
1140
1141         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1142         notify_result->job_handle = job->job_handle;
1143         notify_result->type = job->op->type;
1144         notify_result->specifics.create.result = result;
1145         notify_result->specifics.create.callback = (GnomeVFSAsyncCreateCallback) job->op->callback;
1146         notify_result->specifics.create.callback_data = job->op->callback_data;
1147
1148         if (result != GNOME_VFS_OK) {
1149                 /* if the open failed, just drop the job */
1150                 job->failed = TRUE;
1151         }
1152
1153         job_oneway_notify (job, notify_result);
1154 }
1155         
1156 static void
1157 execute_create_as_channel (GnomeVFSJob *job)
1158 {
1159         GnomeVFSResult result;
1160         GnomeVFSHandle *handle;
1161         GnomeVFSCreateAsChannelOp *create_as_channel_op;
1162         GIOChannel *channel_in, *channel_out;
1163         gint pipefd[2];
1164         GnomeVFSNotifyResult *notify_result;
1165
1166         create_as_channel_op = &job->op->specifics.create_as_channel;
1167
1168         if (create_as_channel_op->uri == NULL) {
1169                 result = GNOME_VFS_ERROR_INVALID_URI;
1170         } else {
1171                 result = gnome_vfs_open_uri_cancellable
1172                         (&handle,
1173                          create_as_channel_op->uri,
1174                          create_as_channel_op->open_mode,
1175                          job->op->context);
1176         }
1177         
1178         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1179         notify_result->job_handle = job->job_handle;
1180         notify_result->type = job->op->type;
1181         notify_result->specifics.create_as_channel.result = result;
1182         notify_result->specifics.create_as_channel.callback = (GnomeVFSAsyncCreateAsChannelCallback) job->op->callback;
1183         notify_result->specifics.create_as_channel.callback_data = job->op->callback_data;
1184
1185         if (result != GNOME_VFS_OK) {
1186                 /* if the open failed, just drop the job */
1187                 job->failed = TRUE;
1188                 job_oneway_notify (job, notify_result);
1189                 return;
1190         }
1191
1192         if (pipe (pipefd) < 0) {
1193                 g_warning (_("Cannot create pipe for open GIOChannel: %s"),
1194                            g_strerror (errno));
1195                 notify_result->specifics.create_as_channel.result = GNOME_VFS_ERROR_INTERNAL;
1196                 /* if the open failed, just drop the job */
1197                 job->failed = TRUE;
1198                 job_oneway_notify (job, notify_result);
1199                 return;
1200         }
1201         
1202         channel_in = g_io_channel_unix_new (pipefd[0]);
1203         channel_out = g_io_channel_unix_new (pipefd[1]);
1204
1205         notify_result->specifics.create_as_channel.channel = channel_out;
1206
1207         job_notify (job, notify_result);
1208
1209         serve_channel_write (handle, channel_in, channel_out, job->op->context);
1210 }
1211
1212 static void
1213 execute_close (GnomeVFSJob *job)
1214 {
1215         GnomeVFSCloseOp *close_op;
1216         GnomeVFSNotifyResult *notify_result;
1217
1218         close_op = &job->op->specifics.close;
1219
1220         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1221         notify_result->job_handle = job->job_handle;
1222         notify_result->type = job->op->type;
1223         notify_result->specifics.close.callback = (GnomeVFSAsyncCloseCallback) job->op->callback;
1224         notify_result->specifics.close.callback_data = job->op->callback_data;
1225         notify_result->specifics.close.result
1226                 = gnome_vfs_close_cancellable (job->handle, job->op->context);
1227
1228         job_oneway_notify (job, notify_result);
1229 }
1230
1231 static void
1232 execute_read (GnomeVFSJob *job)
1233 {
1234         GnomeVFSReadOp *read_op;
1235         GnomeVFSNotifyResult *notify_result;
1236         
1237         read_op = &job->op->specifics.read;
1238
1239         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1240         notify_result->job_handle = job->job_handle;
1241         notify_result->type = job->op->type;
1242         notify_result->specifics.read.callback = (GnomeVFSAsyncReadCallback) job->op->callback;
1243         notify_result->specifics.read.callback_data = job->op->callback_data;
1244         notify_result->specifics.read.buffer = read_op->buffer;
1245         notify_result->specifics.read.num_bytes = read_op->num_bytes;
1246         
1247         notify_result->specifics.read.result = gnome_vfs_read_cancellable (job->handle,
1248                                                                            read_op->buffer,
1249                                                                            read_op->num_bytes,
1250                                                                            &notify_result->specifics.read.bytes_read,
1251                                                                            job->op->context);
1252
1253         job->op->type = GNOME_VFS_OP_READ_WRITE_DONE;
1254
1255         job_oneway_notify (job, notify_result);
1256 }
1257
1258 static void
1259 execute_write (GnomeVFSJob *job)
1260 {
1261         GnomeVFSWriteOp *write_op;
1262         GnomeVFSNotifyResult *notify_result;
1263         
1264         write_op = &job->op->specifics.write;
1265
1266         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1267         notify_result->job_handle = job->job_handle;
1268         notify_result->type = job->op->type;
1269         notify_result->specifics.write.callback = (GnomeVFSAsyncWriteCallback) job->op->callback;
1270         notify_result->specifics.write.callback_data = job->op->callback_data;
1271         notify_result->specifics.write.buffer = write_op->buffer;
1272         notify_result->specifics.write.num_bytes = write_op->num_bytes;
1273
1274         notify_result->specifics.write.result = gnome_vfs_write_cancellable (job->handle,
1275                                                                              write_op->buffer,
1276                                                                              write_op->num_bytes,
1277                                                                              &notify_result->specifics.write.bytes_written,
1278                                                                              job->op->context);
1279
1280         job->op->type = GNOME_VFS_OP_READ_WRITE_DONE;
1281
1282         job_oneway_notify (job, notify_result);
1283 }
1284
1285 static void
1286 execute_get_file_info (GnomeVFSJob *job)
1287 {
1288         GnomeVFSGetFileInfoOp *get_file_info_op;
1289         GList *p;
1290         GnomeVFSGetFileInfoResult *result_item;
1291         GnomeVFSNotifyResult *notify_result;
1292
1293         get_file_info_op = &job->op->specifics.get_file_info;
1294
1295         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1296         notify_result->job_handle = job->job_handle;
1297         notify_result->type = job->op->type;
1298         notify_result->specifics.get_file_info.callback =
1299                 (GnomeVFSAsyncGetFileInfoCallback) job->op->callback;
1300         notify_result->specifics.get_file_info.callback_data = job->op->callback_data;
1301
1302         for (p = get_file_info_op->uris; p != NULL; p = p->next) {
1303                 result_item = g_new (GnomeVFSGetFileInfoResult, 1);
1304
1305                 result_item->uri = gnome_vfs_uri_ref (p->data);
1306                 result_item->file_info = gnome_vfs_file_info_new ();
1307
1308                 result_item->result = gnome_vfs_get_file_info_uri_cancellable
1309                         (result_item->uri,
1310                          result_item->file_info,
1311                          get_file_info_op->options,
1312                          job->op->context);
1313
1314                 notify_result->specifics.get_file_info.result_list =
1315                         g_list_prepend (notify_result->specifics.get_file_info.result_list, result_item);
1316         }
1317         notify_result->specifics.get_file_info.result_list =
1318                 g_list_reverse (notify_result->specifics.get_file_info.result_list);
1319
1320         job_oneway_notify (job, notify_result);
1321 }
1322
1323 static void
1324 execute_set_file_info (GnomeVFSJob *job)
1325 {
1326         GnomeVFSSetFileInfoOp *set_file_info_op;
1327         GnomeVFSURI *parent_uri, *uri_after;
1328         GnomeVFSNotifyResult *notify_result;
1329
1330         set_file_info_op = &job->op->specifics.set_file_info;
1331
1332         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1333         notify_result->job_handle = job->job_handle;
1334         notify_result->type = job->op->type;
1335         notify_result->specifics.set_file_info.callback =
1336                 (GnomeVFSAsyncSetFileInfoCallback) job->op->callback;
1337         notify_result->specifics.set_file_info.callback_data =
1338                 job->op->callback_data;
1339
1340         notify_result->specifics.set_file_info.set_file_info_result =
1341                 gnome_vfs_set_file_info_cancellable (set_file_info_op->uri,
1342                         set_file_info_op->info, set_file_info_op->mask,
1343                         job->op->context);
1344
1345         /* Get the new URI after the set_file_info. The name may have
1346          * changed.
1347          */
1348         uri_after = NULL;
1349         if (notify_result->specifics.set_file_info.set_file_info_result == GNOME_VFS_OK
1350             && (set_file_info_op->mask & GNOME_VFS_SET_FILE_INFO_NAME) != 0) {
1351                 parent_uri = gnome_vfs_uri_get_parent (set_file_info_op->uri);
1352                 if (parent_uri != NULL) {
1353                         uri_after = gnome_vfs_uri_append_file_name
1354                                 (parent_uri, set_file_info_op->info->name);
1355                         gnome_vfs_uri_unref (parent_uri);
1356                 }
1357         }
1358         if (uri_after == NULL) {
1359                 uri_after = set_file_info_op->uri;
1360                 gnome_vfs_uri_ref (uri_after);
1361         }
1362
1363         notify_result->specifics.set_file_info.info = gnome_vfs_file_info_new ();
1364         if (uri_after == NULL) {
1365                 notify_result->specifics.set_file_info.get_file_info_result
1366                         = GNOME_VFS_ERROR_INVALID_URI;
1367         } else {
1368                 notify_result->specifics.set_file_info.get_file_info_result
1369                         = gnome_vfs_get_file_info_uri_cancellable
1370                         (uri_after,
1371                          notify_result->specifics.set_file_info.info,
1372                          set_file_info_op->options,
1373                          job->op->context);
1374                 gnome_vfs_uri_unref (uri_after);
1375         }
1376
1377         job_oneway_notify (job, notify_result);
1378 }
1379
1380 static void
1381 execute_find_directory (GnomeVFSJob *job)
1382 {
1383         GnomeVFSFindDirectoryOp *find_directory_op;
1384         GList *p;
1385         GnomeVFSFindDirectoryResult *result_item;
1386         GnomeVFSNotifyResult *notify_result;
1387
1388         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1389         notify_result->job_handle = job->job_handle;
1390         notify_result->type = job->op->type;
1391         notify_result->specifics.find_directory.callback
1392                 = (GnomeVFSAsyncFindDirectoryCallback) job->op->callback;
1393         notify_result->specifics.find_directory.callback_data = job->op->callback_data;
1394
1395         find_directory_op = &job->op->specifics.find_directory;
1396         for (p = find_directory_op->uris; p != NULL; p = p->next) {
1397                 result_item = g_new0 (GnomeVFSFindDirectoryResult, 1);
1398
1399                 result_item->result = gnome_vfs_find_directory_cancellable
1400                         ((GnomeVFSURI *) p->data,
1401                          find_directory_op->kind,
1402                          &result_item->uri,
1403                          find_directory_op->create_if_needed,
1404                          find_directory_op->find_if_needed,
1405                          find_directory_op->permissions,
1406                          job->op->context);
1407                 notify_result->specifics.find_directory.result_list =
1408                         g_list_prepend (notify_result->specifics.find_directory.result_list, result_item);
1409         }
1410
1411         notify_result->specifics.find_directory.result_list =
1412                 g_list_reverse (notify_result->specifics.find_directory.result_list);
1413         
1414         job_oneway_notify (job, notify_result);
1415 }
1416
1417 static void
1418 load_directory_details (GnomeVFSJob *job)
1419 {
1420         GnomeVFSLoadDirectoryOp *load_directory_op;
1421         GnomeVFSDirectoryHandle *handle;
1422         GList *directory_list;
1423         GnomeVFSFileInfo *info;
1424         GnomeVFSResult result;
1425         guint count;
1426         GnomeVFSNotifyResult *notify_result;
1427
1428         JOB_DEBUG (("%u", GPOINTER_TO_UINT (job->job_handle)));
1429         load_directory_op = &job->op->specifics.load_directory;
1430         
1431         if (load_directory_op->uri == NULL) {
1432                 result = GNOME_VFS_ERROR_INVALID_URI;
1433         } else {
1434                 result = gnome_vfs_directory_open_from_uri_cancellable
1435                         (&handle,
1436                          load_directory_op->uri,
1437                          load_directory_op->options,
1438                          job->op->context);
1439         }
1440
1441         if (result != GNOME_VFS_OK) {
1442                 notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1443                 notify_result->job_handle = job->job_handle;
1444                 notify_result->type = job->op->type;
1445                 notify_result->specifics.load_directory.result = result;
1446                 notify_result->specifics.load_directory.callback =
1447                         (GnomeVFSAsyncDirectoryLoadCallback) job->op->callback;
1448                 notify_result->specifics.load_directory.callback_data = job->op->callback_data;
1449                 job_oneway_notify (job, notify_result);
1450                 return;
1451         }
1452
1453         directory_list = NULL;
1454
1455         count = 0;
1456         while (1) {
1457                 if (gnome_vfs_context_check_cancellation (job->op->context)) {
1458                         JOB_DEBUG (("cancelled, bailing %u",
1459                                     GPOINTER_TO_UINT (job->job_handle)));
1460                         gnome_vfs_file_info_list_free (directory_list);
1461                         directory_list = NULL;
1462                         result = GNOME_VFS_ERROR_CANCELLED;
1463                         break;
1464                 }
1465
1466                 info = gnome_vfs_file_info_new ();
1467
1468                 result = gnome_vfs_directory_read_next_cancellable
1469                         (handle, info, job->op->context);
1470
1471                 if (result == GNOME_VFS_OK) {
1472                         directory_list = g_list_prepend (directory_list, info);
1473                         count++;
1474                 } else {
1475                         gnome_vfs_file_info_unref (info);
1476                 }
1477
1478                 if (count == load_directory_op->items_per_notification
1479                         || result != GNOME_VFS_OK) {
1480
1481                         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1482                         notify_result->job_handle = job->job_handle;
1483                         notify_result->type = job->op->type;
1484                         notify_result->specifics.load_directory.result = result;
1485                         notify_result->specifics.load_directory.entries_read = count;
1486                         notify_result->specifics.load_directory.list = 
1487                                 g_list_reverse (directory_list);
1488                         notify_result->specifics.load_directory.callback =
1489                                 (GnomeVFSAsyncDirectoryLoadCallback) job->op->callback;
1490                         notify_result->specifics.load_directory.callback_data =
1491                                 job->op->callback_data;
1492
1493                         job_oneway_notify (job, notify_result);
1494
1495                         count = 0;
1496                         directory_list = NULL;
1497
1498                         if (result != GNOME_VFS_OK) {
1499                                 break;
1500                         }
1501                 }
1502         }
1503
1504         g_assert (directory_list == NULL);
1505         gnome_vfs_directory_close (handle);
1506 }
1507
1508 static void
1509 execute_load_directory (GnomeVFSJob *job)
1510 {
1511         GnomeVFSLoadDirectoryOp *load_directory_op;
1512
1513         load_directory_op = &job->op->specifics.load_directory;
1514
1515         load_directory_details (job);
1516 }
1517
1518 static gint
1519 xfer_callback (GnomeVFSXferProgressInfo *info,
1520                gpointer data)
1521 {
1522         GnomeVFSJob *job;
1523         GnomeVFSNotifyResult notify_result;
1524
1525         job = (GnomeVFSJob *) data;
1526
1527         /* xfer is fully synchronous, just allocate the notify result struct on the stack */
1528         notify_result.job_handle = job->job_handle;
1529         notify_result.callback_id = 0;
1530         notify_result.cancelled = FALSE;
1531         notify_result.type = job->op->type;
1532         notify_result.specifics.xfer.progress_info = info;
1533         notify_result.specifics.xfer.callback = (GnomeVFSAsyncXferProgressCallback) job->op->callback;
1534         notify_result.specifics.xfer.callback_data = job->op->callback_data;
1535
1536         job_notify (job, &notify_result);
1537
1538         /* Pass the value returned from the callback in the master thread.  */
1539         return notify_result.specifics.xfer.reply;
1540 }
1541
1542 static void
1543 execute_xfer (GnomeVFSJob *job)
1544 {
1545         GnomeVFSXferOp *xfer_op;
1546         GnomeVFSResult result;
1547         GnomeVFSXferProgressInfo info;
1548         GnomeVFSNotifyResult notify_result;
1549
1550         xfer_op = &job->op->specifics.xfer;
1551
1552         result = _gnome_vfs_xfer_private (xfer_op->source_uri_list,
1553                                          xfer_op->target_uri_list,
1554                                          xfer_op->xfer_options,
1555                                          xfer_op->error_mode,
1556                                          xfer_op->overwrite_mode,
1557                                          xfer_callback,
1558                                          job,
1559                                          xfer_op->progress_sync_callback,
1560                                          xfer_op->sync_callback_data);
1561
1562         /* If the xfer functions returns an error now, something really bad
1563          * must have happened.
1564          */
1565         if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_INTERRUPTED) {
1566
1567                 info.status = GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR;
1568                 info.vfs_status = result;
1569                 info.phase = GNOME_VFS_XFER_PHASE_INITIAL;
1570                 info.source_name = NULL;
1571                 info.target_name = NULL;
1572                 info.file_index = 0;
1573                 info.files_total = 0;
1574                 info.bytes_total = 0;
1575                 info.file_size = 0;
1576                 info.bytes_copied = 0;
1577                 info.total_bytes_copied = 0;
1578
1579                 notify_result.job_handle = job->job_handle;
1580                 notify_result.callback_id = 0;
1581                 notify_result.cancelled = FALSE;
1582                 notify_result.type = job->op->type;
1583                 notify_result.specifics.xfer.progress_info = &info;
1584                 notify_result.specifics.xfer.callback = (GnomeVFSAsyncXferProgressCallback) job->op->callback;
1585                 notify_result.specifics.xfer.callback_data = job->op->callback_data;
1586
1587                 job_notify (job, &notify_result);
1588         }
1589 }
1590
1591 static void
1592 execute_file_control (GnomeVFSJob *job)
1593 {
1594         GnomeVFSFileControlOp *file_control_op;
1595         GnomeVFSNotifyResult *notify_result;
1596         
1597         file_control_op = &job->op->specifics.file_control;
1598
1599         notify_result = g_new0 (GnomeVFSNotifyResult, 1);
1600         notify_result->job_handle = job->job_handle;
1601         notify_result->type = job->op->type;
1602         notify_result->specifics.file_control.callback = (GnomeVFSAsyncFileControlCallback) job->op->callback;
1603         notify_result->specifics.file_control.callback_data = job->op->callback_data;
1604         notify_result->specifics.file_control.operation_data = file_control_op->operation_data;
1605         notify_result->specifics.file_control.operation_data_destroy_func = file_control_op->operation_data_destroy_func;
1606         
1607         notify_result->specifics.file_control.result = gnome_vfs_file_control_cancellable (job->handle,
1608                                                                                            file_control_op->operation,
1609                                                                                            file_control_op->operation_data,
1610                                                                                            job->op->context);
1611
1612         job->op->type = GNOME_VFS_OP_FILE_CONTROL;
1613
1614         job_oneway_notify (job, notify_result);
1615 }
1616
1617
1618 /*
1619  * _gnome_vfs_job_execute:
1620  * @job: the job to execute
1621  * 
1622  *   This function is called by the slave thread to execute
1623  * the job - all work performed by a thread starts here.
1624  */
1625 void
1626 _gnome_vfs_job_execute (GnomeVFSJob *job)
1627 {
1628         guint id;
1629
1630         id = GPOINTER_TO_UINT (job->job_handle);
1631
1632         JOB_DEBUG (("exec job %u", id));
1633
1634         if (!job->cancelled) {
1635                 set_current_job (job);
1636
1637                 JOB_DEBUG (("executing %u %d type %s", id, job->op->type,
1638                             JOB_DEBUG_TYPE (job->op->type)));
1639
1640                 switch (job->op->type) {
1641                 case GNOME_VFS_OP_OPEN:
1642                         execute_open (job);
1643                         break;
1644                 case GNOME_VFS_OP_OPEN_AS_CHANNEL:
1645                         execute_open_as_channel (job);
1646                         break;
1647                 case GNOME_VFS_OP_CREATE:
1648                         execute_create (job);
1649                         break;
1650                 case GNOME_VFS_OP_CREATE_AS_CHANNEL:
1651                         execute_create_as_channel (job);
1652                         break;
1653                 case GNOME_VFS_OP_CREATE_SYMBOLIC_LINK:
1654                         execute_create_symbolic_link (job);
1655                         break;
1656                 case GNOME_VFS_OP_CLOSE:
1657                         execute_close (job);
1658                         break;
1659                 case GNOME_VFS_OP_READ:
1660                         execute_read (job);
1661                         break;
1662                 case GNOME_VFS_OP_WRITE:
1663                         execute_write (job);
1664                         break;
1665                 case GNOME_VFS_OP_LOAD_DIRECTORY:
1666                         execute_load_directory (job);
1667                         break;
1668                 case GNOME_VFS_OP_FIND_DIRECTORY:
1669                         execute_find_directory (job);
1670                         break;
1671                 case GNOME_VFS_OP_XFER:
1672                         execute_xfer (job);
1673                         break;
1674                 case GNOME_VFS_OP_GET_FILE_INFO:
1675                         execute_get_file_info (job);
1676                         break;
1677                 case GNOME_VFS_OP_SET_FILE_INFO:
1678                         execute_set_file_info (job);
1679                         break;
1680                 case GNOME_VFS_OP_FILE_CONTROL:
1681                         execute_file_control (job);
1682                         break;
1683                 default:
1684                         g_warning (_("Unknown job kind %u"), job->op->type);
1685                         break;
1686                 }
1687                 /* NB. 'job' is quite probably invalid now */
1688                 clear_current_job ();
1689         } else {
1690                 switch (job->op->type) {
1691                 case GNOME_VFS_OP_READ:
1692                 case GNOME_VFS_OP_WRITE:
1693                         job->op->type = GNOME_VFS_OP_READ_WRITE_DONE;
1694                         break;
1695                 default:
1696                         break;
1697                 }
1698         }
1699         
1700         JOB_DEBUG (("done job %u", id));
1701 }
1702
1703 void
1704 _gnome_vfs_job_module_cancel (GnomeVFSJob *job)
1705 {
1706         GnomeVFSCancellation *cancellation;
1707
1708         JOB_DEBUG (("%u", GPOINTER_TO_UINT (job->job_handle)));
1709         
1710         cancellation = gnome_vfs_context_get_cancellation (job->op->context);
1711         if (cancellation != NULL) {
1712                 JOB_DEBUG (("cancelling %u", GPOINTER_TO_UINT (job->job_handle)));
1713                 gnome_vfs_cancellation_cancel (cancellation);
1714         }
1715
1716 #ifdef OLD_CONTEXT_DEPRECATED   
1717         gnome_vfs_context_emit_message (job->op->context, _("Operation stopped"));
1718 #endif /* OLD_CONTEXT_DEPRECATED */
1719
1720         /* Since we are cancelling, we won't have anyone respond to notifications;
1721          * set the expectations right.
1722          */
1723         JOB_DEBUG (("done %u", GPOINTER_TO_UINT (job->job_handle)));
1724 }
1725
1726 static void
1727 set_current_job (GnomeVFSJob *job)
1728 {
1729         /* There shouldn't have been anything here. */
1730         g_assert (g_static_private_get (&job_private) == NULL);
1731
1732         g_static_private_set (&job_private, job, NULL);
1733
1734         _gnome_vfs_module_callback_use_stack_info (job->op->stack_info);
1735         _gnome_vfs_module_callback_set_in_async_thread (TRUE);
1736 }
1737
1738 static void
1739 clear_current_job (void)
1740 {
1741         g_static_private_set (&job_private, NULL, NULL);
1742
1743         _gnome_vfs_module_callback_clear_stacks ();
1744 }
1745
1746 void
1747 _gnome_vfs_get_current_context (GnomeVFSContext **context)
1748 {
1749         GnomeVFSJob *job;
1750         
1751         g_return_if_fail (context != NULL);
1752
1753         job = g_static_private_get (&job_private);
1754
1755         if (job != NULL) {
1756                 *context = job->op->context;
1757         } else {
1758                 *context = NULL;
1759         }
1760 }
1761
1762 void
1763 _gnome_vfs_dispatch_module_callback (GnomeVFSAsyncModuleCallback callback,
1764                                     gconstpointer in, gsize in_size,
1765                                     gpointer out, gsize out_size,
1766                                     gpointer user_data,
1767                                     GnomeVFSModuleCallbackResponse response,
1768                                     gpointer response_data)
1769 {
1770         GnomeVFSJob *job;
1771         GnomeVFSNotifyResult notify_result;
1772
1773         job = g_static_private_get (&job_private);
1774
1775         g_return_if_fail (job != NULL);
1776
1777         memset (&notify_result, 0, sizeof (notify_result));
1778
1779         notify_result.job_handle = job->job_handle;
1780
1781         notify_result.type = GNOME_VFS_OP_MODULE_CALLBACK;
1782
1783         notify_result.specifics.callback.callback       = callback;
1784         notify_result.specifics.callback.user_data      = user_data;
1785         notify_result.specifics.callback.in             = in;
1786         notify_result.specifics.callback.in_size        = in_size;
1787         notify_result.specifics.callback.out            = out;
1788         notify_result.specifics.callback.out_size       = out_size;
1789         notify_result.specifics.callback.out            = out;
1790         notify_result.specifics.callback.out_size       = out_size;
1791         notify_result.specifics.callback.response       = response;
1792         notify_result.specifics.callback.response_data  = response_data;
1793
1794         job_notify (job, &notify_result);
1795 }