The Wayback Machine - https://web.archive.org/web/20120119065455/http://mainroach.blogspot.com/

1.18.2012

NaCl And Pepper Thread communication.

Chrome provides a plugin API called Pepper (PPAPI) that NaCl uses to communicate with the underlying platform. As documented before, all pepper calls must come from the main thread (referred to after this as the ‘pepper’ thread). This includes FileIO, Audio, Rendering, and Input. It's also worth pointing out that these calls are all non-blocking which means that you call them, and some time in the future, the results are received. More specifically, Pepper APIs and message pumps run on the Pepper thread, along with your webpage script, and V8 / Javascript processing. Which means that blocking the main thread will result in your page processing hanging, causing your tab to stall.

Audio and Rendering are fire-and-forget style systems, and as such don’t require much modification to your game/app; You can call these on the main thread, and continue on withing having to worry about blocking. Input is poll-based, and can be quiried at any time. The real problem is FileIO, where most developers have expectations that the fileIO is blocking.

All of the FileSystemAPIs are non-blocking. As you make the call, your pepper thread will not halt and wait for the function to finish. Your results will be available the next frame cycle. POSIX style blocking functions are on the road-map, but their ETA is yet to occur.

One of the more common solutions to this, is to Spawn a new thread to act as your main thread. This thread will own your update/render loop. You can overload fopen / fread to kick off commands to the pepper thread, and spin-loop to wait for their result.
One of the key results in this is that you need some sort of nifty code that allows you to kick off calls to the main thread from your workers, and block until the results are finished.

Mad Props to NaCl PM Christian Stefansen who threw this snippet together.


Christian wrote a generic "do stuff on the main thread and block" class:

class GenericJob : public MainThreadJob {
     void (*function_)();
     public:
          ~GenericJob() {}
          GenericJob(void (*function)()) : function_(function) {}
          void Run(MainThreadRunner::JobEntry* e) {
               (*function_)();
               MainThreadRunner::ResultCompletion(e, 0);
          }
};

To run "func" on the main thread and block, you'd do something like


GenericJob* job = new GenericJob(func);
runner_->RunJob(job);
where runner_ is a pointer to a pre-initialized MainThreadRunner object.

With this, you should be able to overload fread/fwrite when called from worker threads to kick off read calls to the pepper thread to be serviced.