1 | <refentry id="gnome-vfs-writing-modules" revision="16 Apr 2004"> |
---|
2 | <refmeta> |
---|
3 | <refentrytitle>Writing Modules</refentrytitle> |
---|
4 | <manvolnum>3</manvolnum> |
---|
5 | <refmiscinfo>GNOME-VFS Library</refmiscinfo> |
---|
6 | </refmeta> |
---|
7 | |
---|
8 | <refnamediv> |
---|
9 | <refname>Writing Modules</refname><refpurpose>basic gnome-vfs module concepts</refpurpose> |
---|
10 | </refnamediv> |
---|
11 | |
---|
12 | <refsect1 id="Introduction"> |
---|
13 | <title>Introduction</title> |
---|
14 | |
---|
15 | <para>This section will introduce the basic concepts that are |
---|
16 | needed for writing GNOME Virtual File System modules.</para> |
---|
17 | |
---|
18 | <refsect2 id="uris"> |
---|
19 | <title>GNOME VFS URIs (Uniform Resource Identifiers)</title> |
---|
20 | |
---|
21 | <para>The GNOME Virtual file system uses URIs similiar to the |
---|
22 | standard WWW URIs. The basic difference between a VFS URI and |
---|
23 | WWW URI is that, while with WWW URIs you can only use a single |
---|
24 | protocol for accessing a certain file, with GNOME VFS URIs you |
---|
25 | can combine different access methods in sequence.</para> |
---|
26 | |
---|
27 | <para>For example, suppose you want to access file |
---|
28 | <filename>hello.c</filename> in a <filename>tar.gz</filename> |
---|
29 | file which is in turn accessible through FTP from a remote |
---|
30 | machine. In order to access this file, you would need to:</para> |
---|
31 | |
---|
32 | <orderedlist> |
---|
33 | <listitem><para>Connect to the FTP site.</para></listitem> |
---|
34 | |
---|
35 | <listitem><para>Fetch the <filename>tar.gz</filename> |
---|
36 | file.</para></listitem> |
---|
37 | |
---|
38 | <listitem><para>Decompress the <filename>tar.gz</filename> file using |
---|
39 | GZIP.</para></listitem> |
---|
40 | |
---|
41 | <listitem><para>Extract <filename>hello.c</filename> from the resulting |
---|
42 | uncompressed <filename>tar</filename> file.</para></listitem> |
---|
43 | </orderedlist> |
---|
44 | |
---|
45 | <para>The GNOME Virtual File System lets you express this by |
---|
46 | combining the three access methods (i.e. tar, GZIP and FTP) |
---|
47 | into a single URI. Access methods are combined in the URI by |
---|
48 | using the `#' character, followed by the name for the access |
---|
49 | method and the subpath for that specific access method. The |
---|
50 | subpath can be omitted for those storage methods that do not |
---|
51 | need a path to retrieve the file. (For example, a GZIP file |
---|
52 | always contains a single uncompressed file, so no path is |
---|
53 | needed to locate the uncompressed file within the GZIP file. |
---|
54 | But on the other hand, the TAR method requires a path to |
---|
55 | locate a specific file or directory.)</para> |
---|
56 | |
---|
57 | <para>For example, in the case we outlined above, the URI would |
---|
58 | look something like:</para> |
---|
59 | |
---|
60 | <!-- FixMe what should I use here instead of programlisting? --> |
---|
61 | <programlisting> |
---|
62 | |
---|
63 | ftp://username:password@host.net/path/to/file.tar.gz#gzip:#tar:/path/to/hello.c</programlisting> |
---|
64 | |
---|
65 | <para>Each method/subpath couple is called a <firstterm>URI |
---|
66 | element</firstterm>. When URI elements are combined like this, |
---|
67 | each URI element uses the previous one to access a base resource |
---|
68 | into which it will look up a file, using the subpath |
---|
69 | information. For this reason, we will say that each element is |
---|
70 | the <firstterm>parent</firstterm> element for the following one.</para> |
---|
71 | |
---|
72 | <para>The first URI element, the one which has no parent, is |
---|
73 | called the <firstterm>toplevel element</firstterm>. It does not |
---|
74 | use the `#' character; instead, it uses the standard syntax of |
---|
75 | WWW URIs: </para> |
---|
76 | |
---|
77 | <programlisting> |
---|
78 | |
---|
79 | method://user:password@host/path/to/file</programlisting> |
---|
80 | |
---|
81 | <para>This way, normal WWW URIs can be used with the GNOME Virtual |
---|
82 | File System.</para> |
---|
83 | |
---|
84 | <para>Toplevel elements are also special because they let users |
---|
85 | specify user names, passwords and host names, while |
---|
86 | non-toplevel elements don't.</para> |
---|
87 | </refsect2> |
---|
88 | |
---|
89 | <refsect2> |
---|
90 | <title>The <structname>GnomeVFSURI</structname> type</title> |
---|
91 | |
---|
92 | <para>Within the GNOME Virtual File System library, URI elements |
---|
93 | are represented by a special type, |
---|
94 | <structname>GnomeVFSURI</structname>, which is meant to represent |
---|
95 | user-provided URIs in a machine-optimized way. </para> |
---|
96 | |
---|
97 | <para>Every <structname>GnomeVFSURI</structname> contains the |
---|
98 | following information:</para> |
---|
99 | |
---|
100 | <itemizedlist> |
---|
101 | <listitem><para>A reference counter</para></listitem> |
---|
102 | |
---|
103 | <listitem><para>A pointer to the parent |
---|
104 | <structname>GnomeVFSURI</structname> URI element.</para></listitem> |
---|
105 | |
---|
106 | <listitem><para>The subpath.</para></listitem> |
---|
107 | |
---|
108 | <listitem><para>The name of the access method.</para></listitem> |
---|
109 | |
---|
110 | <listitem><para>A pointer to a |
---|
111 | <structname>GnomeVFSMethod</structname> object, describing the |
---|
112 | access method (see below).</para></listitem> |
---|
113 | </itemizedlist> |
---|
114 | |
---|
115 | </refsect2> |
---|
116 | |
---|
117 | </refsect1> |
---|
118 | |
---|
119 | <refsect1> |
---|
120 | <title>GNOME Virtual File System access method implementation</title> |
---|
121 | |
---|
122 | <para>In the GNOME Virtual File System, the implementations for |
---|
123 | all the access methods are loaded at runtime, as shared library |
---|
124 | modules. The modules are loaded during parsing of the string URI. |
---|
125 | If the parser encounters an access method for which no |
---|
126 | implementation is currently loaded, it retrieves the corresponding |
---|
127 | library file, dynamically links it into the executable, and |
---|
128 | initializes it.</para> |
---|
129 | |
---|
130 | <para>After initialization, the module returns a special |
---|
131 | <structname>GnomeVFSMethod</structname> object that contains |
---|
132 | pointers to the various implementation functions for that specific |
---|
133 | method. By storing a pointer to this object into the |
---|
134 | <structname>GnomeVFSURI</structname> type, the VFS library is then |
---|
135 | able to use these functions for file access.</para> |
---|
136 | |
---|
137 | <refsect2> |
---|
138 | <title>How file access is performed</title> |
---|
139 | |
---|
140 | <para>When the VFS library needs to perform some file operation, |
---|
141 | it performs the following steps:</para> |
---|
142 | |
---|
143 | <itemizedlist> |
---|
144 | |
---|
145 | <listitem><para>If the URI is given in textual form (i.e. as a |
---|
146 | string), it parses it and activates the necessary access method |
---|
147 | modules.</para></listitem> |
---|
148 | |
---|
149 | <listitem><para>It retrieves a pointer to the lowmost |
---|
150 | level URI element.</para></listitem> |
---|
151 | |
---|
152 | <listitem><para>It retrieves a pointer to the |
---|
153 | <structname>GnomeVFSMethod</structname> object that corresponds |
---|
154 | to the access method for that URI element.</para></listitem> |
---|
155 | |
---|
156 | <listitem><para>It retrieves a pointer to the implementation |
---|
157 | function for that operation from the |
---|
158 | <structname>GnomeVFSMethod</structname>object.</para></listitem> |
---|
159 | |
---|
160 | <listitem><para>It invokes that implementation function |
---|
161 | passing the pointer to the lowmost level URI |
---|
162 | element.</para></listitem> |
---|
163 | |
---|
164 | </itemizedlist> |
---|
165 | |
---|
166 | <para>Combining the access methods is always done within the |
---|
167 | method implementation. If the method implementation needs to do |
---|
168 | some file operation on the the parent URI element, it can do so |
---|
169 | by simply invoking the corresponding VFS function, by using |
---|
170 | the parent pointer in the <structname>GnomeVFSURI</structname> |
---|
171 | object. </para> |
---|
172 | |
---|
173 | <para>For example, suppose you have to read a simple URI like |
---|
174 | the following:</para> |
---|
175 | |
---|
176 | <!-- FixMe what should I use here instead of programlisting? --> |
---|
177 | <programlisting> |
---|
178 | |
---|
179 | file:/home/ettore/file.gz#gzip:</programlisting> |
---|
180 | |
---|
181 | <para>In this case, the GZIP method will be invoked with a |
---|
182 | pointer to the <structname>GnomeVFSURI</structname> describing the |
---|
183 | `gzip' part. The GZIP method will be able to read |
---|
184 | <filename>file.gz</filename> by just invoking the corresponding |
---|
185 | GNOME VFS library function on its parent and decompressing it on |
---|
186 | the fly. </para> |
---|
187 | |
---|
188 | </refsect2> |
---|
189 | |
---|
190 | </refsect1> |
---|
191 | |
---|
192 | <refsect1> |
---|
193 | <title>Implementing an access method in practice</title> |
---|
194 | |
---|
195 | <para>Implementing a new access method is really not difficult at |
---|
196 | all. This section explains how this is done.</para> |
---|
197 | |
---|
198 | <refsect2> |
---|
199 | <title>Using shared libraries</title> |
---|
200 | |
---|
201 | <para>Every module must be compiled as a shared library (i.e. a |
---|
202 | <filename>.so</filename> file).</para> |
---|
203 | |
---|
204 | <para>The current way for accessing the right module for the |
---|
205 | right method is very simple, and is based on file names. In |
---|
206 | practice, a module implementing an access method named |
---|
207 | <filename>foo</filename> must be named |
---|
208 | <filename>libfoo.so</filename>. For example, the module |
---|
209 | implementing the <filename>ftp:</filename> access method is |
---|
210 | called <filename>libftp.so</filename>, the module implementing |
---|
211 | <filename>#gzip:</filename> access is called |
---|
212 | <filename>libgzip.so</filename> and so on.</para> |
---|
213 | |
---|
214 | <para>This might change in the future.</para> |
---|
215 | |
---|
216 | </refsect2> |
---|
217 | |
---|
218 | <refsect2> |
---|
219 | <title>The initialization/shutdown functions</title> |
---|
220 | |
---|
221 | <para>Every shared library module must provide two functions:</para> |
---|
222 | |
---|
223 | <programlisting role="c"> |
---|
224 | |
---|
225 | GnomeVFSMethod *vfs_module_init (const char *method_name, const char *args); |
---|
226 | void vfs_module_shutdown (GnomeVFSMethod *method);</programlisting> |
---|
227 | |
---|
228 | <para>These are the only functions that the VFS library will |
---|
229 | access directly. All the other symbols (i.e. functions and |
---|
230 | variables) in the module should be made static. </para> |
---|
231 | |
---|
232 | <para><function>vfs_module_init()</function> is called |
---|
233 | as soon as the module is loaded in memory. It will have to |
---|
234 | return a pointer to a <structname>GnomeVFSMethod</structname> |
---|
235 | object that will contain the pointers to the method's |
---|
236 | implementation functions. We will describe this later. </para> |
---|
237 | |
---|
238 | <para><function>vfs_module_shutdown</function>, instead, |
---|
239 | is called before the module is unloaded or the program that uses |
---|
240 | it dies. This functions should:</para> |
---|
241 | |
---|
242 | <itemizedlist> |
---|
243 | |
---|
244 | <listitem><para>Deallocate all the memory allocated by the |
---|
245 | module.</para></listitem> |
---|
246 | |
---|
247 | <listitem><para>Close all the file descriptors associated with |
---|
248 | the module.</para></listitem> |
---|
249 | |
---|
250 | <listitem><para>Kill any external process spawned by the |
---|
251 | module.</para></listitem> |
---|
252 | |
---|
253 | <listitem><para>In general, make sure that any operation that |
---|
254 | was going on before this function was called will be |
---|
255 | interrupted correctly, as soon as possible and without any |
---|
256 | leaks.</para></listitem> |
---|
257 | |
---|
258 | </itemizedlist> |
---|
259 | |
---|
260 | </refsect2> |
---|
261 | |
---|
262 | <refsect2> |
---|
263 | <title>The <structname>GnomeVFSMethod</structname> object</title> |
---|
264 | |
---|
265 | <para>This object contains pointers to the module |
---|
266 | implementation functions.</para> |
---|
267 | |
---|
268 | <programlisting role="c"> |
---|
269 | GnomeVFSResult (* open) (GnomeVFSMethod *method, |
---|
270 | GnomeVFSMethodHandle **method_handle_return, |
---|
271 | GnomeVFSURI *uri, |
---|
272 | GnomeVFSOpenMode mode, |
---|
273 | GnomeVFSContext *context); |
---|
274 | |
---|
275 | GnomeVFSResult (* create) (GnomeVFSMethod *method, |
---|
276 | GnomeVFSMethodHandle **method_handle_return, |
---|
277 | GnomeVFSURI *uri, |
---|
278 | GnomeVFSOpenMode mode, |
---|
279 | gboolean exclusive, |
---|
280 | guint perm, |
---|
281 | GnomeVFSContext *context); |
---|
282 | |
---|
283 | GnomeVFSResult (* close) (GnomeVFSMethod *method, |
---|
284 | GnomeVFSMethodHandle *method_handle, |
---|
285 | GnomeVFSContext *context); |
---|
286 | |
---|
287 | GnomeVFSResult (* read) (GnomeVFSMethod *method, |
---|
288 | GnomeVFSMethodHandle *method_handle, |
---|
289 | gpointer buffer, |
---|
290 | GnomeVFSFileSize num_bytes, |
---|
291 | GnomeVFSFileSize *bytes_read_return, |
---|
292 | GnomeVFSContext *context); |
---|
293 | |
---|
294 | GnomeVFSResult (* write) (GnomeVFSMethod *method, |
---|
295 | GnomeVFSMethodHandle *method_handle, |
---|
296 | gconstpointer buffer, |
---|
297 | GnomeVFSFileSize num_bytes, |
---|
298 | GnomeVFSFileSize *bytes_written_return, |
---|
299 | GnomeVFSContext *context); |
---|
300 | |
---|
301 | GnomeVFSResult (* seek) (GnomeVFSMethod *method, |
---|
302 | GnomeVFSMethodHandle *method_handle, |
---|
303 | GnomeVFSSeekPosition whence, |
---|
304 | GnomeVFSFileOffset offset, |
---|
305 | GnomeVFSContext *context); |
---|
306 | |
---|
307 | GnomeVFSResult (* tell) (GnomeVFSMethod *method, |
---|
308 | GnomeVFSMethodHandle *method_handle, |
---|
309 | GnomeVFSFileOffset *offset_return); |
---|
310 | |
---|
311 | GnomeVFSResult (* truncate) (GnomeVFSMethod *method, |
---|
312 | GnomeVFSMethodHandle **method_handle, |
---|
313 | GnomeVFSURI *uri, |
---|
314 | GnomeVFSFileInfoOptions options, |
---|
315 | GnomeVFSContext *context); |
---|
316 | |
---|
317 | GnomeVFSResult (* close_directory) (GnomeVFSMethod *method, |
---|
318 | GnomeVFSMethodHandle *method_handle, |
---|
319 | GnomeVFSContext *context); |
---|
320 | |
---|
321 | GnomeVFSResult (* read_directory) (GnomeVFSMethod *method, |
---|
322 | GnomeVFSMethodHandle *method_handle, |
---|
323 | GnomeVFSFileInfo *file_info, |
---|
324 | GnomeVFSContext *context); |
---|
325 | |
---|
326 | GnomeVFSResult (* get_file_info) (GnomeVFSMethod *method, |
---|
327 | GnomeVFSURI *uri, |
---|
328 | GnomeVFSFileInfo *file_info, |
---|
329 | GnomeVFSFileInfoOptions options, |
---|
330 | GnomeVFSContext *context); |
---|
331 | |
---|
332 | GnomeVFSResult (* get_file_info_from_handle) |
---|
333 | (GnomeVFSMethod *method, |
---|
334 | GnomeVFSMethodHandl e *method_handle, |
---|
335 | GnomeVFSFileInfo *file_info, |
---|
336 | GnomeVFSFileInfoOptions options, |
---|
337 | GnomeVFSContext *context); |
---|
338 | |
---|
339 | GnomeVFSResult (* truncate) (GnomeVFSMethod *method, |
---|
340 | GnomeVFSURI *uri, |
---|
341 | GnomeVFSFileSize length, |
---|
342 | GnomeVFSContext *context); |
---|
343 | |
---|
344 | GnomeVFSResult (* truncate_handle) (GnomeVFSMethod *method, |
---|
345 | GnomeVFSMethodHandle *handle, |
---|
346 | GnomeVFSFileSize length, |
---|
347 | GnomeVFSContext *context); |
---|
348 | |
---|
349 | gboolean (* is_local) (GnomeVFSMethod *method, |
---|
350 | const GnomeVFSURI *uri); |
---|
351 | |
---|
352 | GnomeVFSResult (* make_directory) (GnomeVFSMethod *method, |
---|
353 | GnomeVFSURI *uri, |
---|
354 | guint perm, |
---|
355 | GnomeVFSContext *context); |
---|
356 | |
---|
357 | GnomeVFSResult (* find_directory) (GnomeVFSMethod *method, |
---|
358 | GnomeVFSURI *find_near_uri, |
---|
359 | GnomeVFSFindDirectoryKind kind, |
---|
360 | GnomeVFSURI **result_uri, |
---|
361 | gboolean create_if_needed, |
---|
362 | gboolean find_if_needed, |
---|
363 | guint perm, |
---|
364 | GnomeVFSContext *context); |
---|
365 | |
---|
366 | GnomeVFSResult (* remove_directory) (GnomeVFSMethod *method, |
---|
367 | GnomeVFSURI *uri, |
---|
368 | GnomeVFSContext *context); |
---|
369 | |
---|
370 | GnomeVFSResult (* move) (GnomeVFSMethod *method, |
---|
371 | GnomeVFSURI *old_uri, |
---|
372 | GnomeVFSURI *new_uri, |
---|
373 | gboolean force_replace, |
---|
374 | GnomeVFSContext *context); |
---|
375 | |
---|
376 | GnomeVFSResult (* unlink) (GnomeVFSMethod *method, |
---|
377 | GnomeVFSURI *uri, |
---|
378 | GnomeVFSContext *context); |
---|
379 | |
---|
380 | GnomeVFSResult (* check_same_fs) (GnomeVFSMethod *method, |
---|
381 | GnomeVFSURI *a, |
---|
382 | GnomeVFSURI *b, |
---|
383 | gboolean *same_fs_return, |
---|
384 | GnomeVFSContext *context); |
---|
385 | |
---|
386 | GnomeVFSResult (* set_file_info) (GnomeVFSMethod *method, |
---|
387 | GnomeVFSURI *a, |
---|
388 | const GnomeVFSFileInfo *info, |
---|
389 | GnomeVFSSetFileInfoMask mask, |
---|
390 | GnomeVFSContext *context); |
---|
391 | |
---|
392 | GnomeVFSResult (* create_symbolic_link) (GnomeVFSMethod *method, |
---|
393 | GnomeVFSURI *uri, |
---|
394 | const gchar *target_reference, |
---|
395 | GnomeVFSContext *context); |
---|
396 | |
---|
397 | GnomeVFSResult (* monitor_add) (GnomeVFSMethod *method, |
---|
398 | GnomeVFSMethodHandle **method_handle_return, |
---|
399 | GnomeVFSURI *uri, |
---|
400 | GnomeVFSMonitorType monitor_type); |
---|
401 | |
---|
402 | GnomeVFSResult (* monitor_cancel) (GnomeVFSMethod *method, |
---|
403 | GnomeVFSMethodHandle *handle); |
---|
404 | |
---|
405 | GnomeVFSResult (* file_control) (GnomeVFSMethod *method, |
---|
406 | GnomeVFSMethodHandle *method_handle, |
---|
407 | const char *operation, |
---|
408 | gpointer operation_data, |
---|
409 | GnomeVFSContext *context); |
---|
410 | </programlisting> |
---|
411 | |
---|
412 | </refsect2> |
---|
413 | |
---|
414 | </refsect1> |
---|
415 | |
---|
416 | <refsect1> |
---|
417 | <title>Handling cancellation</title> |
---|
418 | |
---|
419 | <para>As VFS operations might take very long to complete, especially in the |
---|
420 | case of transient errors (such as a network server that has gone down), |
---|
421 | the GNOME Virtual File System Library provides a standard way to |
---|
422 | handle the cancellation of VFS operations.</para> |
---|
423 | |
---|
424 | <refsect2> |
---|
425 | <title>The <structname>GnomeVFSCancellation</structname> object</title> |
---|
426 | |
---|
427 | <para>The object that encapsulates this functionality is |
---|
428 | <structname>GnomeVFSCancellation</structname>. Most |
---|
429 | implementation functions get a pointer to such an object, and are |
---|
430 | expected to use this object to recognize when an operation should |
---|
431 | be interrupted.</para> |
---|
432 | |
---|
433 | <para>The most simple way to check for a cancellation request is |
---|
434 | to poll the object with |
---|
435 | <function>gnome_vfs_cancellation_check()</function>:</para> |
---|
436 | |
---|
437 | <programlisting role="c"> |
---|
438 | |
---|
439 | gboolean gnome_vfs_cancellation_check (GnomeVFSCancellation *cancellation);</programlisting> |
---|
440 | |
---|
441 | <para>This function will return a nonzero value if the current |
---|
442 | operation should be cancelled.</para> |
---|
443 | |
---|
444 | <para>Notice that cancellation is an asynchronous operation that |
---|
445 | might happen outside your function, in parallel with the code that |
---|
446 | you are writing. For example, in the case of threads, the request |
---|
447 | will be set in the master thread; in the case of slave |
---|
448 | CORBA-driven processes, the request will be activated by a Unix |
---|
449 | signal. So you can expect a cancellation request to happen (and |
---|
450 | consequently be signalled in |
---|
451 | <structname>GnomeVFSCancellation</structname>) at any time.</para> |
---|
452 | |
---|
453 | <para>For this reason, you should be calling this function |
---|
454 | periodically, whenever you are going to perform several |
---|
455 | iterations of the same task, or execute a single expensive task. |
---|
456 | When the function returns a nonzero value, the correct way to |
---|
457 | react is:</para> |
---|
458 | |
---|
459 | <orderedlist> |
---|
460 | <listitem><para>Clean things up so that the result of the |
---|
461 | operations that have been performed are all |
---|
462 | cancelled.</para></listitem> |
---|
463 | <listitem><para>Return the |
---|
464 | <symbol>GNOME_VFS_ERROR_CANCELLED</symbol> error |
---|
465 | code.</para></listitem> |
---|
466 | </orderedlist> |
---|
467 | |
---|
468 | <para>Note, there are some other situations in which you want to |
---|
469 | be able to interrupt an I/O operation when a cancellation request |
---|
470 | is performed. In such cases, polling is not a viable option.</para> |
---|
471 | |
---|
472 | <para>For this reason, |
---|
473 | <structname>GnomeVFSCancellation</structname> provides an |
---|
474 | alternative way of sending notifications, using a file |
---|
475 | descriptor. To use this feature, you should use the following |
---|
476 | function:</para> |
---|
477 | |
---|
478 | <programlisting> |
---|
479 | |
---|
480 | gint gnome_vfs_cancellation_get_fd (GnomeVFSCancellation *cancellation); </programlisting> |
---|
481 | |
---|
482 | <para>When this function is called, it will return an open file |
---|
483 | descriptor, which is the read-side of a pipe. The pipe will be |
---|
484 | given a character from the write side as soon as a cancellation |
---|
485 | request is sent. You can check for a cancellation by using the |
---|
486 | <function>select()</function> system call with this file descriptor. |
---|
487 | As soon as <function>select</function> reports that some |
---|
488 | data is available on this file descriptor, you know that a |
---|
489 | cancellation is being requested.</para> |
---|
490 | |
---|
491 | <para>For example, if you are reading from a file descriptor and |
---|
492 | you want to check for a pending cancellation at the same time, |
---|
493 | you can set up <function>select</function>for checking if data |
---|
494 | is available on both the cancellation file descriptor and the |
---|
495 | file descriptor you are reading from.</para> |
---|
496 | </refsect2> |
---|
497 | |
---|
498 | <refsect2> |
---|
499 | <title>Dealing with <symbol>EINTR</symbol></title> |
---|
500 | |
---|
501 | <para>In order to maximize the chance of cancelling an operation |
---|
502 | immediately, the GNOME Virtual File System can sends a signal to |
---|
503 | the asynchronous thread or process. This does not happen on all |
---|
504 | the systems and setups, though.</para> |
---|
505 | |
---|
506 | <para>The result of this is that, if a process is in the middle |
---|
507 | of a Unix system call while receiving this signal, the system |
---|
508 | call might be interrupted and return a <symbol>EINTR</symbol> |
---|
509 | error.</para> |
---|
510 | |
---|
511 | <para>For this reason, when you receive <symbol>EINTR</symbol> |
---|
512 | you should check if a cancellation request is pending, using |
---|
513 | <function>gnome_vfs_cancellation_check()</function> on the |
---|
514 | <structname>GnomeVFSCancellation</structname> object that the |
---|
515 | implementation function received:</para> |
---|
516 | |
---|
517 | <itemizedlist> |
---|
518 | <listitem><para>If a cancellation is indeed pending |
---|
519 | (<function>gnome_vfs_cancellation_check()</function> returns a |
---|
520 | nonzero value), you should cancel the operation, cleaning up |
---|
521 | all the effects, and return |
---|
522 | <symbol>GNOME_VFS_ERROR_INTERRUPTED</symbol> or |
---|
523 | <symbol>GNOME_VFS_ERROR_CANCELLED</symbol></para></listitem> |
---|
524 | |
---|
525 | <listitem><para>Otherwise, retry the system call as you would |
---|
526 | normally do.</para></listitem> |
---|
527 | </itemizedlist> |
---|
528 | </refsect2> |
---|
529 | |
---|
530 | </refsect1> |
---|
531 | |
---|
532 | <refsect1> |
---|
533 | <title>Basic guidelines for writing a module</title> |
---|
534 | |
---|
535 | <para>Writing GNOME VFS modules is easy, but there are a few |
---|
536 | things that you must keep in mind when hacking them:</para> |
---|
537 | |
---|
538 | <itemizedlist> |
---|
539 | <listitem><para>All of the code must be completely thread safe. |
---|
540 | The reason for this is that the asynchronous GNOME VFS engine |
---|
541 | will use threads when available; if you don't make sure that the |
---|
542 | code is thread-safe, every kind of weird and unexpected errors |
---|
543 | will happen. As debugging these problems can be very hard, it's |
---|
544 | important to write the code with threads in mind right from the |
---|
545 | start.</para></listitem> |
---|
546 | |
---|
547 | <listitem><para>Use the special |
---|
548 | <function>gnome_vfs_*_cancellable()</function> VFS functions |
---|
549 | instead of the standard non-cancellable ones, passing them the |
---|
550 | same <structname>GnomeVFSCancellation</structname> object you |
---|
551 | are given, so that the operation can always be interrrupted at |
---|
552 | any time.</para></listitem> |
---|
553 | |
---|
554 | <listitem><para>The code should respect the basic GNOME |
---|
555 | guidelines for source code indentation and |
---|
556 | style.</para></listitem> |
---|
557 | </itemizedlist> |
---|
558 | |
---|
559 | <refsect2> |
---|
560 | <title>How to make the code thread safe</title> |
---|
561 | |
---|
562 | <para>Although it might sound scary at first, making the code |
---|
563 | for the modules thread safe is not complicated at all.</para> |
---|
564 | |
---|
565 | <para>First of all, make sure the amount of global variables is |
---|
566 | kept to the bare minimum. If possible, you should avoid them at |
---|
567 | all cost.</para> |
---|
568 | |
---|
569 | <para>For those cases where globals are inevitable (such as |
---|
570 | caches, connection pools or things like that), you have to make |
---|
571 | sure every variable is properly associated with a mutex, and |
---|
572 | that the mutex is locked before every access to this variable |
---|
573 | and released afterwards. You can also use |
---|
574 | <function>G_LOCK_DEFINE_STATIC</function>, |
---|
575 | <function>G_LOCK</function> and <function>G_UNLOCK</function> |
---|
576 | for this. |
---|
577 | </para> |
---|
578 | |
---|
579 | <para>Generally speaking, if you are going to dynamically |
---|
580 | allocate structures that are shared by more than one |
---|
581 | operation/file, you should provide all of them with their nice |
---|
582 | mutex locks.</para> |
---|
583 | |
---|
584 | <para>Finally, make sure mutexes are used only if they are |
---|
585 | available. One way to do so is to use macros like the |
---|
586 | following:</para> |
---|
587 | |
---|
588 | <programlisting> |
---|
589 | |
---|
590 | #ifdef G_THREADS_ENABLED |
---|
591 | #define MUTEX_NEW() g_mutex_new () |
---|
592 | #define MUTEX_FREE(a) g_mutex_free (a) |
---|
593 | #define MUTEX_LOCK(a) if ((a) != NULL) g_mutex_lock (a) |
---|
594 | #define MUTEX_UNLOCK(a) if ((a) != NULL) g_mutex_unlock (a) |
---|
595 | #else |
---|
596 | #define MUTEX_NEW() NULL |
---|
597 | #define MUTEX_FREE(a) |
---|
598 | #define MUTEX_LOCK(a) |
---|
599 | #define MUTEX_UNLOCK(a) |
---|
600 | #endif</programlisting> |
---|
601 | |
---|
602 | <para><function>G_LOCK_DEFINE_STATIC</function>, |
---|
603 | <function>G_LOCK</function> and <function>G_UNLOCK</function> in |
---|
604 | GLib are always safe to use, as they are already defined to be |
---|
605 | nothing when thread support is not available.</para> |
---|
606 | |
---|
607 | <para>(Probably it would be a good idea to have something in the |
---|
608 | private GNOME VFS API that does this stuff for all the |
---|
609 | modules.)</para> |
---|
610 | |
---|
611 | </refsect2> |
---|
612 | </refsect1> |
---|
613 | |
---|
614 | </refentry> |
---|