GPU workaround to simulate Out of Memory errors with large textures
[chromium-blink-merge.git] / native_client_sdk / src / doc / devguide / coding / file-io.rst
blob17df6d47d8967bc7cb8c57d08b5ca1ca47eafb53
1 .. _devguide-coding-fileio:
3 ########
4 File I/O
5 ########
7 .. contents::
8   :local:
9   :backlinks: none
10   :depth: 2
12 Introduction
13 ============
15 This section describes how to use the `FileIO API
16 </native-client/pepper_stable/cpp/classpp_1_1_file_i_o>`_ to read and write
17 files using a local secure data store.
19 You might use the File IO API with the URL Loading APIs to create an overall
20 data download and caching solution for your NaCl applications. For example:
22 #. Use the File IO APIs to check the local disk to see if a file exists that
23    your program needs.
24 #. If the file exists locally, load it into memory using the File IO API. If
25    the file doesn't exist locally, use the URL Loading API to retrieve the
26    file from the server.
27 #. Use the File IO API to write the file to disk.
28 #. Load the file into memory using the File IO API when needed by your
29    application.
31 The example discussed in this section is included in the SDK in the directory
32 ``examples/api/file_io``.
34 Reference information
35 =====================
37 For reference information related to FileIO, see the following documentation:
39 * `file_io.h </native-client/pepper_stable/cpp/file__io_8h>`_ - API to create a
40   FileIO object
41 * `file_ref.h </native-client/pepper_stable/cpp/file__ref_8h>`_ - API to create
42   a file reference or "weak pointer" to a file in a file system
43 * `file_system.h </native-client/pepper_stable/cpp/file__system_8h>`_ - API to
44   create a file system associated with a file
46 Local file I/O
47 ==============
49 Chrome provides an obfuscated, restricted area on disk to which a web app can
50 safely `read and write files
51 <https://developers.google.com/chrome/whitepapers/storage#persistent>`_. The
52 Pepper FileIO, FileRef, and FileSystem APIs (collectively called the File IO
53 APIs) allow you to access this sandboxed local disk so you can read and write
54 files and manage caching yourself. The data is persistent between launches of
55 Chrome, and is not removed unless your application deletes it or the user
56 manually deletes it. There is no limit to the amount of local data you can
57 use, other than the actual space available on the local drive.
59 .. _quota_management:
60 .. _enabling_file_access:
62 Enabling local file I/O
63 -----------------------
65 The easiest way to enable the writing of persistent local data is to include
66 the `unlimitedStorage permission
67 </extensions/declare_permissions#unlimitedStorage>`_ in your Chrome Web Store
68 manifest file. With this permission you can use the Pepper FileIO API without
69 the need to request disk space at run time. When the user installs the app
70 Chrome displays a message announcing that the app writes to the local disk.
72 If you do not use the ``unlimitedStorage`` permission you must include
73 JavaScript code that calls the `HTML5 Quota Management API
74 <http://updates.html5rocks.com/2011/11/Quota-Management-API-Fast-Facts>`_ to
75 explicitly request local disk space before using the FileIO API. In this case
76 Chrome will prompt the user to accept a requestQuota call every time one is
77 made.
79 Testing local file I/O
80 ----------------------
82 You should be aware that using the ``unlimitedStorage`` manifest permission
83 constrains the way you can test your app. Three of the four techniques
84 described in :doc:`Running Native Client Applications <../devcycle/running>`
85 read the Chrome Web Store manifest file and enable the ``unlimitedStorage``
86 permission when it appears, but the first technique (local server) does not.
87 If you want to test the file IO portion of your app with a simple local server,
88 you need to include JavaScript code that calls the HTML5 Quota Management API.
89 When you deliver your application you can replace this code with the
90 ``unlimitedStorage`` manifest permission.
92 The ``file_io`` example
93 =======================
95 The Native Client SDK includes an example, ``file_io``, that demonstrates how
96 to read and write a local disk file. Since you will probably run the example
97 from a local server without a Chrome Web Store manifest file, the example's
98 index file uses JavaScript to perform the Quota Management setup as described
99 above. The example has these primary files:
101 * ``index.html`` - The HTML code that launches the Native Client module and
102   displays the user interface.
103 * ``example.js`` - JavaScript code that requests quota (as described above). It
104   also listens for user interaction with the user interface, and forwards the
105   requests to the Native Client module.
106 * ``file_io.cc`` - The code that sets up and provides an entry point to the
107   Native Client module.
109 The remainder of this section covers the code in the ``file_io.cc`` file for
110 reading and writing files.
112 File I/O overview
113 -----------------
115 Like many Pepper APIs, the File IO API includes a set of methods that execute
116 asynchronously and that invoke callback functions in your Native Client module.
117 Unlike most other examples, the ``file_io`` example also demonstrates how to
118 make Pepper calls synchronously on a worker thread.
120 It is illegal to make blocking calls to Pepper on the module's main thread.
121 This restriction is lifted when running on a worker thread---this is called
122 "calling Pepper off the main thread". This often simplifies the logic of your
123 code; multiple asynchronous Pepper functions can be called from one function on
124 your worker thread, so you can use the stack and standard control flow
125 structures normally.
127 The high-level flow for the ``file_io`` example is described below.  Note that
128 methods in the namespace ``pp`` are part of the Pepper C++ API.
130 Creating and writing a file
131 ---------------------------
133 Following are the high-level steps involved in creating and writing to a
134 file:
136 #. ``pp::FileIO::Open`` is called with the ``PP_FILEOPEN_FLAG_CREATE`` flag to
137    create a file.  Because the callback function is ``pp::BlockUntilComplete``,
138    this thread is blocked until ``Open`` succeeds or fails.
139 #. ``pp::FileIO::Write`` is called to write the contents. Again, the thread is
140    blocked until the call to ``Write`` completes. If there is more data to
141    write, ``Write`` is called again.
142 #. When there is no more data to write, call ``pp::FileIO::Flush``.
144 Opening and reading a file
145 --------------------------
147 Following are the high-level steps involved in opening and reading a file:
149 #. ``pp::FileIO::Open`` is called to open the file. Because the callback
150    function is ``pp::BlockUntilComplete``, this thread is blocked until Open
151    succeeds or fails.
152 #. ``pp::FileIO::Query`` is called to query information about the file, such as
153    its file size. The thread is blocked until ``Query`` completes.
154 #. ``pp::FileIO::Read`` is called to read the contents. The thread is blocked
155    until ``Read`` completes. If there is more data to read, ``Read`` is called
156    again.
158 Deleting a file
159 ---------------
161 Deleting a file is straightforward: call ``pp::FileRef::Delete``. The thread is
162 blocked until ``Delete`` completes.
164 Making a directory
165 ------------------
167 Making a directory is also straightforward: call ``pp::File::MakeDirectory``.
168 The thread is blocked until ``MakeDirectory`` completes.
170 Listing the contents of a directory
171 -----------------------------------
173 Following are the high-level steps involved in listing a directory:
175 #. ``pp::FileRef::ReadDirectoryEntries`` is called, and given a directory entry
176    to list. A callback is given as well; many of the other functions use
177    ``pp::BlockUntilComplete``, but ``ReadDirectoryEntries`` returns results in
178    its callback, so it must be specified.
179 #. When the call to ``ReadDirectoryEntries`` completes, it calls
180    ``ListCallback`` which packages up the results into a string message, and
181    sends it to JavaScript.
183 ``file_io`` deep dive
184 =====================
186 The ``file_io`` example displays a user interface with a couple of fields and
187 several buttons. Following is a screenshot of the ``file_io`` example:
189 .. image:: /images/fileioexample.png
191 Each radio button is a file operation you can perform, with some reasonable
192 default values for filenames. Try typing a message in the large input box and
193 clicking ``Save``, then switching to the ``Load File`` operation, and
194 clicking ``Load``.
196 Let's take a look at what is going on under the hood.
198 Opening a file system and preparing for file I/O
199 ------------------------------------------------
201 ``pp::Instance::Init`` is called when an instance of a module is created. In
202 this example, ``Init`` starts a new thread (via the ``pp::SimpleThread``
203 class), and tells it to open the filesystem:
205 .. naclcode::
207   virtual bool Init(uint32_t /*argc*/,
208                     const char * /*argn*/ [],
209                     const char * /*argv*/ []) {
210     file_thread_.Start();
211     // Open the file system on the file_thread_. Since this is the first
212     // operation we perform there, and because we do everything on the
213     // file_thread_ synchronously, this ensures that the FileSystem is open
214     // before any FileIO operations execute.
215     file_thread_.message_loop().PostWork(
216         callback_factory_.NewCallback(&FileIoInstance::OpenFileSystem));
217     return true;
218   }
220 When the file thread starts running, it will call ``OpenFileSystem``. This
221 calls ``pp::FileSystem::Open`` and blocks the file thread until the function
222 returns.
224 .. Note::
225   :class: note
227   Note that the call to ``pp::FileSystem::Open`` uses
228   ``pp::BlockUntilComplete`` as its callback. This is only possible because we
229   are running off the main thread; if you try to make a blocking call from the
230   main thread, the function will return the error
231   ``PP_ERROR_BLOCKS_MAIN_THREAD``.
233 .. naclcode::
235   void OpenFileSystem(int32_t /*result*/) {
236     int32_t rv = file_system_.Open(1024 * 1024, pp::BlockUntilComplete());
237     if (rv == PP_OK) {
238       file_system_ready_ = true;
239       // Notify the user interface that we're ready
240       PostMessage("READY|");
241     } else {
242       ShowErrorMessage("Failed to open file system", rv);
243     }
244   }
246 Handling messages from JavaScript
247 ---------------------------------
249 When you click the ``Save`` button, JavaScript posts a message to the NaCl
250 module with the file operation to perform sent as a string (See :doc:`Messaging
251 System <message-system>` for more details on message passing). The string is
252 parsed by ``HandleMessage``, and new work is added to the file thread:
254 .. naclcode::
256   virtual void HandleMessage(const pp::Var& var_message) {
257     if (!var_message.is_string())
258       return;
260     // Parse message into: instruction file_name_length file_name [file_text]
261     std::string message = var_message.AsString();
262     std::string instruction;
263     std::string file_name;
264     std::stringstream reader(message);
265     int file_name_length;
267     reader >> instruction >> file_name_length;
268     file_name.resize(file_name_length);
269     reader.ignore(1);  // Eat the delimiter
270     reader.read(&file_name[0], file_name_length);
272     ...
274     // Dispatch the instruction
275     if (instruction == kLoadPrefix) {
276       file_thread_.message_loop().PostWork(
277           callback_factory_.NewCallback(&FileIoInstance::Load, file_name));
278     } else if (instruction == kSavePrefix) {
279       ...
280     }
281   }
283 Saving a file
284 -------------
286 ``FileIoInstance::Save`` is called when the ``Save`` button is pressed. First,
287 it checks to see that the FileSystem has been successfully opened:
289 .. naclcode::
291   if (!file_system_ready_) {
292     ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
293     return;
294   }
296 It then creates a ``pp::FileRef`` resource with the name of the file. A
297 ``FileRef`` resource is a weak reference to a file in the FileSystem; that is,
298 a file can still be deleted even if there are outstanding ``FileRef``
299 resources.
301 .. naclcode::
303   pp::FileRef ref(file_system_, file_name.c_str());
305 Next, a ``pp::FileIO`` resource is created and opened. The call to
306 ``pp::FileIO::Open`` passes ``PP_FILEOPEFLAG_WRITE`` to open the file for
307 writing, ``PP_FILEOPENFLAG_CREATE`` to create a new file if it doesn't already
308 exist and ``PP_FILEOPENFLAG_TRUNCATE`` to clear the file of any previous
309 content:
311 .. naclcode::
313   pp::FileIO file(this);
315   int32_t open_result =
316       file.Open(ref,
317                 PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE |
318                     PP_FILEOPENFLAG_TRUNCATE,
319                 pp::BlockUntilComplete());
320   if (open_result != PP_OK) {
321     ShowErrorMessage("File open for write failed", open_result);
322     return;
323   }
325 Now that the file is opened, it is written to in chunks. In an asynchronous
326 model, this would require writing a separate function, storing the current
327 state on the free store and a chain of callbacks. Because this function is
328 called off the main thread, ``pp::FileIO::Write`` can be called synchronously
329 and a conventional do/while loop can be used:
331 .. naclcode::
333   int64_t offset = 0;
334   int32_t bytes_written = 0;
335   do {
336     bytes_written = file.Write(offset,
337                                file_contents.data() + offset,
338                                file_contents.length(),
339                                pp::BlockUntilComplete());
340     if (bytes_written > 0) {
341       offset += bytes_written;
342     } else {
343       ShowErrorMessage("File write failed", bytes_written);
344       return;
345     }
346   } while (bytes_written < static_cast<int64_t>(file_contents.length()));
348 Finally, the file is flushed to push all changes to disk:
350 .. naclcode::
352   int32_t flush_result = file.Flush(pp::BlockUntilComplete());
353   if (flush_result != PP_OK) {
354     ShowErrorMessage("File fail to flush", flush_result);
355     return;
356   }
358 Loading a file
359 --------------
361 ``FileIoInstance::Load`` is called when the ``Load`` button is pressed. Like
362 the ``Save`` function, ``Load`` first checks to see if the FileSystem has been
363 successfully opened, and creates a new ``FileRef``:
365 .. naclcode::
367   if (!file_system_ready_) {
368     ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
369     return;
370   }
371   pp::FileRef ref(file_system_, file_name.c_str());
373 Next, ``Load`` creates and opens a new ``FileIO`` resource, passing
374 ``PP_FILEOPENFLAG_READ`` to open the file for reading. The result is compared
375 to ``PP_ERROR_FILENOTFOUND`` to give a better error message when the file
376 doesn't exist:
378 .. naclcode::
380   int32_t open_result =
381       file.Open(ref, PP_FILEOPENFLAG_READ, pp::BlockUntilComplete());
382   if (open_result == PP_ERROR_FILENOTFOUND) {
383     ShowErrorMessage("File not found", open_result);
384     return;
385   } else if (open_result != PP_OK) {
386     ShowErrorMessage("File open for read failed", open_result);
387     return;
388   }
390 Then ``Load`` calls ``pp::FileIO::Query`` to get metadata about the file, such
391 as its size. This is used to allocate a ``std::vector`` buffer that holds the
392 data from the file in memory:
394 .. naclcode::
396   int32_t query_result = file.Query(&info, pp::BlockUntilComplete());
397   if (query_result != PP_OK) {
398     ShowErrorMessage("File query failed", query_result);
399     return;
400   }
402   ...
404   std::vector<char> data(info.size);
406 Similar to ``Save``, a conventional while loop is used to read the file into
407 the newly allocated buffer:
409 .. naclcode::
411   int64_t offset = 0;
412   int32_t bytes_read = 0;
413   int32_t bytes_to_read = info.size;
414   while (bytes_to_read > 0) {
415     bytes_read = file.Read(offset,
416                            &data[offset],
417                            data.size() - offset,
418                            pp::BlockUntilComplete());
419     if (bytes_read > 0) {
420       offset += bytes_read;
421       bytes_to_read -= bytes_read;
422     } else if (bytes_read < 0) {
423       // If bytes_read < PP_OK then it indicates the error code.
424       ShowErrorMessage("File read failed", bytes_read);
425       return;
426     }
427   }
429 Finally, the contents of the file are sent back to JavaScript, to be displayed
430 on the page. This example uses "``DISP|``" as a prefix command for display
431 information:
433 .. naclcode::
435   std::string string_data(data.begin(), data.end());
436   PostMessage("DISP|" + string_data);
437   ShowStatusMessage("Load success");
439 Deleting a file
440 ---------------
442 ``FileIoInstance::Delete`` is called when the ``Delete`` button is pressed.
443 First, it checks whether the FileSystem has been opened, and creates a new
444 ``FileRef``:
446 .. naclcode::
448   if (!file_system_ready_) {
449     ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
450     return;
451   }
452   pp::FileRef ref(file_system_, file_name.c_str());
454 Unlike ``Save`` and ``Load``, ``Delete`` is called on the ``FileRef`` resource,
455 not a ``FileIO`` resource. Note that the result is checked for
456 ``PP_ERROR_FILENOTFOUND`` to give a better error message when trying to delete
457 a non-existent file:
459 .. naclcode::
461   int32_t result = ref.Delete(pp::BlockUntilComplete());
462   if (result == PP_ERROR_FILENOTFOUND) {
463     ShowStatusMessage("File/Directory not found");
464     return;
465   } else if (result != PP_OK) {
466     ShowErrorMessage("Deletion failed", result);
467     return;
468   }
470 Listing files in a directory
471 ----------------------------
473 ``FileIoInstance::List`` is called when the ``List Directory`` button is
474 pressed. Like all other operations, it checks whether the FileSystem has been
475 opened and creates a new ``FileRef``:
477 .. naclcode::
479   if (!file_system_ready_) {
480     ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
481     return;
482   }
484   pp::FileRef ref(file_system_, dir_name.c_str());
486 Unlike the other operations, it does not make a blocking call to
487 ``pp::FileRef::ReadDirectoryEntries``. Since ``ReadDirectoryEntries`` returns
488 the resulting directory entries in its callback, a new callback object is
489 created pointing to ``FileIoInstance::ListCallback``.
491 The ``pp::CompletionCallbackFactory`` template class is used to instantiate a
492 new callback. Notice that the ``FileRef`` resource is passed as a parameter;
493 this will add a reference count to the callback object, to keep the ``FileRef``
494 resource from being destroyed when the function finishes.
496 .. naclcode::
498   // Pass ref along to keep it alive.
499   ref.ReadDirectoryEntries(callback_factory_.NewCallbackWithOutput(
500       &FileIoInstance::ListCallback, ref));
502 ``FileIoInstance::ListCallback`` then gets the results passed as a
503 ``std::vector`` of ``pp::DirectoryEntry`` objects, and sends them to
504 JavaScript:
506 .. naclcode::
508   void ListCallback(int32_t result,
509                     const std::vector<pp::DirectoryEntry>& entries,
510                     pp::FileRef /*unused_ref*/) {
511     if (result != PP_OK) {
512       ShowErrorMessage("List failed", result);
513       return;
514     }
516     std::stringstream ss;
517     ss << "LIST";
518     for (size_t i = 0; i < entries.size(); ++i) {
519       pp::Var name = entries[i].file_ref().GetName();
520       if (name.is_string()) {
521         ss << "|" << name.AsString();
522       }
523     }
524     PostMessage(ss.str());
525     ShowStatusMessage("List success");
526   }
528 Making a new directory
529 ----------------------
531 ``FileIoInstance::MakeDir`` is called when the ``Make Directory`` button is
532 pressed. Like all other operations, it checks whether the FileSystem has been
533 opened and creates a new ``FileRef``:
535 .. naclcode::
537   if (!file_system_ready_) {
538     ShowErrorMessage("File system is not open", PP_ERROR_FAILED);
539     return;
540   }
541   pp::FileRef ref(file_system_, dir_name.c_str());
543 Then the ``pp::FileRef::MakeDirectory`` function is called.
545 .. naclcode::
547   int32_t result = ref.MakeDirectory(
548       PP_MAKEDIRECTORYFLAG_NONE, pp::BlockUntilComplete());
549   if (result != PP_OK) {
550     ShowErrorMessage("Make directory failed", result);
551     return;
552   }
553   ShowStatusMessage("Make directory success");