diff options
author | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-18 00:06:24 +0000 |
---|---|---|
committer | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-18 00:06:24 +0000 |
commit | 0f5e11c4d7e510ff5283b8cb359c0ef0cab66ed1 (patch) | |
tree | be3815790e23582e6690d779c3bfb72c5a18f9fe /native_client_sdk | |
parent | 05e38fd0546e5301f476cfb388f998c75fd0ec37 (diff) | |
download | chromium_src-0f5e11c4d7e510ff5283b8cb359c0ef0cab66ed1.zip chromium_src-0f5e11c4d7e510ff5283b8cb359c0ef0cab66ed1.tar.gz chromium_src-0f5e11c4d7e510ff5283b8cb359c0ef0cab66ed1.tar.bz2 |
[NaCl SDK] Add html5fs mount to pepper example.
Requires calling all nacl_mounts calls off main thread.
BUG=none
TBR=noelallen@chromium.org
NOTRY=true
Review URL: https://codereview.chromium.org/11610003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173585 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
6 files changed, 263 insertions, 44 deletions
diff --git a/native_client_sdk/src/examples/hello_nacl_mounts/example.dsc b/native_client_sdk/src/examples/hello_nacl_mounts/example.dsc index 7c40eac..abffe26 100644 --- a/native_client_sdk/src/examples/hello_nacl_mounts/example.dsc +++ b/native_client_sdk/src/examples/hello_nacl_mounts/example.dsc @@ -9,6 +9,8 @@ 'handlers.h', 'hello_nacl_mounts.c', 'hello_nacl_mounts.h', + 'queue.c', + 'queue.h', ], 'LIBS': ['ppapi', 'pthread', 'nacl_mounts'] } diff --git a/native_client_sdk/src/examples/hello_nacl_mounts/example.js b/native_client_sdk/src/examples/hello_nacl_mounts/example.js index 5dcb456..bc6786b 100644 --- a/native_client_sdk/src/examples/hello_nacl_mounts/example.js +++ b/native_client_sdk/src/examples/hello_nacl_mounts/example.js @@ -2,12 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Start up the paint timer when the NaCl module has loaded. function moduleDidLoad() { common.hideModule(); } // Called by the common.js module. +function domContentLoaded(name, tc, config, width, height) { + window.webkitStorageInfo.requestQuota(window.PERSISTENT, 1024*1024, + function(bytes) { + common.updateStatus( + 'Allocated '+bytes+' bytes of persistant storage.'); + common.createNaClModule(name, tc, config, width, height); + common.attachDefaultListeners(); + }, + function(e) { alert('Failed to allocate space') }); +} + +// Called by the common.js module. function attachListeners() { var radioEls = document.querySelectorAll('input[type="radio"]'); for (var i = 0; i < radioEls.length; ++i) { diff --git a/native_client_sdk/src/examples/hello_nacl_mounts/hello_nacl_mounts.c b/native_client_sdk/src/examples/hello_nacl_mounts/hello_nacl_mounts.c index 850f921..9782e9e 100644 --- a/native_client_sdk/src/examples/hello_nacl_mounts/hello_nacl_mounts.c +++ b/native_client_sdk/src/examples/hello_nacl_mounts/hello_nacl_mounts.c @@ -9,6 +9,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <pthread.h> #include "ppapi/c/pp_errors.h" #include "ppapi/c/pp_module.h" @@ -22,7 +23,7 @@ #include "nacl_mounts/kernel_intercept.h" #include "handlers.h" - +#include "queue.h" #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -30,6 +31,9 @@ #define va_copy(d, s) ((d) = (s)) #endif +int mount(const char *source, const char *target, const char *filesystemtype, + unsigned long mountflags, const void *data); + typedef struct { const char* name; @@ -37,10 +41,12 @@ typedef struct { } FuncNameMapping; +static PP_Instance g_instance = 0; +static PPB_GetInterface get_browser_interface = NULL; static PPB_Messaging* ppb_messaging_interface = NULL; static PPB_Var* ppb_var_interface = NULL; -static FuncNameMapping g_FunctionMap[] = { +static FuncNameMapping g_function_map[] = { { "fopen", HandleFopen }, { "fwrite", HandleFwrite }, { "fread", HandleFread }, @@ -49,6 +55,8 @@ static FuncNameMapping g_FunctionMap[] = { { NULL, NULL }, }; +/** A handle to the thread the handles messages. */ +static pthread_t g_handle_message_thread; /** * Create a new PP_Var from a C string. @@ -197,7 +205,7 @@ static size_t ParseMessage(char* message, * @param[in] function_name The function name to look up. * @return The handler function mapped to |function_name|. */ static HandleFunc GetFunctionByName(const char* function_name) { - FuncNameMapping* map_iter = g_FunctionMap; + FuncNameMapping* map_iter = g_function_map; for (; map_iter->name; ++map_iter) { if (strcmp(map_iter->name, function_name) == 0) { return map_iter->function; @@ -207,37 +215,10 @@ static HandleFunc GetFunctionByName(const char* function_name) { return NULL; } - -static PP_Bool Instance_DidCreate(PP_Instance instance, - uint32_t argc, - const char* argn[], - const char* argv[]) { - /* Initialize nacl mounts. */ - ki_init(NULL); - return PP_TRUE; -} - - -static void Instance_DidDestroy(PP_Instance instance) { -} - -static void Instance_DidChangeView(PP_Instance instance, - PP_Resource view_resource) { -} - -static void Instance_DidChangeFocus(PP_Instance instance, - PP_Bool has_focus) { -} - -static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance, - PP_Resource url_loader) { - /* NaCl modules do not need to handle the document load function. */ - return PP_FALSE; -} - -static void Messaging_HandleMessage(PP_Instance instance, - struct PP_Var message) { - char buffer[1024]; +/** Handle as message from JavaScript on the worker thread. + * + * @param[in] message The message to parse and handle. */ +static void HandleMessage(char* message) { char* function_name; char* params[MAX_PARAMS]; size_t num_params; @@ -245,17 +226,13 @@ static void Messaging_HandleMessage(PP_Instance instance, int result; HandleFunc function; - /* Read the message from JavaScript. */ - VarToCStr(message, &buffer[0], 1024); - - /* Parse it */ - num_params = ParseMessage(buffer, &function_name, ¶ms[0], MAX_PARAMS); + num_params = ParseMessage(message, &function_name, ¶ms[0], MAX_PARAMS); function = GetFunctionByName(function_name); if (!function) { /* Function name wasn't found. Error. */ ppb_messaging_interface->PostMessage( - instance, PrintfToVar("Error: Unknown function \"%s\"", function)); + g_instance, PrintfToVar("Error: Unknown function \"%s\"", function)); } /* Function name was found, call it. */ @@ -274,19 +251,82 @@ static void Messaging_HandleMessage(PP_Instance instance, } /* Post the error to JavaScript, so the user can see it. */ - ppb_messaging_interface->PostMessage(instance, var); + ppb_messaging_interface->PostMessage(g_instance, var); return; } if (output != NULL) { /* Function returned an output string. Send it to JavaScript. */ - ppb_messaging_interface->PostMessage(instance, CStrToVar(output)); + ppb_messaging_interface->PostMessage(g_instance, CStrToVar(output)); free(output); } } +/** A worker thread that handles messages from JavaScript. + * @param[in] user_data Unused. + * @return unused. */ +void* HandleMessageThread(void* user_data) { + while (1) { + char* message = DequeueMessage(); + HandleMessage(message); + free(message); + } +} + + +static PP_Bool Instance_DidCreate(PP_Instance instance, + uint32_t argc, + const char* argn[], + const char* argv[]) { + g_instance = instance; + ki_init_ppapi(NULL, instance, get_browser_interface); + mount( + "", /* source */ + "/persistent", /* target */ + "html5fs", /* filesystemtype */ + 0, /* mountflags */ + "type=PERSISTENT,expected_size=1048576"); /* data */ + + pthread_create(&g_handle_message_thread, NULL, &HandleMessageThread, NULL); + InitializeMessageQueue(); + + return PP_TRUE; +} + + +static void Instance_DidDestroy(PP_Instance instance) { +} + +static void Instance_DidChangeView(PP_Instance instance, + PP_Resource view_resource) { +} + +static void Instance_DidChangeFocus(PP_Instance instance, + PP_Bool has_focus) { +} + +static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance, + PP_Resource url_loader) { + /* NaCl modules do not need to handle the document load function. */ + return PP_FALSE; +} + +static void Messaging_HandleMessage(PP_Instance instance, + struct PP_Var message) { + char buffer[1024]; + VarToCStr(message, &buffer[0], 1024); + if (!EnqueueMessage(strdup(buffer))) { + struct PP_Var var; + var = PrintfToVar( + "Warning: dropped message \"%s\" because the queue was full.", + message); + ppb_messaging_interface->PostMessage(g_instance, var); + } +} + PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id, PPB_GetInterface get_browser) { + get_browser_interface = get_browser; ppb_messaging_interface = (PPB_Messaging*)(get_browser(PPB_MESSAGING_INTERFACE)); ppb_var_interface = (PPB_Var*)(get_browser(PPB_VAR_INTERFACE)); diff --git a/native_client_sdk/src/examples/hello_nacl_mounts/index.html b/native_client_sdk/src/examples/hello_nacl_mounts/index.html index a30b2b2..e216a51 100644 --- a/native_client_sdk/src/examples/hello_nacl_mounts/index.html +++ b/native_client_sdk/src/examples/hello_nacl_mounts/index.html @@ -12,9 +12,30 @@ found in the LICENSE file. <script type="text/javascript" src="common.js"></script> <script type="text/javascript" src="example.js"></script> </head> -<body data-name="<NAME>" data-tc="<tc>" data-path="<path>"> +<body data-name="<NAME>" data-tc="<tc>" data-path="<path>" + data-custom-load="true"> <h1><TITLE></h1> <h2>Status: <code id="statusField">NO-STATUS</code></h2> + <p> + This example shows how you can use standard C library file operation + functions in Native Client using a library called nacl_mounts. + </p> + <p> + nacl_mounts provides a virtual filesystem. The filesystem can be "mounted" + in a given directory tree. When you perform operations on files in those + directories, the mount determines how those operations should be performed. + </p> + <p> + This example has three mounts by default. + <ol> + <li><i>/</i> the root of the filesystem. This is a memory mount, and + is non-persistent.</li> + <li><i>/persistent</i> a persistent storage area. Any data written + here can be read back after Chrome is restarted.</li> + <li><i>/dev</i> a mount containing some utility files. /dev/null, + /dev/zero, etc.</li> + </ol> + </p> <div> <span> <input type="radio" id="radiofopen" name="group" checked="checked">fopen diff --git a/native_client_sdk/src/examples/hello_nacl_mounts/queue.c b/native_client_sdk/src/examples/hello_nacl_mounts/queue.c new file mode 100644 index 0000000..bd0c438 --- /dev/null +++ b/native_client_sdk/src/examples/hello_nacl_mounts/queue.c @@ -0,0 +1,115 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "queue.h" + +#include <pthread.h> +#include <stdlib.h> + +#define MAX_QUEUE_SIZE 16 + +/** A mutex that guards |g_queue|. */ +static pthread_mutex_t g_queue_mutex; + +/** A condition variable that is signalled when |g_queue| is not empty. */ +static pthread_cond_t g_queue_not_empty_cond; + +/** A circular queue of messages from JavaScript to be handled. + * + * If g_queue_start < g_queue_end: + * all elements in the range [g_queue_start, g_queue_end) are valid. + * If g_queue_start > g_queue_end: + * all elements in the ranges [0, g_queue_end) and + * [g_queue_start, MAX_QUEUE_SIZE) are valid. + * If g_queue_start == g_queue_end, and g_queue_size > 0: + * all elements in the g_queue are valid. + * If g_queue_start == g_queue_end, and g_queue_size == 0: + * No elements are valid. */ +static char* g_queue[MAX_QUEUE_SIZE]; + +/** The index of the head of the queue. */ +static int g_queue_start = 0; + +/** The index of the tail of the queue, non-inclusive. */ +static int g_queue_end = 0; + +/** The size of the queue. */ +static int g_queue_size = 0; + +/** Return whether the queue is empty. + * + * NOTE: this function assumes g_queue_mutex lock is held. + * @return non-zero if the queue is empty. */ +static int IsQueueEmpty() { + return g_queue_size == 0; +} + +/** Return whether the queue is full. + * + * NOTE: this function assumes g_queue_mutex lock is held. + * @return non-zero if the queue is full. */ +static int IsQueueFull() { + return g_queue_size == MAX_QUEUE_SIZE; +} + +/** Initialize the message queue. */ +void InitializeMessageQueue() { + pthread_mutex_init(&g_queue_mutex, NULL); + pthread_cond_init(&g_queue_not_empty_cond, NULL); +} + +/** Enqueue a message (i.e. add to the end) + * + * If the queue is full, the message will be dropped. + * + * NOTE: this function assumes g_queue_mutex is _NOT_ held. + * @param[in] message The message to enqueue. + * @return non-zero if the message was added to the queue. */ +int EnqueueMessage(char* message) { + pthread_mutex_lock(&g_queue_mutex); + + /* We shouldn't block the main thread waiting for the queue to not be full, + * so just drop the message. */ + if (IsQueueFull()) { + pthread_mutex_unlock(&g_queue_mutex); + free(message); + return 0; + } + + g_queue[g_queue_end] = message; + g_queue_end = (g_queue_end + 1) % MAX_QUEUE_SIZE; + g_queue_size++; + + pthread_cond_signal(&g_queue_not_empty_cond); + + pthread_mutex_unlock(&g_queue_mutex); + + return 1; +} + +/** Dequeue a message and return it. + * + * This function blocks until a message is available. It should not be called + * on the main thread. + * + * NOTE: this function assumes g_queue_mutex is _NOT_ held. + * @return The message at the head of the queue. */ +char* DequeueMessage() { + char* message = NULL; + + pthread_mutex_lock(&g_queue_mutex); + + while (IsQueueEmpty()) { + pthread_cond_wait(&g_queue_not_empty_cond, &g_queue_mutex); + } + + message = g_queue[g_queue_start]; + g_queue_start = (g_queue_start + 1) % MAX_QUEUE_SIZE; + g_queue_size--; + + pthread_mutex_unlock(&g_queue_mutex); + + return message; +} diff --git a/native_client_sdk/src/examples/hello_nacl_mounts/queue.h b/native_client_sdk/src/examples/hello_nacl_mounts/queue.h new file mode 100644 index 0000000..ed267f8 --- /dev/null +++ b/native_client_sdk/src/examples/hello_nacl_mounts/queue.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2012 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef QUEUE_H_ +#define QUEUE_H_ + +/* This file implements a single-producer/single-consumer queue, using a mutex + * and a condition variable. + * + * There are techniques to implement a queue like this without using memory + * barriers or locks on x86, but ARM's memory system is different from x86, so + * we cannot make the same assuptions about visibility order of writes. Using a + * mutex is slower, but also simpler. + * + * We make the assumption that messages are only enqueued on the main thread + * and consumed on the worker thread. Because we don't want to block the main + * thread, EnqueueMessage will return zero if the message could not be enqueued. + * + * DequeueMessage will block until a message is available using a condition + * variable. Again, this may not be as fast as spin-waiting, but will consume + * much less CPU (and battery), which is important to consider for ChromeOS + * devices. */ + +void InitializeMessageQueue(); +int EnqueueMessage(char* message); +char* DequeueMessage(); + +#endif /* QUEUE_H_ */ |