Pages

Oct 4, 2012

Simulating Blocking PPAPI functions in Native Client



Simulating blocking functions in Native Client

One of the largest hurdles in porting to Native Client is the Pepper API; a powerful, secure plugin api for Chrome, which NaCl utilizes to gain access to lower-level operations through the sandbox. Along with being secure, Pepper has two specific restrictions that we’ve covered before


  1. All pepper calls must come from the main thread
  2. All pepper calls must be non-blocking

Which, as a developer porting over to NaCl, may throw a wrench in the works, considering that most C library APIs for system access are blocking functions (fread, fopen come to mind directly). We’ve mentioned that this is a temporary restriction, and soon you’ll be able to call pepper functions off the main thread. In the meantime, however, it’d be nice to have a way to allow calls coming from the main thread to interact with Pepper in a blocking manner; this will reduce the amount of code you’ll have to change in your codebase to support the NaCl platform.

Thanks to some great insight and help from NaCl engineer Bradley Nelson, we can offer just that capability using a fancy technology : coroutines.

Sometimes, Blocking is the right thing to do.


From wikipedia :

Coroutines are computer program components that generalize subroutines to allow multiple entry points for suspending and resuming execution at certain locations. When subroutines are invoked, execution begins at the start and once a subroutine exits, it is finished; an instance of a subroutine only returns once. Coroutines are similar except they can also exit by calling other coroutines, which may later return to the point of calling in the original coroutine; from the coroutine point of view, it is not exciting at all but simply calling another coroutine.


Or for humans, coroutines are like global goto statements, that also preserve stack-state, and are exposed with a function pair : setjmp and longjmp. The setjmp function saves a stack environment, which you can subsequently restore, using longjmp. When used together, setjmp and longjmp provide a way to execute a non-local goto. They are typically used to pass execution control to error-handling or recovery code in a previously called routine without using the normal calling or return conventions.This process can be imagined to be a "jump" back to the point of program execution where setjmp saved the environment.

There’s plenty of documentation on how these APIs work, so I’ll ignore the description of walk-through here, and refer you to the resources section at the end of this article.

Using coroutines to emulate blocking Pepper Calls


Firstly, recall how Chrome interacts with Native Client; The PPAPIs are effectively plugin APIs that chrome calls back into, so execution control can either be owned by Chrome, or owned by your NaCl plugin; coprocessing is not an option. So, for us to simulate a blocking PPAPI call on the main thread, it must stop execution, store the state, and allow Chrome to regain execution control. Chrome can continue forward and process the pepper call, and in the resulting callback, we can process the result, and continue execution back where we left off. The diagram below shows this in more visual form:



When used correctly, the setjmp/longjmp pair allow us to save and restore executional state. Meaning we can exit the current program counter and stack, allow execution to occur at a different logic branch, and when the time is right, resume execution control back to where we wanted. This is perfect for what we want to accomplish with PPAPI.

The source code

You can grab the source code to a working example from my github repo; which will utilize the new, fancy Visual Studio 2010 add-in for native client. The source code wraps up most of the heavy lifting of how co-routines can be utilized in NaCl; so for the sake of brevity, I’ll show you the expected workflow using the C APIs.


static PP_Bool Instance_DidCreate(PP_Instance instance,
                                  uint32_t argc,
                                  const char* argn[],
                                  const char* argv[]) {
 appInstance_ = instance;
 //called when the app boots. Allows user to own their control loop
 coroutine::Create(userUpdate);
 return PP_TRUE;
}

When the application is created, the first thing we do is allow the co-routine system to set up the execution slicing. Simply calling coroutine::create is sufficient. You pass in as a parameter a function which will be ran during execution slicing.



void userUpdate(void) {
  for (;;) {
   
 //..some code goes here....
 
 //randomly kick off a file load call for whatever reason
 if(rand() %50 == 25)
  blockingURLRead();

 //some other code goes here....


 //IMPORTANT, at the end of the while-loop, 
 //we need to signal execution control back to chrome for processing 
 coroutine::Flush();
  }
}

One of the nice things about using this coroutine setup is that you gain the ability to treat your app updating as though you were a native application: by owning your processing loop.  In the code above, we randomly kick off a call to load some file from the intertubes. At the end of the loop, we need to call coroutine::flush which will handle some of the time-slice execution that we’ll be using to make all this work.



void blockingURLRead()
{

 //rather than actually calling readFileFromURL, 
 //we simulate some arbitrary Pepper API function by just calling 'callonmainthread'
 PP_CompletionCallback fileLoadedCB = PP_MakeCompletionCallback(onFileLoaded, 0);
 ppb_core_interface->CallOnMainThread(0, fileLoadedCB, 0);

 //now that the async call has been kicked off to chrome, 
 //we yield execution control back to chrome so it can service it
 coroutine::Block();

 //once 'onFileLoaded' has been called, we resume control here.
 printf(&fileIOBuffer[0]);// AKA DO SOMETHING WITH THE FILE DATA YOU LOADED!
}


The blocking URL read will kick off the Async pepper call, which simply pushes a command into Chrome’s processing queue; Recall Chrome won’t process this event until it regains execution control sometime in the future.
Once kicked off, coroutine::Block is called, which will halt execution of the main thread at this location, yielding execution control back to Chrome to do processing.

void onFileLoaded(void* pData, int32_t dataSize)
{
 // process data returned from file loading
 strcpy(&fileIOBuffer[0], "I AM A CHEESEBURGER");

 //once all the data has been read, we can regain execution control
 coroutine::Resume();
}



Once chrome has time to process the command, it will issue the callback to your NaCl application with the results (onFileLoaded). For the sake of sanity, we move the data out of this callback system to a heap variable, and call coroutine::Resume, which will restore the stack state from where we last called coroutine:Block;

Caveats and limitations

There’s some nuances with getting this working correctly, most dominantly that we must manipulate the stack to allocate enough space such that when we resume our operations data on the stack didn’t get clobbered. For safety measures, I suggest increasing the stack size for your application (I’ve found 3MB to be sufficient for most apps) .
Also note that some IDEs (VS included) may do weird things when deep in this level of stack manipulation; so be warned that there may be dragons in the future.

But at least those dragons don’t require you to re-factor your code as much ;)

~Main

Resources:








You can find Colt McAnlis here:

  

2 comments: