diff options
author | Ben Murdoch <benm@google.com> | 2013-08-07 11:04:47 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2013-08-07 11:04:47 +0100 |
commit | 3240926e260ce088908e02ac07a6cf7b0c0cbf44 (patch) | |
tree | 5ff5fa16b2fb9278c6d731b3d19123731f3f8626 /native_client_sdk | |
parent | 3b21a50ee4fe6f71bb117cbee9998a4f465eea9d (diff) | |
download | chromium_org-3240926e260ce088908e02ac07a6cf7b0c0cbf44.tar.gz |
Merge from Chromium at DEPS revision r216133
This commit was generated by merge_to_master.py.
Change-Id: I541d5d1d8520b6b3829fbc1fa18552bf9ad4a5c7
Diffstat (limited to 'native_client_sdk')
32 files changed, 1014 insertions, 428 deletions
diff --git a/native_client_sdk/src/build_tools/sdk_files.list b/native_client_sdk/src/build_tools/sdk_files.list index cbf0b8d7be..c3fd8302b7 100644 --- a/native_client_sdk/src/build_tools/sdk_files.list +++ b/native_client_sdk/src/build_tools/sdk_files.list @@ -355,12 +355,18 @@ include/newlib/arpa/inet.h include/newlib/netdb.h include/newlib/netinet/in.h include/newlib/netinet6/in6.h +include/newlib/poll.h +include/newlib/sys/select.h include/newlib/sys/socket.h +include/newlib/sys/utsname.h include/pnacl/arpa/inet.h include/pnacl/netdb.h include/pnacl/netinet/in.h include/pnacl/netinet6/in6.h +include/pnacl/poll.h +include/pnacl/sys/select.h include/pnacl/sys/socket.h +include/pnacl/sys/utsname.h include/ppapi/c/dev/deprecated_bool.h include/ppapi/c/dev/pp_cursor_type_dev.h include/ppapi/c/dev/pp_print_settings_dev.h @@ -597,9 +603,11 @@ include/sdk_util/thread_safe_queue.h [win]include/win/context.h [win]include/win/implement.h [win]include/win/need_errno.h +include/win/poll.h [win]include/win/pthread.h [win]include/win/sched.h [win]include/win/semaphore.h + [linux]lib/${PLATFORM}_host/Debug/libgmock.a [linux]lib/${PLATFORM}_host/Debug/libgtest.a [linux]lib/${PLATFORM}_host/Debug/libjsoncpp.a @@ -617,23 +625,19 @@ include/sdk_util/thread_safe_queue.h [win]lib/${PLATFORM}_x86_32_host/Debug/gmock.lib [win]lib/${PLATFORM}_x86_32_host/Debug/gtest.lib [win]lib/${PLATFORM}_x86_32_host/Debug/jsoncpp.lib -[win]lib/${PLATFORM}_x86_32_host/Debug/nacl_io.lib [win]lib/${PLATFORM}_x86_32_host/Debug/ppapi.lib [win]lib/${PLATFORM}_x86_32_host/Debug/ppapi_cpp.lib [win]lib/${PLATFORM}_x86_32_host/Debug/ppapi_cpp_private.lib [win]lib/${PLATFORM}_x86_32_host/Debug/ppapi_gles2.lib -[win]lib/${PLATFORM}_x86_32_host/Debug/ppapi_simple.lib [win]lib/${PLATFORM}_x86_32_host/Debug/pthread.lib [win]lib/${PLATFORM}_x86_32_host/Debug/sdk_util.lib [win]lib/${PLATFORM}_x86_32_host/Release/gmock.lib [win]lib/${PLATFORM}_x86_32_host/Release/gtest.lib [win]lib/${PLATFORM}_x86_32_host/Release/jsoncpp.lib -[win]lib/${PLATFORM}_x86_32_host/Release/nacl_io.lib [win]lib/${PLATFORM}_x86_32_host/Release/ppapi.lib [win]lib/${PLATFORM}_x86_32_host/Release/ppapi_cpp.lib [win]lib/${PLATFORM}_x86_32_host/Release/ppapi_cpp_private.lib [win]lib/${PLATFORM}_x86_32_host/Release/ppapi_gles2.lib -[win]lib/${PLATFORM}_x86_32_host/Release/ppapi_simple.lib [win]lib/${PLATFORM}_x86_32_host/Release/pthread.lib [win]lib/${PLATFORM}_x86_32_host/Release/sdk_util.lib lib/glibc_x86_32/Debug/libgmock.a @@ -828,6 +832,7 @@ src/jsoncpp/README.chromium src/Makefile src/nacl_io/event_emitter.cc src/nacl_io/event_listener.cc +src/nacl_io/inet_ntoa.cc src/nacl_io/kernel_handle.cc src/nacl_io/kernel_intercept.cc src/nacl_io/kernel_object.cc @@ -868,9 +873,12 @@ src/nacl_io/syscalls/lchown.c src/nacl_io/syscalls/link.c src/nacl_io/syscalls/mkdir.c src/nacl_io/syscalls/mount.c +src/nacl_io/syscalls/poll.c src/nacl_io/syscalls/remove.c src/nacl_io/syscalls/rmdir.c +src/nacl_io/syscalls/select.c src/nacl_io/syscalls/umount.c +src/nacl_io/syscalls/uname.c src/nacl_io/syscalls/unlink.c src/nacl_io/syscalls/utime.c [win]src/ppapi/make.bat diff --git a/native_client_sdk/src/build_tools/test_projects.py b/native_client_sdk/src/build_tools/test_projects.py index 8aac430c7a..a74292304e 100755 --- a/native_client_sdk/src/build_tools/test_projects.py +++ b/native_client_sdk/src/build_tools/test_projects.py @@ -105,6 +105,9 @@ def GetBrowserTesterCommand(desc, toolchain, config): ppapi_plugin += '.so' args.extend(['--ppapi_plugin', ppapi_plugin]) + if toolchain == 'pnacl': + args.extend(['--browser_flag', '--enable-pnacl']) + url = 'index.html' url += '?tc=%s&config=%s&test=true' % (toolchain, config) args.extend(['--url', url]) diff --git a/native_client_sdk/src/examples/demo/earth/earth.cc b/native_client_sdk/src/examples/demo/earth/earth.cc index f363b39eaf..d0c6de740c 100644 --- a/native_client_sdk/src/examples/demo/earth/earth.cc +++ b/native_client_sdk/src/examples/demo/earth/earth.cc @@ -4,16 +4,8 @@ #include <assert.h> #include <math.h> -#include <ppapi/c/pp_point.h> #include <ppapi/c/ppb_input_event.h> -#include <ppapi/cpp/completion_callback.h> -#include <ppapi/cpp/graphics_2d.h> -#include <ppapi/cpp/image_data.h> #include <ppapi/cpp/input_event.h> -#include <ppapi/cpp/instance.h> -#include <ppapi/cpp/module.h> -#include <ppapi/cpp/rect.h> -#include <ppapi/cpp/size.h> #include <ppapi/cpp/var.h> #include <ppapi/cpp/var_array.h> #include <ppapi/cpp/var_array_buffer.h> @@ -28,6 +20,11 @@ #include <algorithm> #include <string> +#include "ppapi_simple/ps.h" +#include "ppapi_simple/ps_context_2d.h" +#include "ppapi_simple/ps_event.h" +#include "ppapi_simple/ps_interface.h" +#include "ppapi_simple/ps_main.h" #include "sdk_util/macros.h" #include "sdk_util/thread_pool.h" @@ -179,20 +176,14 @@ inline uint32_t Clamp255(float x) { // The main object that runs the Earth demo. -class Planet : public pp::Instance { +class Planet { public: - explicit Planet(PP_Instance instance); + Planet(); virtual ~Planet(); - - virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); - - virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip); - - // Catch events. - virtual bool HandleInputEvent(const pp::InputEvent& event); - - // Catch messages posted from Javascript. - virtual void HandleMessage(const pp::Var& message); + // Runs a tick of the simulations, update 2D output. + void Update(); + // Handle event from user, or message from JS. + void HandleEvent(PSEvent* ps_event); private: // Methods prefixed with 'w' are run on worker threads. @@ -219,26 +210,14 @@ class Planet : public pp::Instance { uint32_t* pixels); void Reset(); - void PostInit(int32_t result); + void RequestTextures(); void UpdateSim(); void Render(); void Draw(); void StartBenchmark(); void EndBenchmark(); - - // Runs a tick of the simulations, updating all buffers. Flushes the - // contents of |image_data_| to the 2D graphics context. - void Update(); - // Post a small key-value message to update JS. void PostUpdateMessage(const char* message_name, double value); - // Create and initialize the 2D context used for drawing. - void CreateContext(const pp::Size& size); - // Destroy the 2D drawing context. - void DestroyContext(); - // Push the pixels to the browser, then attempt to flush the 2D context. - void FlushPixelBuffer(); - static void FlushCallback(void* data, int32_t result); // User Interface settings. These settings are controlled via html // controls or via user input. @@ -280,33 +259,22 @@ class Planet : public pp::Instance { Texture* night_tex_; int width_for_tex_; int height_for_tex_; - std::string name_for_tex_; // Quick ArcCos helper. ArcCosine acos_; // Misc. - pp::Graphics2D* graphics_2d_context_; - pp::ImageData* image_data_; + PSContext2D_t* ps_context_; int num_threads_; - int num_regions_; ThreadPool* workers_; - int width_; - int height_; - uint32_t stride_in_pixels_; - uint32_t* pixel_buffer_; - int benchmark_frame_counter_; bool benchmarking_; + int benchmark_frame_counter_; double benchmark_start_time_; double benchmark_end_time_; }; -bool Planet::Init(uint32_t argc, const char* argn[], const char* argv[]) { - // Request PPAPI input events for mouse & keyboard. - RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE); - RequestInputEvents(PP_INPUTEVENT_CLASS_WHEEL); - RequestInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); +void Planet::RequestTextures() { // Request a set of images from JS. After images are loaded by JS, a // message from JS -> NaCl will arrive containing the pixel data. See // HandleMessage() method in this file. @@ -316,8 +284,7 @@ bool Planet::Init(uint32_t argc, const char* argn[], const char* argv[]) { names.Set(0, "earth.jpg"); names.Set(1, "earthnight.jpg"); message.Set("names", names); - PostMessage(message); - return true; + PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var()); } void Planet::Reset() { @@ -378,47 +345,35 @@ void Planet::Reset() { } -Planet::Planet(PP_Instance instance) : pp::Instance(instance), - graphics_2d_context_(NULL), - image_data_(NULL), - num_regions_(256) { - width_ = 0; - height_ = 0; - stride_in_pixels_ = 0; - pixel_buffer_ = NULL; - benchmark_frame_counter_ = 0; - benchmarking_ = false; - base_tex_ = NULL; - night_tex_ = NULL; - name_for_tex_ = ""; +Planet::Planet() : base_tex_(NULL), night_tex_(NULL), num_threads_(0), + benchmarking_(false), benchmark_frame_counter_(0) { Reset(); - + RequestTextures(); // By default, render from the dispatch thread. - num_threads_ = 0; workers_ = new ThreadPool(num_threads_); + PSEventSetFilter(PSE_ALL); + ps_context_ = PSContext2DAllocate(); } Planet::~Planet() { delete workers_; - DestroyContext(); + PSContext2DFree(ps_context_); } // Given a region r, derive a rectangle. // This rectangle shouldn't overlap with work being done by other workers. // If multithreading, this function is only called by the worker threads. void Planet::wMakeRect(int r, int *x, int *y, int *w, int *h) { - int dy = height_ / num_regions_; *x = 0; - *w = width_; - *y = r * dy; - *h = dy; + *w = ps_context_->width; + *y = r; + *h = 1; } inline uint32_t* Planet::wGetAddr(int x, int y) { - assert(pixel_buffer_); - return (pixel_buffer_ + y * stride_in_pixels_) + x; + return ps_context_->data + x + y * ps_context_->stride / sizeof(uint32_t); } // This is the meat of the ray tracer. Given a pixel span (x0, x1) on @@ -430,7 +385,7 @@ void Planet::wRenderPixelSpan(int x0, int x1, int y) { const int kColorBlack = MakeRGBA(0, 0, 0, 0xFF); float y0 = eye_y_; float z0 = eye_z_; - float y1 = (static_cast<float>(y) / height_) * 2.0f - 1.0f; + float y1 = (static_cast<float>(y) / ps_context_->height) * 2.0f - 1.0f; float z1 = 0.0f; float dy = (y1 - y0); float dz = (z1 - z0); @@ -439,7 +394,7 @@ void Planet::wRenderPixelSpan(int x0, int x1, int y) { 2.0f * dz * (z0 - planet_z_); float planet_xyz_eye_xyz = planet_xyz_ + eye_xyz_; float y_y0_z_z0 = planet_y_ * y0 + planet_z_ * z0; - float oowidth = 1.0f / width_; + float oowidth = 1.0f / ps_context_->width; uint32_t* pixels = this->wGetAddr(x0, y); for (int x = x0; x <= x1; ++x) { // scan normalized screen -1..1 @@ -565,12 +520,8 @@ void Planet::wRenderRegionEntry(int region, void* thiz) { } // Renders the planet, dispatching the work to multiple threads. -// Note: This Dispatch() is from the main PPAPI thread, so care must be taken -// not to attempt PPAPI calls from the worker threads, since Dispatch() will -// block here until all work is complete. The worker threads are compute only -// and do not make any PPAPI calls. void Planet::Render() { - workers_->Dispatch(num_regions_, wRenderRegionEntry, this); + workers_->Dispatch(ps_context_->height, wRenderRegionEntry, this); } // Pre-calculations to make inner loops faster. @@ -669,16 +620,6 @@ void Planet::UpdateSim() { SetPlanetSpin(x, y); } -void Planet::DidChangeView(const pp::Rect& position, const pp::Rect& clip) { - if (position.size().width() == width_ && - position.size().height() == height_) - return; // Size didn't change, no need to update anything. - // Create a new device context with the new size. - DestroyContext(); - CreateContext(position.size()); - Update(); -} - void Planet::StartBenchmark() { // For more consistent benchmark numbers, reset to default state. Reset(); @@ -723,43 +664,81 @@ void Planet::SetTexture(const std::string& name, int width, int height, } } -// Handle input events from the user. -bool Planet::HandleInputEvent(const pp::InputEvent& event) { - switch (event.GetType()) { - case PP_INPUTEVENT_TYPE_KEYDOWN: { - pp::KeyboardInputEvent key(event); - uint32_t key_code = key.GetKeyCode(); - if (key_code == 84) // 't' key - if (!benchmarking_) - StartBenchmark(); - break; - } - case PP_INPUTEVENT_TYPE_MOUSEMOVE: - case PP_INPUTEVENT_TYPE_MOUSEDOWN: { - pp::MouseInputEvent mouse = pp::MouseInputEvent(event); - if (mouse.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { - PP_Point delta = mouse.GetMovement(); - float delta_x = static_cast<float>(delta.x); - float delta_y = static_cast<float>(delta.y); - float spin_x = std::min(4.0f, std::max(-4.0f, delta_x * 0.5f)); - float spin_y = std::min(4.0f, std::max(-4.0f, delta_y * 0.5f)); - ui_spin_x_ = spin_x / 100.0f; - ui_spin_y_ = spin_y / 100.0f; +// Handle input events from the user and messages from JS. +void Planet::HandleEvent(PSEvent* ps_event) { + // Give the 2D context a chance to process the event. + if (0 != PSContext2DHandleEvent(ps_context_, ps_event)) + return; + if (ps_event->type == PSE_INSTANCE_HANDLEINPUT) { + // Convert Pepper Simple event to a PPAPI C++ event + pp::InputEvent event(ps_event->as_resource); + switch (event.GetType()) { + case PP_INPUTEVENT_TYPE_KEYDOWN: { + pp::KeyboardInputEvent key(event); + uint32_t key_code = key.GetKeyCode(); + if (key_code == 84) // 't' key + if (!benchmarking_) + StartBenchmark(); + break; } - break; + case PP_INPUTEVENT_TYPE_MOUSEMOVE: + case PP_INPUTEVENT_TYPE_MOUSEDOWN: { + pp::MouseInputEvent mouse = pp::MouseInputEvent(event); + if (mouse.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { + PP_Point delta = mouse.GetMovement(); + float delta_x = static_cast<float>(delta.x); + float delta_y = static_cast<float>(delta.y); + float spin_x = std::min(4.0f, std::max(-4.0f, delta_x * 0.5f)); + float spin_y = std::min(4.0f, std::max(-4.0f, delta_y * 0.5f)); + ui_spin_x_ = spin_x / 100.0f; + ui_spin_y_ = spin_y / 100.0f; + } + break; + } + case PP_INPUTEVENT_TYPE_WHEEL: { + pp::WheelInputEvent wheel = pp::WheelInputEvent(event); + PP_FloatPoint ticks = wheel.GetTicks(); + SetZoom(ui_zoom_ + (ticks.x + ticks.y) * kWheelSpeed); + // Update html slider by sending update message to JS. + PostUpdateMessage("set_zoom", ui_zoom_); + break; + } + default: + break; } - case PP_INPUTEVENT_TYPE_WHEEL: { - pp::WheelInputEvent wheel = pp::WheelInputEvent(event); - PP_FloatPoint ticks = wheel.GetTicks(); - SetZoom(ui_zoom_ + (ticks.x + ticks.y) * kWheelSpeed); - // Update html slider by sending update message to JS. - PostUpdateMessage("set_zoom", ui_zoom_); - break; + } else if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) { + // Convert Pepper Simple message to PPAPI C++ vars + pp::Var var(ps_event->as_var); + if (var.is_dictionary()) { + pp::VarDictionary dictionary(var); + std::string message = dictionary.Get("message").AsString(); + if (message == "run benchmark" && !benchmarking_) { + StartBenchmark(); + } else if (message == "set_light") { + SetLight(static_cast<float>(dictionary.Get("value").AsDouble())); + } else if (message == "set_zoom") { + SetZoom(static_cast<float>(dictionary.Get("value").AsDouble())); + } else if (message == "set_threads") { + int threads = dictionary.Get("value").AsInt(); + delete workers_; + workers_ = new ThreadPool(threads); + } else if (message == "texture") { + std::string name = dictionary.Get("name").AsString(); + int width = dictionary.Get("width").AsInt(); + int height = dictionary.Get("height").AsInt(); + pp::VarArrayBuffer array_buffer(dictionary.Get("data")); + if (!name.empty() && !array_buffer.is_null()) { + if (width > 0 && height > 0) { + uint32_t* pixels = static_cast<uint32_t*>(array_buffer.Map()); + SetTexture(name, width, height, pixels); + array_buffer.Unmap(); + } + } + } + } else { + printf("Handle message unknown type: %s\n", var.DebugString().c_str()); } - default: - return false; } - return true; } // PostUpdateMessage() helper function for sending small messages to JS. @@ -767,53 +746,17 @@ void Planet::PostUpdateMessage(const char* message_name, double value) { pp::VarDictionary message; message.Set("message", message_name); message.Set("value", value); - PostMessage(message); -} - -// Handle message sent from Javascript. -void Planet::HandleMessage(const pp::Var& var) { - if (var.is_dictionary()) { - pp::VarDictionary dictionary(var); - std::string message = dictionary.Get("message").AsString(); - if (message == "run benchmark" && !benchmarking_) { - StartBenchmark(); - } else if (message == "set_light") { - SetLight(static_cast<float>(dictionary.Get("value").AsDouble())); - } else if (message == "set_zoom") { - SetZoom(static_cast<float>(dictionary.Get("value").AsDouble())); - } else if (message == "set_threads") { - int threads = dictionary.Get("value").AsInt(); - delete workers_; - workers_ = new ThreadPool(threads); - } else if (message == "texture") { - std::string name = dictionary.Get("name").AsString(); - int width = dictionary.Get("width").AsInt(); - int height = dictionary.Get("height").AsInt(); - pp::VarArrayBuffer array_buffer(dictionary.Get("data")); - if (!name.empty() && width > 0 && height > 0 && !array_buffer.is_null()) { - uint32_t* pixels = static_cast<uint32_t*>(array_buffer.Map()); - SetTexture(name, width, height, pixels); - array_buffer.Unmap(); - } - } - } else { - printf("Handle message unknown type: %s\n", var.DebugString().c_str()); - } -} - -void Planet::FlushCallback(void* thiz, int32_t result) { - static_cast<Planet*>(thiz)->Update(); -} - -// Update the 2d region and flush to make it visible on the page. -void Planet::FlushPixelBuffer() { - graphics_2d_context_->PaintImageData(*image_data_, pp::Point(0, 0)); - graphics_2d_context_->Flush(pp::CompletionCallback(&FlushCallback, this)); + PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var()); } void Planet::Update() { - // Don't call FlushPixelBuffer() when benchmarking - vsync is enabled by - // default, and will throttle the benchmark results. + // When benchmarking is running, don't update display via + // PSContext2DSwapBuffer() - vsync is enabled by default, and will throttle + // the benchmark results. + PSContext2DGetBuffer(ps_context_); + if (NULL == ps_context_->data) + return; + do { UpdateSim(); Render(); @@ -823,51 +766,28 @@ void Planet::Update() { if (benchmarking_) EndBenchmark(); - FlushPixelBuffer(); -} - -void Planet::CreateContext(const pp::Size& size) { - graphics_2d_context_ = new pp::Graphics2D(this, size, false); - if (graphics_2d_context_->is_null()) - printf("Failed to create a 2D resource!\n"); - if (!BindGraphics(*graphics_2d_context_)) - printf("Couldn't bind the device context\n"); - image_data_ = new pp::ImageData(this, - PP_IMAGEDATAFORMAT_BGRA_PREMUL, - size, - false); - width_ = image_data_->size().width(); - height_ = image_data_->size().height(); - stride_in_pixels_ = static_cast<uint32_t>(image_data_->stride() / 4); - pixel_buffer_ = static_cast<uint32_t*>(image_data_->data()); - num_regions_ = height_; -} - -void Planet::DestroyContext() { - delete graphics_2d_context_; - delete image_data_; - graphics_2d_context_ = NULL; - image_data_ = NULL; - width_ = 0; - height_ = 0; - stride_in_pixels_ = 0; - pixel_buffer_ = NULL; -} - -class PlanetModule : public pp::Module { - public: - PlanetModule() : pp::Module() {} - virtual ~PlanetModule() {} + PSContext2DSwapBuffer(ps_context_); +} + - // Create and return a Planet instance. - virtual pp::Instance* CreateInstance(PP_Instance instance) { - return new Planet(instance); +// Starting point for the module. We do not use main since it would +// collide with main in libppapi_cpp. +int example_main(int argc, char* argv[]) { + Planet earth; + while (true) { + PSEvent* ps_event; + // Consume all available events + while ((ps_event = PSEventTryAcquire()) != NULL) { + earth.HandleEvent(ps_event); + PSEventRelease(ps_event); + } + // Do simulation, render and present. + earth.Update(); } -}; -namespace pp { -Module* CreateModule() { - return new PlanetModule(); + return 0; } -} // namespace pp +// Register the function to call once the Instance Object is initialized. +// see: pappi_simple/ps_main.h +PPAPI_SIMPLE_REGISTER_MAIN(example_main); diff --git a/native_client_sdk/src/examples/demo/earth/example.dsc b/native_client_sdk/src/examples/demo/earth/example.dsc index 21eb60c354..4d4829c93d 100644 --- a/native_client_sdk/src/examples/demo/earth/example.dsc +++ b/native_client_sdk/src/examples/demo/earth/example.dsc @@ -7,7 +7,7 @@ 'SOURCES' : [ 'earth.cc' ], - 'LIBS': ['sdk_util', 'ppapi_cpp', 'ppapi', 'pthread'] + 'LIBS': ['ppapi_simple', 'nacl_io', 'sdk_util', 'ppapi_cpp', 'ppapi', 'pthread'] } ], 'DATA': [ diff --git a/native_client_sdk/src/examples/demo/voronoi/example.dsc b/native_client_sdk/src/examples/demo/voronoi/example.dsc index d9fabcebc9..1d766d6b4d 100644 --- a/native_client_sdk/src/examples/demo/voronoi/example.dsc +++ b/native_client_sdk/src/examples/demo/voronoi/example.dsc @@ -8,7 +8,7 @@ 'voronoi.cc' ], - 'LIBS': ['sdk_util', 'ppapi_cpp', 'ppapi', 'pthread'] + 'LIBS': ['ppapi_simple', 'nacl_io', 'sdk_util', 'ppapi_cpp', 'ppapi', 'pthread'] } ], 'DATA': [ diff --git a/native_client_sdk/src/examples/demo/voronoi/voronoi.cc b/native_client_sdk/src/examples/demo/voronoi/voronoi.cc index d85d185e04..7cadb1a1b5 100644 --- a/native_client_sdk/src/examples/demo/voronoi/voronoi.cc +++ b/native_client_sdk/src/examples/demo/voronoi/voronoi.cc @@ -4,15 +4,11 @@ #include <assert.h> #include <math.h> -#include <ppapi/cpp/completion_callback.h> -#include <ppapi/cpp/graphics_2d.h> -#include <ppapi/cpp/image_data.h> +#include <ppapi/c/ppb_input_event.h> #include <ppapi/cpp/input_event.h> -#include <ppapi/cpp/instance.h> -#include <ppapi/cpp/module.h> -#include <ppapi/cpp/rect.h> -#include <ppapi/cpp/size.h> #include <ppapi/cpp/var.h> +#include <ppapi/cpp/var_array.h> +#include <ppapi/cpp/var_array_buffer.h> #include <ppapi/cpp/var_dictionary.h> #include <pthread.h> #include <stdio.h> @@ -24,6 +20,11 @@ #include <algorithm> #include <string> +#include "ppapi_simple/ps.h" +#include "ppapi_simple/ps_context_2d.h" +#include "ppapi_simple/ps_event.h" +#include "ppapi_simple/ps_interface.h" +#include "ppapi_simple/ps_main.h" #include "sdk_util/thread_pool.h" using namespace sdk_util; // For sdk_util::ThreadPool @@ -39,6 +40,7 @@ const int kFramesToBenchmark = 100; const unsigned int kRandomStartSeed = 0xC0DE533D; const int kMaxPointCount = 1024; const int kStartPointCount = 256; +const int kDefaultNumRegions = 256; unsigned int g_rand_state = kRandomStartSeed; @@ -91,25 +93,18 @@ struct Vec2 { }; // The main object that runs Voronoi simulation. -class Voronoi : public pp::Instance { +class Voronoi { public: - explicit Voronoi(PP_Instance instance); + Voronoi(); virtual ~Voronoi(); - - virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { - return true; - } - - virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip); - - // Catch events. - virtual bool HandleInputEvent(const pp::InputEvent& event); - - // Catch messages posted from Javascript. - virtual void HandleMessage(const pp::Var& message); + // Runs a tick of the simulations, update 2D output. + void Update(); + // Handle event from user, or message from JS. + void HandleEvent(PSEvent* ps_event); private: // Methods prefixed with 'w' are run on worker threads. + uint32_t* wGetAddr(int x, int y); int wCell(float x, float y); inline void wFillSpan(uint32_t *pixels, uint32_t color, int width); void wRenderTile(int x, int y, int w, int h); @@ -131,39 +126,21 @@ class Voronoi : public pp::Instance { void Draw(); void StartBenchmark(); void EndBenchmark(); - - // Runs a tick of the simulations, updating all buffers. Flushes the - // contents of |image_data_| to the 2D graphics context. - void Update(); - // Helper to post small update messages to JS. void PostUpdateMessage(const char* message_name, double value); - // Create and initialize the 2D context used for drawing. - void CreateContext(const pp::Size& size); - // Destroy the 2D drawing context. - void DestroyContext(); - // Push the pixels to the browser, then attempt to flush the 2D context. - void FlushPixelBuffer(); - static void FlushCallback(void* data, int32_t result); - - pp::Graphics2D* graphics_2d_context_; - pp::ImageData* image_data_; + PSContext2D_t* ps_context_; Vec2 positions_[kMaxPointCount]; Vec2 screen_positions_[kMaxPointCount]; Vec2 velocities_[kMaxPointCount]; uint32_t colors_[kMaxPointCount]; float ang_; - int point_count_; - int num_threads_; const int num_regions_; + int num_threads_; + int point_count_; bool draw_points_; bool draw_interiors_; ThreadPool* workers_; - int width_; - int height_; - uint32_t stride_in_pixels_; - uint32_t* pixel_buffer_; int benchmark_frame_counter_; bool benchmarking_; double benchmark_start_time_; @@ -171,7 +148,6 @@ class Voronoi : public pp::Instance { }; - void Voronoi::Reset() { rand_reset(kRandomStartSeed); ang_ = 0.0f; @@ -190,34 +166,23 @@ void Voronoi::Reset() { } } -Voronoi::Voronoi(PP_Instance instance) : pp::Instance(instance), - graphics_2d_context_(NULL), - image_data_(NULL), - num_regions_(256) { - draw_points_ = true; - draw_interiors_ = true; - width_ = 0; - height_ = 0; - stride_in_pixels_ = 0; - pixel_buffer_ = NULL; - benchmark_frame_counter_ = 0; - benchmarking_ = false; - - point_count_ = kStartPointCount; +Voronoi::Voronoi() : num_regions_(kDefaultNumRegions), num_threads_(0), + point_count_(kStartPointCount), draw_points_(true), draw_interiors_(true), + benchmark_frame_counter_(0), benchmarking_(false) { Reset(); - // By default, render from the dispatch thread. - num_threads_ = 0; workers_ = new ThreadPool(num_threads_); - - // Request PPAPI input events for mouse & keyboard. - RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE); - RequestInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); + PSEventSetFilter(PSE_ALL); + ps_context_ = PSContext2DAllocate(); } Voronoi::~Voronoi() { delete workers_; - DestroyContext(); + PSContext2DFree(ps_context_); +} + +inline uint32_t* Voronoi::wGetAddr(int x, int y) { + return ps_context_->data + x + y * ps_context_->stride / sizeof(uint32_t); } // This is the core of the Voronoi calculation. At a given point on the @@ -249,8 +214,8 @@ int Voronoi::wCell(float x, float y) { void Voronoi::wMakeRect(int r, int* x, int* y, int* w, int* h) { const int parts = 16; assert(parts * parts == num_regions_); - *w = width_ / parts; - *h = height_ / parts; + *w = ps_context_->width / parts; + *h = ps_context_->height / parts; *x = *w * (r % parts); *y = *h * ((r / parts) % parts); } @@ -293,11 +258,11 @@ inline void Voronoi::wFillSpan(uint32_t* pixels, uint32_t color, int width) { // the width w parameter is evenly divisible by 4. // If multithreading, this function is only called by the worker threads. void Voronoi::wFillRect(int x, int y, int w, int h, uint32_t color) { - const uint32_t pitch = width_; - uint32_t* pixels = pixel_buffer_ + y * pitch + x; + const uint32_t stride_in_pixels = ps_context_->stride / sizeof(uint32_t); + uint32_t* pixels = wGetAddr(x, y); for (int j = 0; j < h; j++) { wFillSpan(pixels, color, w); - pixels += pitch; + pixels += stride_in_pixels; } } @@ -309,7 +274,8 @@ void Voronoi::wFillRect(int x, int y, int w, int h, uint32_t color) { // voronoi membership per pixel. void Voronoi::wRenderTile(int x, int y, int w, int h) { // rip through a tile - uint32_t* pixels = pixel_buffer_ + y * stride_in_pixels_ + x; + const uint32_t stride_in_pixels = ps_context_->stride / sizeof(uint32_t); + uint32_t* pixels = wGetAddr(x, y); for (int j = 0; j < h; j++) { // get start and end cell values int ms = wCell(x + 0, y + j); @@ -327,7 +293,7 @@ void Voronoi::wRenderTile(int x, int y, int w, int h) { } *p++ = colors_[me]; } - pixels += stride_in_pixels_; + pixels += stride_in_pixels; } } @@ -402,8 +368,8 @@ void Voronoi::UpdateSim() { for (int j = 0; j < kMaxPointCount; j++) { positions_[j].x += (velocities_[j].x) * z; positions_[j].y += (velocities_[j].y) * z; - screen_positions_[j].x = positions_[j].x * width_; - screen_positions_[j].y = positions_[j].y * height_; + screen_positions_[j].x = positions_[j].x * ps_context_->width; + screen_positions_[j].y = positions_[j].y * ps_context_->height; } } @@ -411,18 +377,19 @@ void Voronoi::UpdateSim() { void Voronoi::RenderDot(float x, float y, uint32_t color1, uint32_t color2) { const int ix = static_cast<int>(x); const int iy = static_cast<int>(y); + const uint32_t stride_in_pixels = ps_context_->stride / sizeof(uint32_t); // clip it against window if (ix < 1) return; - if (ix >= (width_ - 1)) return; + if (ix >= (ps_context_->width - 1)) return; if (iy < 1) return; - if (iy >= (height_ - 1)) return; - uint32_t* pixel = pixel_buffer_ + iy * stride_in_pixels_ + ix; + if (iy >= (ps_context_->height - 1)) return; + uint32_t* pixel = wGetAddr(ix, iy); // render dot as a small diamond *pixel = color1; *(pixel - 1) = color2; *(pixel + 1) = color2; - *(pixel - stride_in_pixels_) = color2; - *(pixel + stride_in_pixels_) = color2; + *(pixel - stride_in_pixels) = color2; + *(pixel + stride_in_pixels) = color2; } // Superimposes dots on the positions. @@ -436,27 +403,12 @@ void Voronoi::SuperimposePositions() { } // Renders the Voronoi diagram, dispatching the work to multiple threads. -// Note: This Dispatch() is from the main PPAPI thread, so care must be taken -// not to attempt PPAPI calls from the worker threads, since Dispatch() will -// block here until all work is complete. The worker threads are compute only -// and do not make any PPAPI calls. void Voronoi::Render() { workers_->Dispatch(num_regions_, wRenderRegionEntry, this); if (draw_points_) SuperimposePositions(); } -void Voronoi::DidChangeView(const pp::Rect& position, const pp::Rect& clip) { - if (position.size().width() == width_ && - position.size().height() == height_) - return; // Size didn't change, no need to update anything. - - // Create a new device context with the new size. - DestroyContext(); - CreateContext(position.size()); - Update(); -} - void Voronoi::StartBenchmark() { Reset(); printf("Benchmark started...\n"); @@ -475,41 +427,46 @@ void Voronoi::EndBenchmark() { PostUpdateMessage("benchmark_result", result); } -// Handle input events from the user. -bool Voronoi::HandleInputEvent(const pp::InputEvent& event) { - switch (event.GetType()) { - case PP_INPUTEVENT_TYPE_KEYDOWN: { - pp::KeyboardInputEvent key = pp::KeyboardInputEvent(event); - uint32_t key_code = key.GetKeyCode(); - if (key_code == 84) // 't' key - if (!benchmarking_) - StartBenchmark(); - break; +// Handle input events from the user and messages from JS. +void Voronoi::HandleEvent(PSEvent* ps_event) { + // Give the 2D context a chance to process the event. + if (0 != PSContext2DHandleEvent(ps_context_, ps_event)) + return; + if (ps_event->type == PSE_INSTANCE_HANDLEINPUT) { + // Convert Pepper Simple event to a PPAPI C++ event + pp::InputEvent event(ps_event->as_resource); + switch (event.GetType()) { + case PP_INPUTEVENT_TYPE_KEYDOWN: { + pp::KeyboardInputEvent key = pp::KeyboardInputEvent(event); + uint32_t key_code = key.GetKeyCode(); + if (key_code == 84) // 't' key + if (!benchmarking_) + StartBenchmark(); + break; + } + default: + break; } - default: - return false; - } - return true; -} - -// Handle messages sent from Javascript. -void Voronoi::HandleMessage(const pp::Var& var) { - if (var.is_dictionary()) { - pp::VarDictionary dictionary(var); - std::string message = dictionary.Get("message").AsString(); - if (message == "run_benchmark" && !benchmarking_) - StartBenchmark(); - else if (message == "draw_points") - draw_points_ = dictionary.Get("value").AsBool(); - else if (message == "draw_interiors") - draw_interiors_ = dictionary.Get("value").AsBool(); - else if (message == "set_points") { - int num_points = dictionary.Get("value").AsInt(); - point_count_ = std::min(kMaxPointCount, std::max(0, num_points)); - } else if (message == "set_threads") { - int thread_count = dictionary.Get("value").AsInt(); - delete workers_; - workers_ = new ThreadPool(thread_count); + } else if (ps_event->type == PSE_INSTANCE_HANDLEMESSAGE) { + // Convert Pepper Simple message to PPAPI C++ var + pp::Var var(ps_event->as_var); + if (var.is_dictionary()) { + pp::VarDictionary dictionary(var); + std::string message = dictionary.Get("message").AsString(); + if (message == "run_benchmark" && !benchmarking_) + StartBenchmark(); + else if (message == "draw_points") + draw_points_ = dictionary.Get("value").AsBool(); + else if (message == "draw_interiors") + draw_interiors_ = dictionary.Get("value").AsBool(); + else if (message == "set_points") { + int num_points = dictionary.Get("value").AsInt(); + point_count_ = std::min(kMaxPointCount, std::max(0, num_points)); + } else if (message == "set_threads") { + int thread_count = dictionary.Get("value").AsInt(); + delete workers_; + workers_ = new ThreadPool(thread_count); + } } } } @@ -519,22 +476,19 @@ void Voronoi::PostUpdateMessage(const char* message_name, double value) { pp::VarDictionary message; message.Set("message", message_name); message.Set("value", value); - PostMessage(message); -} - -void Voronoi::FlushCallback(void* thiz, int32_t result) { - static_cast<Voronoi*>(thiz)->Update(); -} - -// Update the 2d region and flush to make it visible on the page. -void Voronoi::FlushPixelBuffer() { - graphics_2d_context_->PaintImageData(*image_data_, pp::Point(0, 0)); - graphics_2d_context_->Flush(pp::CompletionCallback(&FlushCallback, this)); + PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var()); } void Voronoi::Update() { - // Don't call FlushPixelBuffer() when benchmarking - vsync is enabled by - // default, and will throttle the benchmark results. + PSContext2DGetBuffer(ps_context_); + if (NULL == ps_context_->data) + return; + assert(is_pow2(ps_context_->width)); + assert(is_pow2(ps_context_->height)); + + // When benchmarking is running, don't update display via + // PSContext2DSwapBuffer() - vsync is enabled by default, and will throttle + // the benchmark results. do { UpdateSim(); Render(); @@ -543,50 +497,28 @@ void Voronoi::Update() { } while (benchmark_frame_counter_ > 0); if (benchmarking_) EndBenchmark(); - FlushPixelBuffer(); -} - -void Voronoi::CreateContext(const pp::Size& size) { - graphics_2d_context_ = new pp::Graphics2D(this, size, false); - if (!BindGraphics(*graphics_2d_context_)) - printf("Couldn't bind the device context\n"); - image_data_ = new pp::ImageData(this, - PP_IMAGEDATAFORMAT_BGRA_PREMUL, - size, - false); - width_ = image_data_->size().width(); - height_ = image_data_->size().height(); - // This demo requires power of two width & height buffers. - assert(is_pow2(width_) && is_pow2(height_)); - stride_in_pixels_ = static_cast<uint32_t>(image_data_->stride() / 4); - pixel_buffer_ = static_cast<uint32_t*>(image_data_->data()); -} -void Voronoi::DestroyContext() { - delete graphics_2d_context_; - delete image_data_; - graphics_2d_context_ = NULL; - image_data_ = NULL; - width_ = 0; - height_ = 0; - stride_in_pixels_ = 0; - pixel_buffer_ = NULL; + PSContext2DSwapBuffer(ps_context_); } -class VoronoiModule : public pp::Module { - public: - VoronoiModule() : pp::Module() {} - virtual ~VoronoiModule() {} - - // Create and return a Voronoi instance. - virtual pp::Instance* CreateInstance(PP_Instance instance) { - return new Voronoi(instance); +// Starting point for the module. We do not use main since it would +// collide with main in libppapi_cpp. +int example_main(int argc, char* argv[]) { + Voronoi voronoi; + while (true) { + PSEvent* ps_event; + // Consume all available events. + while ((ps_event = PSEventTryAcquire()) != NULL) { + voronoi.HandleEvent(ps_event); + PSEventRelease(ps_event); + } + // Do simulation, render and present. + voronoi.Update(); } -}; -namespace pp { -Module* CreateModule() { - return new VoronoiModule(); + return 0; } -} // namespace pp +// Register the function to call once the Instance Object is initialized. +// see: pappi_simple/ps_main.h +PPAPI_SIMPLE_REGISTER_MAIN(example_main); diff --git a/native_client_sdk/src/libraries/nacl_io/event_emitter.h b/native_client_sdk/src/libraries/nacl_io/event_emitter.h index 4cca72fe26..96670b6c36 100644 --- a/native_client_sdk/src/libraries/nacl_io/event_emitter.h +++ b/native_client_sdk/src/libraries/nacl_io/event_emitter.h @@ -29,10 +29,10 @@ struct EventInfo : public sdk_util::RefObject { // User provied data to be returned on EventListener::Wait uint64_t user_data; - // Bitfield of enum KernelEventType currently signaled. + // Bitfield of POLL events currently signaled. uint32_t events; - // Bitfield of enum KernelEventType that can signal. + // Bitfield of POLL events of interest. uint32_t filter; // We do not use a ScopedRef to prevent circular references. @@ -71,7 +71,7 @@ class EventEmitter : public sdk_util::RefObject { void UnregisterEventInfo(const ScopedEventInfo& info); public: - // Returns the current state of the emitter as KernelEventType bitfield. + // Returns the current state of the emitter as POLL events bitfield. virtual uint32_t GetEventStatus() = 0; // Returns the type of the emitter (compatible with st_mode in stat) diff --git a/native_client_sdk/src/libraries/nacl_io/event_listener.cc b/native_client_sdk/src/libraries/nacl_io/event_listener.cc index 5eaa0603fc..d8b293da47 100644 --- a/native_client_sdk/src/libraries/nacl_io/event_listener.cc +++ b/native_client_sdk/src/libraries/nacl_io/event_listener.cc @@ -4,6 +4,7 @@ */ #include <errno.h> +#include <poll.h> #include <pthread.h> #include <stdio.h> @@ -44,7 +45,7 @@ void EventListener::Destroy() { uint32_t EventListener::GetEventStatus() { // Always writable, but we can only assume it to be readable if there // is an event waiting. - return signaled_.empty() ? KE_WRITE_READY : KE_WRITE_READY | KE_READ_READY; + return signaled_.empty() ? POLLOUT : POLLIN | POLLOUT; } int EventListener::GetType() { @@ -235,8 +236,8 @@ void EventListener::AbandonedEventInfo(const ScopedEventInfo& event) { } // EventInfos abandoned by the destroyed emitter must still be kept in - // signaled_ set for KE_SHUTDOWN. - event->events = KE_SHUTDOWN; + // signaled_ set for POLLHUP. + event->events = POLLHUP; Signal(event); } diff --git a/native_client_sdk/src/libraries/nacl_io/event_listener.h b/native_client_sdk/src/libraries/nacl_io/event_listener.h index bf2e30761c..cb93f116b2 100644 --- a/native_client_sdk/src/libraries/nacl_io/event_listener.h +++ b/native_client_sdk/src/libraries/nacl_io/event_listener.h @@ -64,14 +64,8 @@ namespace nacl_io { -enum KernelEventType { - KE_READ_READY = 1, - KE_WRITE_READY = 2, - KE_SHUTDOWN = 4 -}; - struct EventData { - // Bit Mask of signaled KernelEvents + // Bit Mask of signaled POLL events. uint32_t events; uint64_t user_data; }; @@ -105,7 +99,7 @@ class EventListener : public EventEmitter { // The number of events recored is returned in |count|. Error Wait(EventData* events, int max, int ms_timeout, int* out_count); - // Tracks a new set of KernelEventTypes for a given unique |id|. The + // Tracks a new set of POLL events for a given unique |id|. The // |user_data| will be returned in the Wait when an event of type |filter| // is received with that |id|. Error Track(int id, diff --git a/native_client_sdk/src/libraries/nacl_io/include/sys/utsname.h b/native_client_sdk/src/libraries/nacl_io/include/sys/utsname.h new file mode 100644 index 0000000000..2e3cd3daa2 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/include/sys/utsname.h @@ -0,0 +1,20 @@ +/* Copyright 2013 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 LIBRARIES_NACL_SYS_UTSNAME_H_ +#define LIBRARIES_NACL_SYS_UTSNAME_H_ + +#define _UTSNAME_LENGTH 65 + +struct utsname { + char sysname[_UTSNAME_LENGTH]; + char nodename[_UTSNAME_LENGTH]; + char release[_UTSNAME_LENGTH]; + char version[_UTSNAME_LENGTH]; + char machine[_UTSNAME_LENGTH]; +}; + +int uname(struct utsname *buf); + +#endif diff --git a/native_client_sdk/src/libraries/nacl_io/inet_ntoa.cc b/native_client_sdk/src/libraries/nacl_io/inet_ntoa.cc new file mode 100644 index 0000000000..fdeae089b7 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/inet_ntoa.cc @@ -0,0 +1,65 @@ +// Copyright 2013 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 "nacl_io/ossocket.h" + +#ifdef PROVIDES_SOCKET_API + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include <iostream> +#include <sstream> +#include <string> + +static inline uint8_t get_byte(const void* addr, int byte) { + const char* buf = static_cast<const char*>(addr); + return static_cast<uint8_t>(buf[byte]); +} + +char* inet_ntoa(struct in_addr in) { + static char addr[INET_ADDRSTRLEN]; + snprintf(addr, INET_ADDRSTRLEN, "%u.%u.%u.%u", + get_byte(&in, 0), get_byte(&in, 1), + get_byte(&in, 2), get_byte(&in, 3)); + return addr; +} + +const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) { + if (AF_INET == af) { + if (size < INET_ADDRSTRLEN) { + errno = ENOSPC; + return NULL; + } + struct in_addr in; + memcpy(&in, src, sizeof(in)); + char* result = inet_ntoa(in); + memcpy(dst, result, strlen(result) + 1); + return dst; + } + + if (AF_INET6 == af) { + if (size < INET6_ADDRSTRLEN) { + errno = ENOSPC; + return NULL; + } + const uint8_t* tuples = static_cast<const uint8_t*>(src); + std::stringstream output; + for (int i = 0; i < 8; i++) { + uint16_t tuple = (tuples[2*i] << 8) + tuples[2*i+1]; + output << std::hex << tuple; + if (i < 7) { + output << ":"; + } + } + memcpy(dst, output.str().c_str(), output.str().size() + 1); + return dst; + } + + errno = EAFNOSUPPORT; + return NULL; +} + +#endif // PROVIDES_SOCKET_API diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc index 4aaa019db8..a479f9ca94 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc @@ -233,6 +233,15 @@ int ki_utime(const char* filename, const struct utimbuf* times) { return s_kp->utime(filename, times); } +int ki_poll(struct pollfd *fds, nfds_t nfds, int timeout) { + return s_kp->poll(fds, nfds, timeout); +} + +int ki_select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, struct timeval* timeout) { + return s_kp->select(nfds, readfds, writefds, exceptfds, timeout); +} + #ifdef PROVIDES_SOCKET_API // Socket Functions int ki_accept(int fd, struct sockaddr* addr, socklen_t* len) { diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h index 2f998b6355..90fecf962f 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h @@ -66,6 +66,10 @@ int ki_fchown(int fd, uid_t owner, gid_t group); int ki_lchown(const char* path, uid_t owner, gid_t group); int ki_utime(const char* filename, const struct utimbuf* times); +int ki_poll(struct pollfd *fds, nfds_t nfds, int timeout); +int ki_select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, struct timeval* timeout); + #ifdef PROVIDES_SOCKET_API // Socket Functions int ki_accept(int fd, struct sockaddr* addr, socklen_t* len); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc index d5f44b595c..1e6ae64734 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc @@ -4,12 +4,17 @@ #include "nacl_io/kernel_proxy.h" + #include <assert.h> #include <errno.h> #include <fcntl.h> +#include <limits.h> +#include <poll.h> #include <pthread.h> #include <stdio.h> #include <string.h> +#include <sys/time.h> + #include <iterator> #include <string> @@ -644,6 +649,182 @@ int KernelProxy::munmap(void* addr, size_t length) { #ifdef PROVIDES_SOCKET_API +int KernelProxy::select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, struct timeval* timeout) { + ScopedEventListener listener(new EventListener); + std::vector<struct pollfd> fds; + + fd_set readout, writeout, exceptout; + + FD_ZERO(&readout); + FD_ZERO(&writeout); + FD_ZERO(&exceptout); + + int fd; + size_t event_cnt = 0; + int event_track = 0; + for (fd = 0; fd < nfds; fd++) { + int events = 0; + + if (readfds != NULL && FD_ISSET(fd, readfds)) + events |= POLLIN; + + if (writefds != NULL && FD_ISSET(fd, writefds)) + events |= POLLOUT; + + if (exceptfds != NULL && FD_ISSET(fd, exceptfds)) + events |= POLLERR | POLLHUP; + + // If we are not interested in this FD, skip it + if (0 == events) continue; + + ScopedKernelHandle handle; + Error err = AcquireHandle(fd, &handle); + + // Select will return immediately if there are bad FDs. + if (err != 0) { + errno = EBADF; + return -1; + } + + int status = handle->node()->GetEventStatus() & events; + if (status & POLLIN) { + FD_SET(fd, &readout); + event_cnt++; + } + + if (status & POLLOUT) { + FD_SET(fd, &writeout); + event_cnt++; + } + + if (status & (POLLERR | POLLHUP)) { + FD_SET(fd, &exceptout); + event_cnt++; + } + + // Otherwise track it. + if (0 == status) { + err = listener->Track(fd, handle->node(), events, fd); + if (err != 0) { + errno = EBADF; + return -1; + } + event_track++; + } + } + + // If nothing is signaled, then we must wait. + if (event_cnt == 0) { + std::vector<EventData> events; + int ready_cnt; + int ms_timeout; + + // NULL timeout signals wait forever. + if (timeout == NULL) { + ms_timeout = -1; + } else { + int64_t ms = timeout->tv_sec * 1000 + ((timeout->tv_usec + 500) / 1000); + + // If the timeout is invalid or too long (larger than signed 32 bit). + if ((timeout->tv_sec < 0) || (timeout->tv_sec >= (INT_MAX / 1000)) || + (timeout->tv_usec < 0) || (timeout->tv_usec >= 1000) || + (ms < 0) || (ms >= INT_MAX)) { + errno = EINVAL; + return -1; + } + + ms_timeout = static_cast<int>(ms); + } + + events.resize(event_track); + listener->Wait(events.data(), event_track, ms_timeout, &ready_cnt); + for (fd = 0; static_cast<int>(fd) < ready_cnt; fd++) { + if (events[fd].events & POLLIN) { + FD_SET(events[fd].user_data, &readout); + event_cnt++; + } + + if (events[fd].events & POLLOUT) { + FD_SET(events[fd].user_data, &writeout); + event_cnt++; + } + + if (events[fd].events & (POLLERR | POLLHUP)) { + FD_SET(events[fd].user_data, &exceptout); + event_cnt++; + } + } + } + + // Copy out the results + if (readfds != NULL) + *readfds = readout; + + if (writefds != NULL) + *writefds = writeout; + + if (exceptfds != NULL) + *exceptfds = exceptout; + + return event_cnt; +} + +int KernelProxy::poll(struct pollfd *fds, nfds_t nfds, int timeout) { + ScopedEventListener listener(new EventListener); + + int index; + size_t event_cnt = 0; + size_t event_track = 0; + for (index = 0; static_cast<nfds_t>(index) < nfds; index++) { + ScopedKernelHandle handle; + struct pollfd* info = &fds[index]; + Error err = AcquireHandle(info->fd, &handle); + + // If the node isn't open, or somehow invalid, mark it so. + if (err != 0) { + info->revents = POLLNVAL; + event_cnt++; + continue; + } + + // If it's already signaled, then just capture the event + if (handle->node()->GetEventStatus() & info->events) { + info->revents = info->events & handle->node()->GetEventStatus(); + event_cnt++; + continue; + } + + // Otherwise try to track it. + err = listener->Track(info->fd, handle->node(), info->events, index); + if (err != 0) { + info->revents = POLLNVAL; + event_cnt++; + continue; + } + event_track++; + } + + // If nothing is signaled, then we must wait. + if (0 == event_cnt) { + std::vector<EventData> events; + int ready_cnt; + + events.resize(event_track); + listener->Wait(events.data(), event_track, timeout, &ready_cnt); + for (index = 0; index < ready_cnt; index++) { + struct pollfd* info = &fds[events[index].user_data]; + + info->revents = events[index].events; + event_cnt++; + } + } + + return event_cnt; +} + + + // Socket Functions int KernelProxy::accept(int fd, struct sockaddr* addr, socklen_t* len) { if (NULL == addr || NULL == len) { diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h index cd118ade72..e0dff69244 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h @@ -14,6 +14,8 @@ #include "nacl_io/ostypes.h" #include "nacl_io/osutime.h" +struct timeval; + namespace nacl_io { class PepperInterface; @@ -118,6 +120,11 @@ class KernelProxy : protected KernelObject { virtual int munmap(void* addr, size_t length); #ifdef PROVIDES_SOCKET_API + virtual int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, struct timeval* timeout); + + virtual int poll(struct pollfd *fds, nfds_t nfds, int timeout); + // Socket support functions virtual int accept(int fd, struct sockaddr* addr, socklen_t* len); virtual int bind(int fd, const struct sockaddr* addr, socklen_t len); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc index 9bbcd1db95..b4e80e9093 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc @@ -126,10 +126,12 @@ EXTERN_C_BEGIN OP(getdents); \ OP(mkdir); \ OP(open); \ + OP(poll);\ OP(read); \ OP(rmdir); \ OP(seek); \ OP(stat); \ + OP(select); \ OP(write); \ OP(mmap); \ OP(munmap); \ @@ -228,6 +230,12 @@ int WRAP(open_resource)(const char* file, int* fd) { return (*fd < 0) ? errno : 0; } +int WRAP(poll)(struct pollfd *fds, nfds_t nfds, int timeout, int* count) { + *count = ki_poll(fds, nfds, timeout); + return (*count < 0) ? errno : 0; + +} + int WRAP(read)(int fd, void *buf, size_t count, size_t *nread) { if (!ki_is_initialized()) return REAL(read)(fd, buf, count, nread); @@ -246,6 +254,12 @@ int WRAP(seek)(int fd, off_t offset, int whence, off_t* new_offset) { return (*new_offset < 0) ? errno : 0; } +int WRAP(select)(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, struct timeval* timeout, int* count) { + *count = ki_select(nfds, readfds, writefds, exceptfds, timeout); + return (*count < 0) ? errno : 0; +} + int WRAP(stat)(const char *pathname, struct nacl_abi_stat *nacl_buf) { struct stat buf; memset(&buf, 0, sizeof(struct stat)); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc index 1b6f42cef0..c2f91a1a5d 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc @@ -118,11 +118,11 @@ int WRAP(seek)(int fd, off_t offset, int whence, off_t* new_offset) { return (*new_offset < 0) ? errno : 0; } -int WRAP(stat)(const char *pathname, struct stat *buf) { +int WRAP(stat)(const char* pathname, struct stat* buf) { return (ki_stat(pathname, buf) < 0) ? errno : 0; } -int WRAP(write)(int fd, const void *buf, size_t count, size_t *nwrote) { +int WRAP(write)(int fd, const void* buf, size_t count, size_t* nwrote) { if (!ki_is_initialized()) return REAL(write)(fd, buf, count, nwrote); @@ -131,6 +131,76 @@ int WRAP(write)(int fd, const void *buf, size_t count, size_t *nwrote) { return (signed_nwrote < 0) ? errno : 0; } +// Socket functions +int accept(int fd, struct sockaddr* addr, socklen_t* len) { + return ki_accept(fd, addr, len); +} + +int bind(int fd, const struct sockaddr* addr, socklen_t len) { + return ki_bind(fd, addr, len); +} + +int connect(int fd, const struct sockaddr* addr, socklen_t len) { + return ki_connect(fd, addr, len); +} + +int getpeername(int fd, struct sockaddr* addr, socklen_t* len) { + return ki_getpeername(fd, addr, len); +} + +int getsockname(int fd, struct sockaddr* addr, socklen_t* len) { + return ki_getsockname(fd, addr, len); +} +int getsockopt(int fd, int lvl, int optname, void* optval, socklen_t* len) { + return ki_getsockopt(fd, lvl, optname, optval, len); +} + +int listen(int fd, int backlog) { + return ki_listen(fd, backlog); +} + +ssize_t recv(int fd, void* buf, size_t len, int flags) { + return ki_recv(fd, buf, len, flags); +} + +ssize_t recvfrom(int fd, void* buf, size_t len, int flags, + struct sockaddr* addr, socklen_t* addrlen) { + return ki_recvfrom(fd, buf, len, flags, addr, addrlen); +} + +ssize_t recvmsg(int fd, struct msghdr* msg, int flags) { + return ki_recvmsg(fd, msg, flags); +} + +ssize_t send(int fd, const void* buf, size_t len, int flags) { + return ki_send(fd, buf, len, flags); +} + +ssize_t sendto(int fd, const void* buf, size_t len, int flags, + const struct sockaddr* addr, socklen_t addrlen) { + return ki_sendto(fd, buf, len, flags, addr, addrlen); +} + +ssize_t sendmsg(int fd, const struct msghdr* msg, int flags) { + return ki_sendmsg(fd, msg, flags); +} + +int setsockopt(int fd, int lvl, int optname, const void* optval, + socklen_t len) { + return ki_setsockopt(fd, lvl, optname, optval, len); +} + +int shutdown(int fd, int how) { + return ki_shutdown(fd, how); +} + +int socket(int domain, int type, int protocol) { + return ki_socket(domain, type, protocol); +} + +int socketpair(int domain, int type, int protocol, int* sv) { + return ki_socketpair(domain, type, protocol, sv); +} // "real" functions, i.e. the unwrapped original functions. @@ -138,11 +208,11 @@ int _real_close(int fd) { return REAL(close)(fd); } -int _real_fstat(int fd, struct stat *buf) { +int _real_fstat(int fd, struct stat* buf) { return REAL(fstat)(fd, buf); } -int _real_getdents(int fd, dirent* nacl_buf, size_t nacl_count, size_t *nread) { +int _real_getdents(int fd, dirent* nacl_buf, size_t nacl_count, size_t* nread) { return REAL(getdents)(fd, nacl_buf, nacl_count, nread); } @@ -171,7 +241,7 @@ int _real_open_resource(const char* file, int* fd) { return ENOSYS; } -int _real_read(int fd, void *buf, size_t count, size_t *nread) { +int _real_read(int fd, void* buf, size_t count, size_t* nread) { return REAL(read)(fd, buf, count, nread); } @@ -179,7 +249,7 @@ int _real_rmdir(const char* pathname) { return ENOSYS; } -int _real_write(int fd, const void *buf, size_t count, size_t *nwrote) { +int _real_write(int fd, const void* buf, size_t count, size_t* nwrote) { return REAL(write)(fd, buf, count, nwrote); } diff --git a/native_client_sdk/src/libraries/nacl_io/library.dsc b/native_client_sdk/src/libraries/nacl_io/library.dsc index dd27fcb58e..8b4ed00cdd 100644 --- a/native_client_sdk/src/libraries/nacl_io/library.dsc +++ b/native_client_sdk/src/libraries/nacl_io/library.dsc @@ -1,9 +1,10 @@ { - 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win'], + 'TOOLS': ['newlib', 'glibc', 'pnacl'], 'SEARCH': [ '.', 'pepper', '../third_party/newlib-extras', + 'include', ], 'TARGETS': [ { @@ -12,6 +13,7 @@ 'SOURCES' : [ "event_emitter.cc", "event_listener.cc", + "inet_ntoa.cc", "kernel_handle.cc", "kernel_intercept.cc", "kernel_object.cc", @@ -50,10 +52,13 @@ "syscalls/lchown.c", "syscalls/mkdir.c", "syscalls/mount.c", + "syscalls/poll.c", "syscalls/remove.c", "syscalls/rmdir.c", + "syscalls/select.c", "syscalls/unlink.c", "syscalls/umount.c", + "syscalls/uname.c", "syscalls/utime.c", ], } @@ -107,7 +112,10 @@ "netdb.h", "netinet/in.h", "netinet6/in6.h", + "poll.h", + "sys/select.h", "sys/socket.h", + "sys/utsname.h", ], 'DEST': 'include/newlib', }, @@ -117,12 +125,21 @@ "netdb.h", "netinet/in.h", "netinet6/in6.h", + "poll.h", + "sys/select.h", "sys/socket.h", + "sys/utsname.h", ], 'DEST': 'include/pnacl', }, { 'FILES': [ + "poll.h", + ], + 'DEST': 'include/win', + }, + { + 'FILES': [ "all_interfaces.h", "define_empty_macros.h", "undef_macros.h", diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.cc b/native_client_sdk/src/libraries/nacl_io/mount_node.cc index e8178cc1c0..3e423da3f9 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.cc @@ -6,6 +6,7 @@ #include <errno.h> #include <fcntl.h> +#include <poll.h> #include <string.h> #include <sys/stat.h> @@ -49,9 +50,10 @@ void MountNode::Destroy() { } // Declared in EventEmitter, default to regular files which always return -// a ready of TRUE. +// a ready of TRUE for read, write, or error. uint32_t MountNode::GetEventStatus() { - return KE_READ_READY | KE_WRITE_READY | KE_SHUTDOWN; + uint32_t val = POLLIN | POLLOUT | POLLERR; + return val; } diff --git a/native_client_sdk/src/libraries/nacl_io/ossocket.h b/native_client_sdk/src/libraries/nacl_io/ossocket.h index e95b32747e..784086af8c 100644 --- a/native_client_sdk/src/libraries/nacl_io/ossocket.h +++ b/native_client_sdk/src/libraries/nacl_io/ossocket.h @@ -5,11 +5,13 @@ #ifndef LIBRARIES_NACL_IO_OSSOCKET_H_ #define LIBRARIES_NACL_IO_OSSOCKET_H_ -#if defined(__native_client__) && defined(__GLIBC__) +#if defined(__native_client__) #include <arpa/inet.h> #include <netdb.h> #include <netinet/in.h> +#include <poll.h> #include <sys/socket.h> +#include <sys/select.h> #define PROVIDES_SOCKET_API #endif diff --git a/native_client_sdk/src/libraries/nacl_io/poll.h b/native_client_sdk/src/libraries/nacl_io/poll.h new file mode 100644 index 0000000000..6c1d378e8f --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/poll.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2013 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 LIBRARIES_NACL_IO_POLL_H_ +#define LIBRARIES_NACL_IO_POLL_H_ + +#include <stdint.h> + +#include "sdk_util/macros.h" + +EXTERN_C_BEGIN + +/* This header adds definitions of flags and structures for use with poll on + * toolchains with 'C' libraries which do not normally supply poll. */ + +/* Node state flags */ +#define POLLIN 0x0001 /* Will not block READ select/poll. */ +#define POLLOUT 0x0002 /* Will not block WRITE select/poll. */ +#define POLLERR 0x0008 /* Will not block EXECPT select/poll. */ +#define POLLHUP 0x0010 /* Connection closed on far side. */ +#define POLLNVAL 0x0020 /* Invalid FD. */ + +/* Number of file descriptors. */ +typedef int nfds_t; + +struct pollfd { + int fd; + uint16_t events; + uint16_t revents; +}; + +int poll (struct pollfd *__fds, nfds_t __nfds, int __timeout); + +EXTERN_C_END + +#endif /* LIBRARIES_NACL_IO_POLL_H_ */ + diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/poll.c b/native_client_sdk/src/libraries/nacl_io/syscalls/poll.c new file mode 100644 index 0000000000..b5ddff7892 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/poll.c @@ -0,0 +1,10 @@ +/* Copyright (c) 2013 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 "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +int poll(struct pollfd *fds, nfds_t nfds, int timeout) { + return ki_poll(fds, nfds, timeout); +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/select.c b/native_client_sdk/src/libraries/nacl_io/syscalls/select.c new file mode 100644 index 0000000000..a486ecd3b4 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/select.c @@ -0,0 +1,11 @@ +/* Copyright (c) 2013 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 "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +int select(int nfds, fd_set* readfds, fd_set* writefds, + fd_set* exceptfds, struct timeval* timeout) { + return ki_select(nfds, readfds, writefds, exceptfds, timeout); +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/uname.c b/native_client_sdk/src/libraries/nacl_io/syscalls/uname.c new file mode 100644 index 0000000000..293b989ee4 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/uname.c @@ -0,0 +1,14 @@ +/* Copyright (c) 2013 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 <stdio.h> +#include <string.h> +#include <sys/utsname.h> + +int uname(struct utsname* buf) { + memset(buf, 0, sizeof(struct utsname)); + snprintf(buf->sysname, _UTSNAME_LENGTH, "NaCl"); + /* TODO(sbc): Fill out the other fields with useful information. */ + return 0; +} diff --git a/native_client_sdk/src/libraries/nacl_io_test/event_test.cc b/native_client_sdk/src/libraries/nacl_io_test/event_test.cc index aca9d9efbf..c342799afa 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/event_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/event_test.cc @@ -5,6 +5,7 @@ #include <errno.h> #include <fcntl.h> +#include <stdio.h> #include <sys/stat.h> #include <sys/time.h> @@ -12,20 +13,27 @@ #include "nacl_io/event_emitter.h" #include "nacl_io/event_listener.h" +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_proxy.h" +#include "nacl_io/kernel_wrap.h" using namespace nacl_io; using namespace sdk_util; -class EventEmitterTester : public EventEmitter { +class EventEmitterTester : public MountNode { public: - EventEmitterTester() : event_status_(0), event_cnt_(0) {} + EventEmitterTester() : MountNode(NULL), event_status_(0), event_cnt_(0) {} void SetEventStatus(uint32_t bits) { event_status_ = bits; } uint32_t GetEventStatus() { return event_status_; } - int GetType() { return S_IFSOCK; } + Error Ioctl(int request, char* arg) { + event_status_ = static_cast<uint32_t>(request); + return 0; + } + int GetType() { return S_IFSOCK; } int NumEvents() { return event_cnt_; } public: @@ -364,3 +372,109 @@ TEST(EventTest, EmitterSignalling) { EXPECT_EQ(USER_DATA_A, ev[0].user_data); EXPECT_EQ(KE_EXPECTED, ev[0].events); } + + +namespace { + +class KernelProxyPolling : public KernelProxy { + public: + virtual int socket(int domain, int type, int protocol) { + ScopedMount mnt; + ScopedMountNode node(new EventEmitterTester()); + ScopedKernelHandle handle(new KernelHandle(mnt, node)); + + Error error = handle->Init(0); + if (error) { + errno = error; + return -1; + } + + return AllocateFD(handle); + } +}; + +class KernelProxyPollingTest : public ::testing::Test { + public: + KernelProxyPollingTest() : kp_(new KernelProxyPolling) { + ki_init(kp_); + } + + ~KernelProxyPollingTest() { + ki_uninit(); + delete kp_; + } + + KernelProxyPolling* kp_; +}; + +} // namespace + + +#define SOCKET_CNT 4 +void SetFDs(fd_set* set, int* fds) { + FD_ZERO(set); + + FD_SET(0, set); + FD_SET(1, set); + FD_SET(2, set); + + for (int index = 0; index < SOCKET_CNT; index++) + FD_SET(fds[index], set); +} + +TEST_F(KernelProxyPollingTest, Select) { + int fds[SOCKET_CNT]; + + fd_set rd_set; + fd_set wr_set; + + FD_ZERO(&rd_set); + FD_ZERO(&wr_set); + + FD_SET(0, &rd_set); + FD_SET(1, &rd_set); + FD_SET(2, &rd_set); + + FD_SET(0, &wr_set); + FD_SET(1, &wr_set); + FD_SET(2, &wr_set); + + // Expect normal files to select as read, write, and error + int cnt = select(4, &rd_set, &rd_set, &rd_set, NULL); + EXPECT_EQ(3 * 3, cnt); + EXPECT_NE(0, FD_ISSET(0, &rd_set)); + EXPECT_NE(0, FD_ISSET(1, &rd_set)); + EXPECT_NE(0, FD_ISSET(2, &rd_set)); + + for (int index = 0 ; index < SOCKET_CNT; index++) { + fds[index] = socket(0, 0, 0); + EXPECT_NE(-1, fds[index]); + } + + // Highest numbered fd + const int fdnum = fds[SOCKET_CNT - 1] + 1; + + // Expect only the normal files to select + SetFDs(&rd_set, fds); + cnt = select(fds[SOCKET_CNT-1] + 1, &rd_set, NULL, NULL, NULL); + EXPECT_EQ(3, cnt); + EXPECT_NE(0, FD_ISSET(0, &rd_set)); + EXPECT_NE(0, FD_ISSET(1, &rd_set)); + EXPECT_NE(0, FD_ISSET(2, &rd_set)); + for (int index = 0 ; index < SOCKET_CNT; index++) { + EXPECT_EQ(0, FD_ISSET(fds[index], &rd_set)); + } + + // Poke one of the pollable nodes to be READ ready + ioctl(fds[0], POLLIN, NULL); + + // Expect normal files to be read/write and one pollable node to be read. + SetFDs(&rd_set, fds); + SetFDs(&wr_set, fds); + cnt = select(fdnum, &rd_set, &wr_set, NULL, NULL); + EXPECT_EQ(7, cnt); + EXPECT_NE(0, FD_ISSET(fds[0], &rd_set)); + EXPECT_EQ(0, FD_ISSET(fds[0], &wr_set)); +} + + diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h index d2fa5310e4..eeaf104f9e 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h @@ -53,6 +53,9 @@ class KernelProxyMock : public nacl_io::KernelProxy { MOCK_METHOD1(open_resource, int(const char*)); #ifdef PROVIDES_SOCKET_API + MOCK_METHOD3(poll, int(struct pollfd*, nfds_t, int)); + MOCK_METHOD5(select, int(int, fd_set*, fd_set*, fd_set*, struct timeval*)); + // Socket support functions MOCK_METHOD3(accept, int(int, struct sockaddr*, socklen_t*)); MOCK_METHOD3(bind, int(int, const struct sockaddr*, socklen_t)); diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc index 5989ce4e5e..92577e094b 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc @@ -287,8 +287,17 @@ TEST_F(KernelWrapTest, write) { } #ifdef PROVIDES_SOCKET_API -// Socket Functions +TEST_F(KernelWrapTest, poll) { + EXPECT_CALL(mock, poll(NULL, 5, -1)); + poll(NULL, 5, -1); +} +TEST_F(KernelWrapTest, select) { + EXPECT_CALL(mock, select(123, NULL, NULL, NULL, NULL)); + select(123, NULL, NULL, NULL, NULL); +} + +// Socket Functions TEST_F(KernelWrapTest, accept) { EXPECT_CALL(mock, accept(123, NULL, NULL)).Times(1); accept(123, NULL, NULL); diff --git a/native_client_sdk/src/libraries/nacl_io_test/socket_test.cc b/native_client_sdk/src/libraries/nacl_io_test/socket_test.cc index 57d3adea4e..9768c1339f 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/socket_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/socket_test.cc @@ -259,4 +259,111 @@ TEST_F(SocketTest, Socketpair) { EXPECT_EQ(errno, EPROTONOSUPPORT); } +// These functions don't go through KernelProxy, so they don't require a test +// fixture + +static struct in_addr generate_ipv4_addr(int tuple1, int tuple2, + int tuple3, int tuple4) { + unsigned char addr[4]; + addr[0] = static_cast<unsigned char>(tuple1); + addr[1] = static_cast<unsigned char>(tuple2); + addr[2] = static_cast<unsigned char>(tuple3); + addr[3] = static_cast<unsigned char>(tuple4); + struct in_addr* real_addr = reinterpret_cast<struct in_addr*>(addr); + return *real_addr; +} + +static struct in6_addr generate_ipv6_addr(int* tuples) { + unsigned char addr[16]; + for (int i = 0; i < 8; i++) { + addr[2*i] = (tuples[i] >> 8) & 0xFF; + addr[2*i+1] = tuples[i] & 0xFF; + } + struct in6_addr* real_addr = reinterpret_cast<struct in6_addr*>(addr); + return *real_addr; +} + +TEST(SocketUtilityFunctions, Inet_ntoa) { + char* stringified_addr = inet_ntoa(generate_ipv4_addr(0,0,0,0)); + ASSERT_TRUE(NULL != stringified_addr); + EXPECT_STREQ("0.0.0.0", stringified_addr); + + stringified_addr = inet_ntoa(generate_ipv4_addr(127,0,0,1)); + ASSERT_TRUE(NULL != stringified_addr); + EXPECT_STREQ("127.0.0.1", stringified_addr); + + stringified_addr = inet_ntoa(generate_ipv4_addr(255,255,255,255)); + ASSERT_TRUE(NULL != stringified_addr); + EXPECT_STREQ("255.255.255.255", stringified_addr); +} + +TEST(SocketUtilityFunctions, Inet_ntop_ipv4) { + char stringified_addr[INET_ADDRSTRLEN]; + + struct in_addr real_addr = generate_ipv4_addr(0,0,0,0); + EXPECT_TRUE(NULL != inet_ntop(AF_INET, &real_addr, + stringified_addr, INET_ADDRSTRLEN)); + EXPECT_STREQ("0.0.0.0", stringified_addr); + + real_addr = generate_ipv4_addr(127,0,0,1); + EXPECT_TRUE(NULL != inet_ntop(AF_INET, &real_addr, + stringified_addr, INET_ADDRSTRLEN)); + EXPECT_STREQ("127.0.0.1", stringified_addr); + + real_addr = generate_ipv4_addr(255,255,255,255); + EXPECT_TRUE(NULL != inet_ntop(AF_INET, &real_addr, + stringified_addr, INET_ADDRSTRLEN)); + EXPECT_STREQ("255.255.255.255", stringified_addr); +} + +TEST(SocketUtilityFunctions, Inet_ntop_ipv6) { + char stringified_addr[INET6_ADDRSTRLEN]; + + { + int addr_tuples[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + struct in6_addr real_addr = generate_ipv6_addr(addr_tuples); + EXPECT_TRUE(NULL != inet_ntop(AF_INET6, &real_addr, + stringified_addr, INET6_ADDRSTRLEN)); + EXPECT_STREQ("0:0:0:0:0:0:0:0", stringified_addr); + } + + { + int addr_tuples[8] = { 0x1234, 0xa, 0x12, 0x0000, + 0x5678, 0x9abc, 0xdef, 0xffff }; + struct in6_addr real_addr = generate_ipv6_addr(addr_tuples); + EXPECT_TRUE(NULL != inet_ntop(AF_INET6, &real_addr, + stringified_addr, INET6_ADDRSTRLEN)); + EXPECT_STREQ("1234:a:12:0:5678:9abc:def:ffff", stringified_addr); + } + + { + int addr_tuples[8] = { 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff }; + struct in6_addr real_addr = generate_ipv6_addr(addr_tuples); + EXPECT_TRUE(NULL != inet_ntop(AF_INET6, &real_addr, + stringified_addr, INET6_ADDRSTRLEN)); + EXPECT_STREQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", stringified_addr); + } +} + +TEST(SocketUtilityFunctions, Inet_ntop_failure) { + char addr_name[INET6_ADDRSTRLEN]; + int addr_tuples[8] = { 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff }; + struct in6_addr ipv6_addr = generate_ipv6_addr(addr_tuples); + struct in_addr ipv4_addr = generate_ipv4_addr(255,255,255,255); + + EXPECT_TRUE(NULL == inet_ntop(AF_UNIX, &ipv6_addr, + addr_name, INET6_ADDRSTRLEN)); + EXPECT_EQ(errno, EAFNOSUPPORT); + + EXPECT_TRUE(NULL == inet_ntop(AF_INET, &ipv4_addr, + addr_name, INET_ADDRSTRLEN - 1)); + EXPECT_EQ(errno, ENOSPC); + + EXPECT_TRUE(NULL == inet_ntop(AF_INET6, &ipv6_addr, + addr_name, INET6_ADDRSTRLEN - 1)); + EXPECT_EQ(errno, ENOSPC); +} + #endif // PROVIDES_SOCKETPAIR_API diff --git a/native_client_sdk/src/libraries/ppapi_simple/library.dsc b/native_client_sdk/src/libraries/ppapi_simple/library.dsc index 841aeedf8f..8cb2c056bf 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/library.dsc +++ b/native_client_sdk/src/libraries/ppapi_simple/library.dsc @@ -1,5 +1,5 @@ { - 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win'], + 'TOOLS': ['newlib', 'glibc', 'pnacl'], 'TARGETS': [ { 'NAME' : 'ppapi_simple', diff --git a/native_client_sdk/src/libraries/third_party/newlib-extras/sys/select.h b/native_client_sdk/src/libraries/third_party/newlib-extras/sys/select.h new file mode 100644 index 0000000000..2ec7612533 --- /dev/null +++ b/native_client_sdk/src/libraries/third_party/newlib-extras/sys/select.h @@ -0,0 +1,20 @@ +#ifndef _SYS_SELECT_H +#define _SYS_SELECT_H 1 + +#include <sys/types.h> +#include <time.h> + +#include "sdk_util/macros.h" + +EXTERN_C_BEGIN + +struct timeval; + +int select (int __nfds, fd_set *__restrict __readfds, + fd_set *__restrict __writefds, + fd_set *__restrict __exceptfds, + struct timeval *__restrict __timeout) __THROW; + +EXTERN_C_END + +#endif /* sys/select.h */ diff --git a/native_client_sdk/src/tools/common.mk b/native_client_sdk/src/tools/common.mk index 91d123da57..11feea8276 100644 --- a/native_client_sdk/src/tools/common.mk +++ b/native_client_sdk/src/tools/common.mk @@ -431,7 +431,7 @@ RUN_PY := python $(NACL_SDK_ROOT)/tools/run.py CHROME_ENV ?= # Additional arguments to pass to Chrome. -CHROME_ARGS += --enable-nacl --no-first-run +CHROME_ARGS += --enable-nacl --enable-pnacl --no-first-run CHROME_ARGS += --user-data-dir=$(CURDIR)/user-data-dir @@ -443,18 +443,25 @@ PPAPI_RELEASE = $(abspath $(OSNAME)/Release/$(TARGET)$(HOST_EXT));application/x- SYSARCH := $(shell $(GETOS) --nacl-arch) SEL_LDR_PATH := python $(NACL_SDK_ROOT)/tools/sel_ldr.py +# +# Common Compile Options +# +ifeq ($(CONFIG),Debug) +SEL_LDR_ARGS += --debug-libs +endif + ifdef SEL_LDR run: all ifndef NACL_ARCH $(error Cannot run in sel_ldr unless $$NACL_ARCH is set) endif - $(SEL_LDR_PATH) $(OUTDIR)/$(TARGET)_$(NACL_ARCH).nexe + $(SEL_LDR_PATH) $(SEL_LDR_ARGS) $(OUTDIR)/$(TARGET)_$(NACL_ARCH).nexe debug: all ifndef NACL_ARCH $(error Cannot run in sel_ldr unless $$NACL_ARCH is set) endif - $(SEL_LDR_PATH) -d $(OUTDIR)/$(TARGET)_$(NACL_ARCH).nexe + $(SEL_LDR_PATH) -d $(SEL_LDR_ARGS) $(OUTDIR)/$(TARGET)_$(NACL_ARCH).nexe else PAGE ?= index.html PAGE_TC_CONFIG ?= "$(PAGE)?tc=$(TOOLCHAIN)&config=$(CONFIG)" diff --git a/native_client_sdk/src/tools/sel_ldr.py b/native_client_sdk/src/tools/sel_ldr.py index 01de0be789..e500192d17 100755 --- a/native_client_sdk/src/tools/sel_ldr.py +++ b/native_client_sdk/src/tools/sel_ldr.py @@ -85,8 +85,12 @@ def main(argv): cmd.insert(0, helper) if dynamic: - libpath = os.path.join(NACL_SDK_ROOT, 'lib', - 'glibc_%s' % arch_suffix, 'Release') + if options.debug_libs: + libpath = os.path.join(NACL_SDK_ROOT, 'lib', + 'glibc_%s' % arch_suffix, 'Debug') + else: + libpath = os.path.join(NACL_SDK_ROOT, 'lib', + 'glibc_%s' % arch_suffix, 'Release') toolchain = '%s_x86_glibc' % osname sdk_lib_dir = os.path.join(NACL_SDK_ROOT, 'toolchain', toolchain, 'x86_64-nacl') |