diff --git a/DEPS b/DEPS index 8654f9c91..5cd8f3c38 100644 --- a/DEPS +++ b/DEPS @@ -19,7 +19,7 @@ vars = { 'chromium_git': 'https://chromium.googlesource.com', - 'mojo_sdk_revision': '033ebba671eced78b155862a1484ec1087b111e0', + 'mojo_sdk_revision': '56e62e2ecf2052becf04a321fdc80e90600a75bd', 'mojo_devtools_revision': '49879d78ce4486e10c2214a101d9b2e82794b2f4', 'skia_revision': '0d39d37ddcfb3847795639eaef513f1112eba627', @@ -29,7 +29,7 @@ vars = { 'dart_observatory_packages_revision': 'a731d3b1caf27b45aecdce9378b87a510240264d', 'dart_root_certificates_revision': 'c3a41df63afacec62fcb8135196177e35fe72f71', - 'buildtools_revision': '5215ee866bc3e8eb4a7f124212845abf4029e60b', + 'buildtools_revision': '565d04e8741429fb1b4f26d102f2c6c3b849edeb', } # Only these hosts are allowed for dependencies in this DEPS file. diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn index 746a1565a..ccb482324 100644 --- a/mojo/android/BUILD.gn +++ b/mojo/android/BUILD.gn @@ -102,7 +102,6 @@ android_library("mojo_javatests") { "//base:base_java", "//base:base_java_test_support", "//mojo/public/interfaces/bindings/tests:test_interfaces_java", - "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental_java", "//mojo/public/java:bindings", "//mojo/public/java:system", ] diff --git a/mojo/common/dart/lib/tracing_helper.dart b/mojo/common/dart/lib/tracing_helper.dart index 44147073d..5d797ecec 100644 --- a/mojo/common/dart/lib/tracing_helper.dart +++ b/mojo/common/dart/lib/tracing_helper.dart @@ -24,6 +24,7 @@ const traceEventPhaseBegin = "B"; const traceEventPhaseEnd = "E"; const traceEventPhaseAsyncBegin = "S"; const traceEventPhaseAsyncEnd = "F"; +const traceEventDuration = "X"; // TracingHelper is used by Dart code running in the Mojo shell in order // to perform tracing. @@ -36,8 +37,10 @@ class TracingHelper { // for use in trace messages. If |appName| is longer than 20 characters then // only the last 20 characters of |appName| will be used. TracingHelper.fromApplication(Application app, String appName) { - _tid = [appName, Isolate.current].fold(7, - (hash, element) => 31 * hash + element.hashCode); + // Masked because the tid is expected to be a 32-bit int. + _tid = [appName, Isolate.current] + .fold(7, (hash, element) => 31 * hash + element.hashCode) & + 0x7fffffff; _impl = new TraceProviderImpl(); ApplicationConnection connection = app.connectToApplication("mojo:tracing"); connection.provideService(TraceProviderName, (e) { @@ -80,33 +83,44 @@ class TracingHelper { _sendTraceMessage(name, categories, traceEventInstant, 0, args: args); } - FunctionTrace _beginFunction(String functionName, String categories, - String phase, {Map args}) { + void traceDuration(String name, String categories, int start, int end, + {Map args}) { + _sendTraceMessage(name, categories, traceEventDuration, 0, + args: args, start: start, duration: end - start); + } + + FunctionTrace _beginFunction( + String functionName, String categories, String phase, + {Map args}) { assert(functionName != null); - final trace = - new _FunctionTraceImpl(this, isActive() ? functionName : null, - categories, phase); + final trace = new _FunctionTraceImpl( + this, isActive() ? functionName : null, categories, phase); _sendTraceMessage(functionName, categories, phase, trace.hashCode, args: args); return trace; } - void _endFunction(String functionName, String categories, String phase, - int traceId) { + void _endFunction( + String functionName, String categories, String phase, int traceId) { _sendTraceMessage(functionName, categories, phase, traceId); } - void _sendTraceMessage(String name, String categories, String phase, - int traceId, {Map args}) { + void _sendTraceMessage( + String name, String categories, String phase, int traceId, + {Map args, int start, int duration}) { if (isActive()) { + var time = (start != null) ? start : getTimeTicksNow(); var map = {}; map["name"] = name; map["cat"] = categories; map["ph"] = phase; - map["ts"] = getTimeTicksNow(); + map["ts"] = time; map["pid"] = pid; map["tid"] = _tid; map["id"] = traceId; + if (duration != null) { + map["dur"] = duration; + } if (args != null) { map["args"] = args; } @@ -148,8 +162,8 @@ class _FunctionTraceImpl implements FunctionTrace { String _categories; String _beginPhase; - _FunctionTraceImpl(this._tracing, this._functionName, this._categories, - this._beginPhase) { + _FunctionTraceImpl( + this._tracing, this._functionName, this._categories, this._beginPhase) { assert(_beginPhase == traceEventPhaseBegin || _beginPhase == traceEventPhaseAsyncBegin); } @@ -158,11 +172,11 @@ class _FunctionTraceImpl implements FunctionTrace { void end() { if (_functionName != null) { if (_beginPhase == traceEventPhaseBegin) { - _tracing._endFunction(_functionName, _categories, traceEventPhaseEnd, - hashCode); + _tracing._endFunction( + _functionName, _categories, traceEventPhaseEnd, hashCode); } else if (_beginPhase == traceEventPhaseAsyncBegin) { - _tracing._endFunction(_functionName, _categories, - traceEventPhaseAsyncEnd, hashCode); + _tracing._endFunction( + _functionName, _categories, traceEventPhaseAsyncEnd, hashCode); } } } diff --git a/mojo/converters/input_events/input_events_type_converters.cc b/mojo/converters/input_events/input_events_type_converters.cc index a7d6a970f..253fcf0c0 100644 --- a/mojo/converters/input_events/input_events_type_converters.cc +++ b/mojo/converters/input_events/input_events_type_converters.cc @@ -21,23 +21,24 @@ namespace { ui::EventType MojoMouseEventTypeToUIEvent(const EventPtr& event) { DCHECK(!event->pointer_data.is_null()); - DCHECK_EQ(POINTER_KIND_MOUSE, event->pointer_data->kind); + DCHECK_EQ(PointerKind::MOUSE, event->pointer_data->kind); switch (event->action) { - case EVENT_TYPE_POINTER_DOWN: + case EventType::POINTER_DOWN: return ui::ET_MOUSE_PRESSED; - case EVENT_TYPE_POINTER_UP: + case EventType::POINTER_UP: return ui::ET_MOUSE_RELEASED; - case EVENT_TYPE_POINTER_MOVE: + case EventType::POINTER_MOVE: DCHECK(event->pointer_data); if (event->pointer_data->horizontal_wheel != 0 || event->pointer_data->vertical_wheel != 0) { return ui::ET_MOUSEWHEEL; } - if (event->flags & - (EVENT_FLAGS_LEFT_MOUSE_BUTTON | EVENT_FLAGS_MIDDLE_MOUSE_BUTTON | - EVENT_FLAGS_RIGHT_MOUSE_BUTTON)) { + if (static_cast(event->flags) & + (static_cast(EventFlags::LEFT_MOUSE_BUTTON) | + static_cast(EventFlags::MIDDLE_MOUSE_BUTTON) | + static_cast(EventFlags::RIGHT_MOUSE_BUTTON))) { return ui::ET_MOUSE_DRAGGED; } return ui::ET_MOUSE_MOVED; @@ -51,18 +52,18 @@ ui::EventType MojoMouseEventTypeToUIEvent(const EventPtr& event) { ui::EventType MojoTouchEventTypeToUIEvent(const EventPtr& event) { DCHECK(!event->pointer_data.is_null()); - DCHECK_EQ(POINTER_KIND_TOUCH, event->pointer_data->kind); + DCHECK_EQ(PointerKind::TOUCH, event->pointer_data->kind); switch (event->action) { - case EVENT_TYPE_POINTER_DOWN: + case EventType::POINTER_DOWN: return ui::ET_TOUCH_PRESSED; - case EVENT_TYPE_POINTER_UP: + case EventType::POINTER_UP: return ui::ET_TOUCH_RELEASED; - case EVENT_TYPE_POINTER_MOVE: + case EventType::POINTER_MOVE: return ui::ET_TOUCH_MOVED; - case EVENT_TYPE_POINTER_CANCEL: + case EventType::POINTER_CANCEL: return ui::ET_TOUCH_CANCELLED; default: @@ -82,43 +83,43 @@ void SetPointerDataLocationFromEvent(const ui::LocatedEvent& located_event, } // namespace -COMPILE_ASSERT(static_cast(EVENT_FLAGS_NONE) == +COMPILE_ASSERT(static_cast(EventFlags::NONE) == static_cast(ui::EF_NONE), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_CAPS_LOCK_DOWN) == +COMPILE_ASSERT(static_cast(EventFlags::CAPS_LOCK_DOWN) == static_cast(ui::EF_CAPS_LOCK_DOWN), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_SHIFT_DOWN) == +COMPILE_ASSERT(static_cast(EventFlags::SHIFT_DOWN) == static_cast(ui::EF_SHIFT_DOWN), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_CONTROL_DOWN) == +COMPILE_ASSERT(static_cast(EventFlags::CONTROL_DOWN) == static_cast(ui::EF_CONTROL_DOWN), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_ALT_DOWN) == +COMPILE_ASSERT(static_cast(EventFlags::ALT_DOWN) == static_cast(ui::EF_ALT_DOWN), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_LEFT_MOUSE_BUTTON) == +COMPILE_ASSERT(static_cast(EventFlags::LEFT_MOUSE_BUTTON) == static_cast(ui::EF_LEFT_MOUSE_BUTTON), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_MIDDLE_MOUSE_BUTTON) == +COMPILE_ASSERT(static_cast(EventFlags::MIDDLE_MOUSE_BUTTON) == static_cast(ui::EF_MIDDLE_MOUSE_BUTTON), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_RIGHT_MOUSE_BUTTON) == +COMPILE_ASSERT(static_cast(EventFlags::RIGHT_MOUSE_BUTTON) == static_cast(ui::EF_RIGHT_MOUSE_BUTTON), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_COMMAND_DOWN) == +COMPILE_ASSERT(static_cast(EventFlags::COMMAND_DOWN) == static_cast(ui::EF_COMMAND_DOWN), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_EXTENDED) == +COMPILE_ASSERT(static_cast(EventFlags::EXTENDED) == static_cast(ui::EF_EXTENDED), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_IS_SYNTHESIZED) == +COMPILE_ASSERT(static_cast(EventFlags::IS_SYNTHESIZED) == static_cast(ui::EF_IS_SYNTHESIZED), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_ALTGR_DOWN) == +COMPILE_ASSERT(static_cast(EventFlags::ALTGR_DOWN) == static_cast(ui::EF_ALTGR_DOWN), event_flags_should_match); -COMPILE_ASSERT(static_cast(EVENT_FLAGS_MOD3_DOWN) == +COMPILE_ASSERT(static_cast(EventFlags::MOD3_DOWN) == static_cast(ui::EF_MOD3_DOWN), event_flags_should_match); @@ -128,7 +129,7 @@ EventType TypeConverter::Convert(ui::EventType type) { switch (type) { case ui::ET_MOUSE_PRESSED: case ui::ET_TOUCH_PRESSED: - return EVENT_TYPE_POINTER_DOWN; + return EventType::POINTER_DOWN; case ui::ET_MOUSE_DRAGGED: case ui::ET_MOUSE_MOVED: @@ -136,30 +137,30 @@ EventType TypeConverter::Convert(ui::EventType type) { case ui::ET_MOUSE_EXITED: case ui::ET_TOUCH_MOVED: case ui::ET_MOUSEWHEEL: - return EVENT_TYPE_POINTER_MOVE; + return EventType::POINTER_MOVE; case ui::ET_MOUSE_RELEASED: case ui::ET_TOUCH_RELEASED: - return EVENT_TYPE_POINTER_UP; + return EventType::POINTER_UP; case ui::ET_TOUCH_CANCELLED: - return EVENT_TYPE_POINTER_CANCEL; + return EventType::POINTER_CANCEL; case ui::ET_KEY_PRESSED: - return EVENT_TYPE_KEY_PRESSED; + return EventType::KEY_PRESSED; case ui::ET_KEY_RELEASED: - return EVENT_TYPE_KEY_RELEASED; + return EventType::KEY_RELEASED; default: break; } - return EVENT_TYPE_UNKNOWN; + return EventType::UNKNOWN; } EventPtr TypeConverter::Convert(const ui::Event& input) { const EventType type = ConvertTo(input.type()); - if (type == EVENT_TYPE_UNKNOWN) + if (type == EventType::UNKNOWN) return nullptr; EventPtr event = Event::New(); @@ -174,7 +175,7 @@ EventPtr TypeConverter::Convert(const ui::Event& input) { PointerDataPtr pointer_data(PointerData::New()); // TODO(sky): come up with a better way to handle this. pointer_data->pointer_id = std::numeric_limits::max(); - pointer_data->kind = POINTER_KIND_MOUSE; + pointer_data->kind = PointerKind::MOUSE; SetPointerDataLocationFromEvent(*located_event, pointer_data.get()); if (input.IsMouseWheelEvent()) { const ui::MouseWheelEvent* wheel_event = @@ -192,7 +193,7 @@ EventPtr TypeConverter::Convert(const ui::Event& input) { static_cast(&input); PointerDataPtr pointer_data(PointerData::New()); pointer_data->pointer_id = touch_event->touch_id(); - pointer_data->kind = POINTER_KIND_TOUCH; + pointer_data->kind = PointerKind::TOUCH; SetPointerDataLocationFromEvent(*touch_event, pointer_data.get()); pointer_data->radius_major = touch_event->radius_x(); pointer_data->radius_minor = touch_event->radius_y(); @@ -244,22 +245,26 @@ scoped_ptr TypeConverter, EventPtr>::Convert( } switch (input->action) { - case EVENT_TYPE_KEY_PRESSED: - case EVENT_TYPE_KEY_RELEASED: { + case EventType::KEY_PRESSED: + case EventType::KEY_RELEASED: { scoped_ptr key_event; if (input->key_data->is_char) { + // TODO(johngro) : Need to verify that input->flags (a mojom generated + // enum) is a valid set of flags for a ui::KeyEvent key_event.reset(new ui::KeyEvent( static_cast(input->key_data->character), static_cast( input->key_data->key_code), - input->flags)); + static_cast(input->flags))); } else { + // TODO(johngro) : Need to verify that input->flags (a mojom generated + // enum) is a valid set of flags for a ui::KeyEvent key_event.reset(new ui::KeyEvent( - input->action == EVENT_TYPE_KEY_PRESSED ? ui::ET_KEY_PRESSED + input->action == EventType::KEY_PRESSED ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED, static_cast(input->key_data->key_code), - input->flags)); + static_cast(input->flags))); } key_event->SetExtendedKeyEventData(scoped_ptr( new MojoExtendedKeyEventData( @@ -269,11 +274,11 @@ scoped_ptr TypeConverter, EventPtr>::Convert( key_event->set_platform_keycode(input->key_data->native_key_code); return key_event.Pass(); } - case EVENT_TYPE_POINTER_DOWN: - case EVENT_TYPE_POINTER_UP: - case EVENT_TYPE_POINTER_MOVE: - case EVENT_TYPE_POINTER_CANCEL: { - if (input->pointer_data->kind == POINTER_KIND_MOUSE) { + case EventType::POINTER_DOWN: + case EventType::POINTER_UP: + case EventType::POINTER_MOVE: + case EventType::POINTER_CANCEL: { + if (input->pointer_data->kind == PointerKind::MOUSE) { // TODO: last flags isn't right. Need to send changed_flags. scoped_ptr event(new ui::MouseEvent( MojoMouseEventTypeToUIEvent(input), location, screen_location, diff --git a/mojo/converters/surfaces/surfaces_type_converters.cc b/mojo/converters/surfaces/surfaces_type_converters.cc index 5935acfd4..ecf65505b 100644 --- a/mojo/converters/surfaces/surfaces_type_converters.cc +++ b/mojo/converters/surfaces/surfaces_type_converters.cc @@ -21,9 +21,9 @@ namespace mojo { -#define ASSERT_ENUM_VALUES_EQUAL(value) \ - COMPILE_ASSERT(cc::DrawQuad::value == static_cast( \ - MATERIAL_##value), \ +#define ASSERT_ENUM_VALUES_EQUAL(value) \ + COMPILE_ASSERT(static_cast(cc::DrawQuad::value) == \ + static_cast(Material::value), \ value##_enum_value_matches) ASSERT_ENUM_VALUES_EQUAL(CHECKERBOARD); @@ -39,11 +39,11 @@ ASSERT_ENUM_VALUES_EQUAL(YUV_VIDEO_CONTENT); COMPILE_ASSERT( cc::YUVVideoDrawQuad::REC_601 == - static_cast(YUV_COLOR_SPACE_REC_601), + static_cast(YUVColorSpace::REC_601), rec_601_enum_matches); COMPILE_ASSERT(cc::YUVVideoDrawQuad::JPEG == static_cast( - YUV_COLOR_SPACE_JPEG), + YUVColorSpace::JPEG), rec_601_jpeg_enum_matches); namespace { @@ -66,7 +66,7 @@ bool ConvertDrawQuad(const QuadPtr& input, cc::SharedQuadState* sqs, cc::RenderPass* render_pass) { switch (input->material) { - case MATERIAL_RENDER_PASS: { + case Material::RENDER_PASS: { cc::RenderPassDrawQuad* render_pass_quad = render_pass->CreateAndAppendDrawQuad(); RenderPassQuadState* render_pass_quad_state = @@ -90,7 +90,7 @@ bool ConvertDrawQuad(const QuadPtr& input, cc::FilterOperations()); // TODO(jamesr): background_filters break; } - case MATERIAL_SOLID_COLOR: { + case Material::SOLID_COLOR: { if (input->solid_color_quad_state.is_null()) return false; cc::SolidColorDrawQuad* color_quad = @@ -105,7 +105,7 @@ bool ConvertDrawQuad(const QuadPtr& input, input->solid_color_quad_state->force_anti_aliasing_off); break; } - case MATERIAL_SURFACE_CONTENT: { + case Material::SURFACE_CONTENT: { if (input->surface_quad_state.is_null()) return false; cc::SurfaceDrawQuad* surface_quad = @@ -119,7 +119,7 @@ bool ConvertDrawQuad(const QuadPtr& input, input->surface_quad_state->surface.To()); break; } - case MATERIAL_TEXTURE_CONTENT: { + case Material::TEXTURE_CONTENT: { TextureQuadStatePtr& texture_quad_state = input->texture_quad_state; if (texture_quad_state.is_null() || @@ -144,7 +144,7 @@ bool ConvertDrawQuad(const QuadPtr& input, texture_quad_state->nearest_neighbor); break; } - case MATERIAL_TILED_CONTENT: { + case Material::TILED_CONTENT: { TileQuadStatePtr& tile_state = input->tile_quad_state; if (tile_state.is_null()) return false; @@ -162,7 +162,7 @@ bool ConvertDrawQuad(const QuadPtr& input, tile_state->nearest_neighbor); break; } - case MATERIAL_YUV_VIDEO_CONTENT: { + case Material::YUV_VIDEO_CONTENT: { YUVVideoQuadStatePtr& yuv_state = input->yuv_video_quad_state; if (yuv_state.is_null()) return false; diff --git a/mojo/converters/surfaces/tests/surface_unittest.cc b/mojo/converters/surfaces/tests/surface_unittest.cc index cdfb9cba9..4c16d78d3 100644 --- a/mojo/converters/surfaces/tests/surface_unittest.cc +++ b/mojo/converters/surfaces/tests/surface_unittest.cc @@ -71,7 +71,7 @@ TEST_F(SurfaceLibQuadTest, ColorQuad) { QuadPtr mojo_quad = Quad::From(*color_quad); ASSERT_FALSE(mojo_quad.is_null()); - EXPECT_EQ(MATERIAL_SOLID_COLOR, mojo_quad->material); + EXPECT_EQ(Material::SOLID_COLOR, mojo_quad->material); EXPECT_EQ(Rect::From(rect), mojo_quad->rect); EXPECT_EQ(Rect::From(opaque_rect), mojo_quad->opaque_rect); EXPECT_EQ(Rect::From(visible_rect), mojo_quad->visible_rect); @@ -91,7 +91,7 @@ TEST_F(SurfaceLibQuadTest, SurfaceQuad) { QuadPtr mojo_quad = Quad::From(*surface_quad); ASSERT_FALSE(mojo_quad.is_null()); - EXPECT_EQ(MATERIAL_SURFACE_CONTENT, mojo_quad->material); + EXPECT_EQ(Material::SURFACE_CONTENT, mojo_quad->material); ASSERT_TRUE(mojo_quad->surface_quad_state); SurfaceQuadStatePtr& mojo_surface_state = mojo_quad->surface_quad_state; EXPECT_EQ(SurfaceId::From(arbitrary_id), @@ -125,7 +125,7 @@ TEST_F(SurfaceLibQuadTest, TextureQuad) { QuadPtr mojo_quad = Quad::From(*texture_quad); ASSERT_FALSE(mojo_quad.is_null()); - EXPECT_EQ(MATERIAL_TEXTURE_CONTENT, mojo_quad->material); + EXPECT_EQ(Material::TEXTURE_CONTENT, mojo_quad->material); ASSERT_TRUE(mojo_quad->texture_quad_state); TextureQuadStatePtr& mojo_texture_state = mojo_quad->texture_quad_state; EXPECT_EQ(resource_id, mojo_texture_state->resource_id); @@ -142,7 +142,7 @@ TEST_F(SurfaceLibQuadTest, TextureQuad) { TEST_F(SurfaceLibQuadTest, TextureQuadEmptyVertexOpacity) { QuadPtr mojo_texture_quad = Quad::New(); - mojo_texture_quad->material = MATERIAL_TEXTURE_CONTENT; + mojo_texture_quad->material = Material::TEXTURE_CONTENT; TextureQuadStatePtr mojo_texture_state = TextureQuadState::New(); mojo_texture_state->background_color = Color::New(); mojo_texture_quad->texture_quad_state = mojo_texture_state.Pass(); @@ -158,7 +158,7 @@ TEST_F(SurfaceLibQuadTest, TextureQuadEmptyVertexOpacity) { TEST_F(SurfaceLibQuadTest, TextureQuadEmptyBackgroundColor) { QuadPtr mojo_texture_quad = Quad::New(); - mojo_texture_quad->material = MATERIAL_TEXTURE_CONTENT; + mojo_texture_quad->material = Material::TEXTURE_CONTENT; TextureQuadStatePtr mojo_texture_state = TextureQuadState::New(); mojo_texture_state->vertex_opacity = mojo::Array::New(4); mojo_texture_quad->texture_quad_state = mojo_texture_state.Pass(); diff --git a/mojo/dart/dart_snapshotter/vm.cc b/mojo/dart/dart_snapshotter/vm.cc index f95cf5a54..2b15cb1a8 100644 --- a/mojo/dart/dart_snapshotter/vm.cc +++ b/mojo/dart/dart_snapshotter/vm.cc @@ -23,7 +23,7 @@ void InitDartVM() { CHECK(Dart_SetVMFlags(arraysize(kDartArgs), kDartArgs)); CHECK(Dart_Initialize(mojo::dart::vm_isolate_snapshot_buffer, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr)); + nullptr, nullptr, nullptr) == nullptr); } Dart_Isolate CreateDartIsolate() { diff --git a/mojo/dart/embedder/BUILD.gn b/mojo/dart/embedder/BUILD.gn index a141cb4f9..f302ae7b4 100644 --- a/mojo/dart/embedder/BUILD.gn +++ b/mojo/dart/embedder/BUILD.gn @@ -121,6 +121,7 @@ action_foreach("dart_embedder_package_sdk") { "//mojo/public/dart/mojo/sdk_ext/src/handle_watcher.dart", "//mojo/public/dart/mojo/sdk_ext/src/natives.dart", "//mojo/public/dart/mojo/sdk_ext/src/timer_queue.dart", + "//mojo/public/dart/mojo/sdk_ext/src/wait_many_state.dart", ] outputs = [ "$root_gen_dir/dart_embedder_packages/{{source_root_relative_dir}}/{{source_file_part}}", @@ -302,6 +303,7 @@ action("generate_snapshot_bin") { "//mojo/public/dart/mojo/sdk_ext/src/handle_watcher.dart", "//mojo/public/dart/mojo/sdk_ext/src/natives.dart", "//mojo/public/dart/mojo/sdk_ext/src/timer_queue.dart", + "//mojo/public/dart/mojo/sdk_ext/src/wait_many_state.dart", ] vm_isolate_snapshot = "$target_gen_dir/vm_isolate_snapshot.bin" isolate_snapshot = "$target_gen_dir/isolate_snapshot.bin" diff --git a/mojo/dart/embedder/dart_controller.cc b/mojo/dart/embedder/dart_controller.cc index 81ca21794..b5f7cf107 100644 --- a/mojo/dart/embedder/dart_controller.cc +++ b/mojo/dart/embedder/dart_controller.cc @@ -40,6 +40,16 @@ static const char* kInternalLibURL = "dart:_internal"; static const char* kIsolateLibURL = "dart:isolate"; static const char* kCoreLibURL = "dart:core"; +// Notes on isolate startup order: +// 1) vm-service isolate is spawned by the VM when Dart_Initialize is called. +// 2) mojo-handle-watcher is spawned by from the vm-service isolate. +// 3) ... application isolates ... +// 4) stop-handle-watcher is spawned to shutdown the handle watcher isolate +// before the controller exits. +static const char* kVmServiceIsolateUri = "vm-service"; +static const char* kStopHandleWatcherIsolateUri = "stop-handle-watcher"; +static const char* kHandleWatcherIsolateUri = "mojo-handle-watcher"; + static uint8_t snapshot_magic_number[] = { 0xf5, 0xf5, 0xdc, 0xdc }; static Dart_Handle SetWorkingDirectory(Dart_Handle builtin_lib) { @@ -267,10 +277,15 @@ Dart_Isolate DartController::CreateIsolateHelper( void* dart_app, bool strict_compilation, IsolateCallbacks callbacks, - const std::string& script_uri, + std::string script_uri, const std::string& package_root, char** error, bool use_network_loader) { + if ((script_uri == kVmServiceIsolateUri) && service_isolate_spawned_) { + // Rewrite the uri so that it is easier to differentiate the actual service + // isolate from the handle watcher isolate. + script_uri = kHandleWatcherIsolateUri; + } auto isolate_data = new MojoDartState(dart_app, strict_compilation, callbacks, @@ -326,6 +341,7 @@ Dart_Isolate DartController::CreateIsolateHelper( // The VM is creating the service isolate. if (Dart_IsServiceIsolate(isolate)) { + service_isolate_spawned_ = true; const intptr_t port = SupportDartMojoIo() ? 0 : -1; InitializeDartMojoIo(); StartHandleWatcherIsolate(); @@ -336,7 +352,8 @@ Dart_Isolate DartController::CreateIsolateHelper( return isolate; } - if ((script_uri == "vm-service") || (script_uri == "stop-handle-watcher")) { + if ((script_uri == kHandleWatcherIsolateUri) || + (script_uri == kStopHandleWatcherIsolateUri)) { // Special case for starting and stopping the the handle watcher isolate. LoadEmptyScript(script_uri); } else { @@ -397,7 +414,7 @@ Dart_Isolate DartController::IsolateCreateCallback(const char* script_uri, } // Inherit parameters from parent isolate (if any). void* dart_app = nullptr; - bool strict_compilation = true; + bool strict_compilation = false; // TODO(johnmccutchan): Use parent's setting? bool use_network_loader = false; IsolateCallbacks callbacks; @@ -444,6 +461,7 @@ void DartController::UnhandledExceptionCallback(Dart_Handle error) { bool DartController::initialized_ = false; bool DartController::service_isolate_running_ = false; +bool DartController::service_isolate_spawned_ = false; bool DartController::strict_compilation_ = false; DartControllerServiceConnector* DartController::service_connector_ = nullptr; base::Lock DartController::lock_; @@ -531,7 +549,13 @@ void DartController::StopHandleWatcherIsolate() { IsolateCallbacks callbacks; char* error; Dart_Isolate shutdown_isolate = CreateIsolateHelper( - nullptr, false, callbacks, "stop-handle-watcher", "", &error, false); + nullptr, + false, + callbacks, + kStopHandleWatcherIsolateUri, + "", + &error, + false); CHECK(shutdown_isolate); Dart_EnterIsolate(shutdown_isolate); @@ -594,15 +618,17 @@ void DartController::InitVmIfNeeded(Dart_EntropySource entropy, // This should be called before calling Dart_Initialize. tonic::DartDebugger::InitDebugger(); - result = Dart_Initialize(vm_isolate_snapshot_buffer, - IsolateCreateCallback, - nullptr, // Isolate interrupt callback. - UnhandledExceptionCallback, - IsolateShutdownCallback, - // File IO callbacks. - nullptr, nullptr, nullptr, nullptr, - entropy); - CHECK(result); + const char* error = Dart_Initialize( + vm_isolate_snapshot_buffer, + nullptr, // Precompiled instructions + IsolateCreateCallback, + nullptr, // Isolate interrupt callback. + UnhandledExceptionCallback, + IsolateShutdownCallback, + // File IO callbacks. + nullptr, nullptr, nullptr, nullptr, + entropy); + CHECK(error == nullptr); initialized_ = true; } diff --git a/mojo/dart/embedder/dart_controller.h b/mojo/dart/embedder/dart_controller.h index 466468051..a6ae25aa1 100644 --- a/mojo/dart/embedder/dart_controller.h +++ b/mojo/dart/embedder/dart_controller.h @@ -147,7 +147,7 @@ class DartController { static Dart_Isolate CreateIsolateHelper(void* dart_app, bool strict_compilation, IsolateCallbacks callbacks, - const std::string& script_uri, + std::string script_uri, const std::string& package_root, char** error, bool use_network_loader); @@ -166,6 +166,7 @@ class DartController { static bool initialized_; static bool strict_compilation_; static bool service_isolate_running_; + static bool service_isolate_spawned_; static DartControllerServiceConnector* service_connector_; }; diff --git a/mojo/dart/embedder/io/server_socket_patch.dart b/mojo/dart/embedder/io/server_socket_patch.dart index 6e2acca55..543ed04d5 100644 --- a/mojo/dart/embedder/io/server_socket_patch.dart +++ b/mojo/dart/embedder/io/server_socket_patch.dart @@ -245,46 +245,6 @@ class _MojoRawServerSocket extends Stream _resume(); } } - - RawServerSocketReference get reference { - if (_referencePort == null) { - _referencePort = new ReceivePort(); - _referencePort.listen((sendPort) { - sendPort.send( - [_socket.address, - _socket.port, - _v6Only]); - }); - } - return new _MojoRawServerSocketReference(_referencePort.sendPort); - } - - Map _toJSON(bool ref) => {}; -} - -class _MojoRawServerSocketReference implements RawServerSocketReference { - final SendPort _sendPort; - - _MojoRawServerSocketReference(this._sendPort); - - Future create() { - var port = new ReceivePort(); - _sendPort.send(port.sendPort); - return port.first.then((List args) { - port.close(); - - InternetAddress address = args[0]; - int tcpPort = args[1]; - bool v6Only = args[2]; - return - RawServerSocket.bind(address, tcpPort, v6Only: v6Only, shared: true); - }); - } - - int get hashCode => _sendPort.hashCode; - - bool operator==(Object other) - => other is _MojoServerSocketReference && _sendPort == other._sendPort; } class _MojoServerSocket extends Stream @@ -323,21 +283,7 @@ class _MojoServerSocket extends Stream Future close() => _socket.close().then((_) => this); - ServerSocketReference get reference { - return new _MojoServerSocketReference(_socket.reference); - } - Map _toJSON(bool ref) => _socket._toJSON(ref); void set _owner(owner) { _socket._owner = owner; } } - -class _MojoServerSocketReference implements ServerSocketReference { - final RawServerSocketReference _rawReference; - - _MojoServerSocketReference(this._rawReference); - - Future create() { - return _rawReference.create().then((raw) => new _MojoServerSocket(raw)); - } -} diff --git a/mojo/dart/embedder/mojo_natives.cc b/mojo/dart/embedder/mojo_natives.cc index 8c6d8278f..02eb5956a 100644 --- a/mojo/dart/embedder/mojo_natives.cc +++ b/mojo/dart/embedder/mojo_natives.cc @@ -39,6 +39,8 @@ namespace dart { V(MojoHandle_Wait, 3) \ V(MojoHandle_Register, 2) \ V(MojoHandle_WaitMany, 3) \ + V(MojoHandleWatcher_GrowStateArrays, 1) \ + V(MojoHandleWatcher_WaitMany, 2) \ V(MojoHandleWatcher_SendControlData, 4) \ V(MojoHandleWatcher_RecvControlData, 1) \ V(MojoHandleWatcher_SetControlHandle, 1) \ @@ -720,6 +722,99 @@ void MojoMessagePipe_Read(Dart_NativeArguments arguments) { Dart_SetReturnValue(arguments, list); } +struct MojoWaitManyState { + std::vector handles; + std::vector signals; + std::vector out_index; + std::vector out_signals; +}; + +// This global is safe because it is only accessed by the single handle watcher +// isolate. If multiple handle watcher isolates are ever needed, it will need +// to be replicated. +static MojoWaitManyState handle_watcher_wait_state; + +void MojoHandleWatcher_GrowStateArrays(Dart_NativeArguments arguments) { + int64_t new_length; + CHECK_INTEGER_ARGUMENT(arguments, 0, &new_length, InvalidArgument); + + handle_watcher_wait_state.handles.resize(new_length); + handle_watcher_wait_state.signals.resize(new_length); + handle_watcher_wait_state.out_index.resize(1); + handle_watcher_wait_state.out_signals.resize(new_length); + + Dart_Handle dart_handles = Dart_NewExternalTypedData( + Dart_TypedData_kUint32, handle_watcher_wait_state.handles.data(), + handle_watcher_wait_state.handles.size()); + if (Dart_IsError(dart_handles)) { + Dart_PropagateError(dart_handles); + } + if (Dart_IsNull(dart_handles)) { + SetNullReturn(arguments); + return; + } + + Dart_Handle dart_signals = Dart_NewExternalTypedData( + Dart_TypedData_kUint32, handle_watcher_wait_state.signals.data(), + handle_watcher_wait_state.signals.size()); + if (Dart_IsError(dart_signals)) { + Dart_PropagateError(dart_signals); + } + if (Dart_IsNull(dart_signals)) { + SetNullReturn(arguments); + return; + } + + Dart_Handle dart_out_index = Dart_NewExternalTypedData( + Dart_TypedData_kUint32, handle_watcher_wait_state.out_index.data(), + handle_watcher_wait_state.out_index.size()); + if (Dart_IsError(dart_out_index)) { + Dart_PropagateError(dart_out_index); + } + if (Dart_IsNull(dart_out_index)) { + SetNullReturn(arguments); + return; + } + + Dart_Handle dart_out_signals = Dart_NewExternalTypedData( + Dart_TypedData_kUint64, handle_watcher_wait_state.out_signals.data(), + handle_watcher_wait_state.out_signals.size()); + if (Dart_IsError(dart_out_signals)) { + Dart_PropagateError(dart_out_signals); + } + if (Dart_IsNull(dart_out_signals)) { + SetNullReturn(arguments); + return; + } + + Dart_Handle list = Dart_NewList(4); + Dart_ListSetAt(list, 0, dart_handles); + Dart_ListSetAt(list, 1, dart_signals); + Dart_ListSetAt(list, 2, dart_out_index); + Dart_ListSetAt(list, 3, dart_out_signals); + Dart_SetReturnValue(arguments, list); +} + +void MojoHandleWatcher_WaitMany(Dart_NativeArguments arguments) { + int64_t handles_len = 0; + int64_t deadline = 0; + CHECK_INTEGER_ARGUMENT(arguments, 0, &handles_len, InvalidArgument); + CHECK_INTEGER_ARGUMENT(arguments, 1, &deadline, InvalidArgument); + + uint32_t* handles = handle_watcher_wait_state.handles.data(); + uint32_t* signals = handle_watcher_wait_state.signals.data(); + uint32_t* out_index = handle_watcher_wait_state.out_index.data(); + MojoHandleSignalsState* out_signals = + handle_watcher_wait_state.out_signals.data(); + + Dart_IsolateBlocked(); + MojoResult mojo_result = MojoWaitMany(handles, signals, handles_len, deadline, + out_index, out_signals); + Dart_IsolateUnblocked(); + + Dart_SetIntegerReturnValue(arguments, static_cast(mojo_result)); +} + struct ControlData { int64_t handle; Dart_Port port; diff --git a/mojo/dart/embedder/test/BUILD.gn b/mojo/dart/embedder/test/BUILD.gn index 2b2d429f5..391a3411e 100644 --- a/mojo/dart/embedder/test/BUILD.gn +++ b/mojo/dart/embedder/test/BUILD.gn @@ -52,7 +52,6 @@ source_set("dart_to_cpp_unittests") { "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", "//mojo/public/interfaces/bindings/tests:test_interfaces", - "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental", "//testing/gtest", ] } diff --git a/mojo/dart/embedder/vmservice/main.dart b/mojo/dart/embedder/vmservice/main.dart index 566e2bfef..0a3ba0ce5 100644 --- a/mojo/dart/embedder/vmservice/main.dart +++ b/mojo/dart/embedder/vmservice/main.dart @@ -8,7 +8,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:isolate'; -import 'dart:vmservice'; +import 'dart:_vmservice'; part 'loader.dart'; part 'resources.dart'; diff --git a/mojo/dart/mojo_services/CHANGELOG.md b/mojo/dart/mojo_services/CHANGELOG.md index 9b163db0b..e97355529 100644 --- a/mojo/dart/mojo_services/CHANGELOG.md +++ b/mojo/dart/mojo_services/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0 + + - 92 changes: https://github.com/domokit/mojo/compare/c1d7bc9...0894421 + ## 0.1.0 - 0 changes: https://github.com/domokit/mojo/compare/86d3dc4...86d3dc4 diff --git a/mojo/dart/mojo_services/pubspec.yaml b/mojo/dart/mojo_services/pubspec.yaml index 598dd292b..4b5d9880e 100644 --- a/mojo/dart/mojo_services/pubspec.yaml +++ b/mojo/dart/mojo_services/pubspec.yaml @@ -5,4 +5,4 @@ dependencies: description: Generated bindings for mojo services homepage: https://github.com/domokit/mojo name: mojo_services -version: 0.1.0 +version: 0.2.0 diff --git a/mojo/dart/test/bindings_generation_test.dart b/mojo/dart/test/bindings_generation_test.dart index 807b20596..71559f2be 100644 --- a/mojo/dart/test/bindings_generation_test.dart +++ b/mojo/dart/test/bindings_generation_test.dart @@ -15,6 +15,7 @@ import 'package:mojom/mojo/test/test_structs.mojom.dart' as structs; import 'package:mojom/mojo/test/test_unions.mojom.dart' as unions; import 'package:mojom/mojo/test/rect.mojom.dart' as rect; import 'package:mojom/mojo/test/serialization_test_structs.mojom.dart' as serialization; +import 'package:mojom/regression_tests/regression_tests.mojom.dart' as regression; class ProviderImpl implements sample.Provider { sample.ProviderStub _stub; @@ -51,8 +52,9 @@ Future testCallResponse() { client.ptr.echoStrings("hello", "mojo!").then((echoStringsResponse) { Expect.equals("hello", echoStringsResponse.a); Expect.equals("mojo!", echoStringsResponse.b); - client.close(); - c.complete(true); + client.close().then((_) { + c.complete(true); + }); }); }); }); @@ -71,7 +73,7 @@ Future testAwaitCallResponse() async { Expect.equals("hello", echoStringsResponse.a); Expect.equals("mojo!", echoStringsResponse.b); - client.close(); + await client.close(); } bindings.ServiceMessage messageOfStruct(bindings.Struct s) => @@ -250,6 +252,35 @@ testUnions() { testUnionsToString(); } +class CheckEnumCapsImpl implements regression.CheckEnumCaps { + regression.CheckEnumCapsStub _stub; + + CheckEnumCapsImpl(core.MojoMessagePipeEndpoint endpoint) { + _stub = new regression.CheckEnumCapsStub.fromEndpoint(endpoint, this); + } + + setEnumWithInternalAllCaps(regression.EnumWithInternalAllCaps e) {} +} + +checkEnumCapsIsolate(core.MojoMessagePipeEndpoint endpoint) { + new CheckEnumCapsImpl(endpoint); +} + +testCheckEnumCapsImpl() { + var pipe = new core.MojoMessagePipe(); + var client = + new regression.CheckEnumCapsProxy.fromEndpoint(pipe.endpoints[0]); + var c = new Completer(); + Isolate.spawn(checkEnumCapsIsolate, pipe.endpoints[1]).then((_) { + client.ptr.setEnumWithInternalAllCaps( + regression.EnumWithInternalAllCaps.STANDARD); + client.close().then((_) { + c.complete(null); + }); + }); + return c.future; +} + testSerializeEnum() { var constants = new structs.ScopedConstants(); constants.f4 = structs.ScopedConstantsEType.E0; @@ -258,8 +289,9 @@ testSerializeEnum() { Expect.equals(constants.f4.value, constants2.f4.value); } -testEnums() { +testEnums() async { testSerializeEnum(); + await testCheckEnumCapsImpl(); } void closingProviderIsolate(core.MojoMessagePipeEndpoint endpoint) { @@ -281,7 +313,7 @@ Future runOnClosedTest() { main() async { testSerializeStructs(); testUnions(); - testEnums(); + await testEnums(); await testCallResponse(); await testAwaitCallResponse(); await runOnClosedTest(); diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc index ef231cee1..20e4664b1 100644 --- a/mojo/edk/embedder/embedder_unittest.cc +++ b/mojo/edk/embedder/embedder_unittest.cc @@ -131,7 +131,7 @@ class ScopedTestChannel { class EmbedderTest : public testing::Test { public: - EmbedderTest() : test_io_thread_(TestIOThread::kAutoStart) {} + EmbedderTest() : test_io_thread_(TestIOThread::StartMode::AUTO) {} ~EmbedderTest() override {} protected: @@ -477,7 +477,7 @@ MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessMasterSlave) { mojo::test::MultiprocessTestHelper::client_platform_handle.Pass(); EXPECT_TRUE(client_platform_handle.is_valid()); - TestIOThread test_io_thread(TestIOThread::kAutoStart); + TestIOThread test_io_thread(TestIOThread::StartMode::AUTO); test::InitWithSimplePlatformSupport(); { @@ -672,7 +672,7 @@ MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessChannelsClient) { mojo::test::MultiprocessTestHelper::client_platform_handle.Pass(); EXPECT_TRUE(client_platform_handle.is_valid()); - TestIOThread test_io_thread(TestIOThread::kAutoStart); + TestIOThread test_io_thread(TestIOThread::StartMode::AUTO); test::InitWithSimplePlatformSupport(); { diff --git a/mojo/edk/js/BUILD.gn b/mojo/edk/js/BUILD.gn deleted file mode 100644 index ed1b81f7f..000000000 --- a/mojo/edk/js/BUILD.gn +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2014 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. - -import("../mojo_edk.gni") - -# TODO(hansmuller): The organization of tests in this directory is weird: -# * Really, js_unittests tests public stuff, so that should live in public -# and be reworked as some sort of apptest. -# * Both js_unittests and js_integration_tests should auto-generate their -# tests somehow. The .cc files are just test runner stubs, including -# explicit lists of .js files. -group("tests") { - testonly = true - deps = [ - "test:js_unittests", - "test:js_integration_tests", - ] -} - -mojo_edk_source_set("js") { - sources = [ - "core.cc", - "core.h", - "drain_data.cc", - "drain_data.h", - "handle.cc", - "handle.h", - "handle_close_observer.h", - "mojo_runner_delegate.cc", - "mojo_runner_delegate.h", - "support.cc", - "support.h", - "threading.cc", - "threading.h", - "waiting_callback.cc", - "waiting_callback.h", - ] - - public_deps = [ - "//base", - "//gin", - "//v8", - ] - - mojo_sdk_deps = [ - "mojo/public/cpp/environment", - "mojo/public/cpp/system", - ] -} - -mojo_edk_source_set("js_unittests") { - testonly = true - sources = [ - "handle_unittest.cc", - ] - - deps = [ - "//testing/gtest", - ] - - mojo_edk_deps = [ - "mojo/edk/js", - "mojo/edk/test:test_support", - ] - - mojo_sdk_deps = [ "mojo/public/cpp/system" ] -} diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc deleted file mode 100644 index cfa58a59a..000000000 --- a/mojo/edk/js/core.cc +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2014 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 "mojo/edk/js/core.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "gin/arguments.h" -#include "gin/array_buffer.h" -#include "gin/converter.h" -#include "gin/dictionary.h" -#include "gin/function_template.h" -#include "gin/handle.h" -#include "gin/object_template_builder.h" -#include "gin/per_isolate_data.h" -#include "gin/public/wrapper_info.h" -#include "gin/wrappable.h" -#include "mojo/edk/js/drain_data.h" -#include "mojo/edk/js/handle.h" - -namespace mojo { -namespace js { - -namespace { - -MojoResult CloseHandle(gin::Handle handle) { - if (!handle->get().is_valid()) - return MOJO_RESULT_INVALID_ARGUMENT; - handle->Close(); - return MOJO_RESULT_OK; -} - -gin::Dictionary WaitHandle(const gin::Arguments& args, - mojo::Handle handle, - MojoHandleSignals signals, - MojoDeadline deadline) { - v8::Isolate* isolate = args.isolate(); - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate); - - MojoHandleSignalsState signals_state; - MojoResult result = mojo::Wait(handle, signals, deadline, &signals_state); - dictionary.Set("result", result); - - mojo::WaitManyResult wmv(result, 0); - if (!wmv.AreSignalsStatesValid()) { - dictionary.Set("signalsState", v8::Null(isolate).As()); - } else { - gin::Dictionary signalsStateDict = gin::Dictionary::CreateEmpty(isolate); - signalsStateDict.Set("satisfiedSignals", signals_state.satisfied_signals); - signalsStateDict.Set("satisfiableSignals", - signals_state.satisfiable_signals); - dictionary.Set("signalsState", signalsStateDict); - } - - return dictionary; -} - -gin::Dictionary WaitMany(const gin::Arguments& args, - const std::vector& handles, - const std::vector& signals, - MojoDeadline deadline) { - v8::Isolate* isolate = args.isolate(); - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate); - - std::vector signals_states(signals.size()); - mojo::WaitManyResult wmv = - mojo::WaitMany(handles, signals, deadline, &signals_states); - dictionary.Set("result", wmv.result); - if (wmv.IsIndexValid()) { - dictionary.Set("index", wmv.index); - } else { - dictionary.Set("index", v8::Null(isolate).As()); - } - if (wmv.AreSignalsStatesValid()) { - std::vector vec; - for (size_t i = 0; i < handles.size(); ++i) { - gin::Dictionary signalsStateDict = gin::Dictionary::CreateEmpty(isolate); - signalsStateDict.Set("satisfiedSignals", - signals_states[i].satisfied_signals); - signalsStateDict.Set("satisfiableSignals", - signals_states[i].satisfiable_signals); - vec.push_back(signalsStateDict); - } - dictionary.Set("signalsState", vec); - } else { - dictionary.Set("signalsState", v8::Null(isolate).As()); - } - - return dictionary; -} - -gin::Dictionary CreateMessagePipe(const gin::Arguments& args) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT); - - MojoHandle handle0 = MOJO_HANDLE_INVALID; - MojoHandle handle1 = MOJO_HANDLE_INVALID; - MojoResult result = MOJO_RESULT_OK; - - v8::Handle options_value = args.PeekNext(); - if (options_value.IsEmpty() || options_value->IsNull() || - options_value->IsUndefined()) { - result = MojoCreateMessagePipe(NULL, &handle0, &handle1); - } else if (options_value->IsObject()) { - gin::Dictionary options_dict(args.isolate(), options_value->ToObject()); - MojoCreateMessagePipeOptions options; - // For future struct_size, we can probably infer that from the presence of - // properties in options_dict. For now, it's always 8. - options.struct_size = 8; - // Ideally these would be optional. But the interface makes it hard to - // typecheck them then. - if (!options_dict.Get("flags", &options.flags)) { - return dictionary; - } - - result = MojoCreateMessagePipe(&options, &handle0, &handle1); - } else { - return dictionary; - } - - CHECK_EQ(MOJO_RESULT_OK, result); - - dictionary.Set("result", result); - dictionary.Set("handle0", mojo::Handle(handle0)); - dictionary.Set("handle1", mojo::Handle(handle1)); - return dictionary; -} - -MojoResult WriteMessage( - mojo::Handle handle, - const gin::ArrayBufferView& buffer, - const std::vector >& handles, - MojoWriteMessageFlags flags) { - std::vector raw_handles(handles.size()); - for (size_t i = 0; i < handles.size(); ++i) - raw_handles[i] = handles[i]->get().value(); - MojoResult rv = MojoWriteMessage(handle.value(), - buffer.bytes(), - static_cast(buffer.num_bytes()), - raw_handles.empty() ? NULL : &raw_handles[0], - static_cast(raw_handles.size()), - flags); - // MojoWriteMessage takes ownership of the handles upon success, so - // release them here. - if (rv == MOJO_RESULT_OK) { - for (size_t i = 0; i < handles.size(); ++i) - ignore_result(handles[i]->release()); - } - return rv; -} - -gin::Dictionary ReadMessage(const gin::Arguments& args, - mojo::Handle handle, - MojoReadMessageFlags flags) { - uint32_t num_bytes = 0; - uint32_t num_handles = 0; - MojoResult result = MojoReadMessage( - handle.value(), NULL, &num_bytes, NULL, &num_handles, flags); - if (result != MOJO_RESULT_RESOURCE_EXHAUSTED) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", result); - return dictionary; - } - - v8::Handle array_buffer = - v8::ArrayBuffer::New(args.isolate(), num_bytes); - std::vector handles(num_handles); - - gin::ArrayBuffer buffer; - ConvertFromV8(args.isolate(), array_buffer, &buffer); - CHECK(buffer.num_bytes() == num_bytes); - - result = MojoReadMessage(handle.value(), - buffer.bytes(), - &num_bytes, - handles.empty() ? NULL : - reinterpret_cast(&handles[0]), - &num_handles, - flags); - - CHECK(buffer.num_bytes() == num_bytes); - CHECK(handles.size() == num_handles); - - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", result); - dictionary.Set("buffer", array_buffer); - dictionary.Set("handles", handles); - return dictionary; -} - -gin::Dictionary CreateDataPipe(const gin::Arguments& args) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT); - - MojoHandle producer_handle = MOJO_HANDLE_INVALID; - MojoHandle consumer_handle = MOJO_HANDLE_INVALID; - MojoResult result = MOJO_RESULT_OK; - - v8::Handle options_value = args.PeekNext(); - if (options_value.IsEmpty() || options_value->IsNull() || - options_value->IsUndefined()) { - result = MojoCreateDataPipe(NULL, &producer_handle, &consumer_handle); - } else if (options_value->IsObject()) { - gin::Dictionary options_dict(args.isolate(), options_value->ToObject()); - MojoCreateDataPipeOptions options; - // For future struct_size, we can probably infer that from the presence of - // properties in options_dict. For now, it's always 16. - options.struct_size = 16; - // Ideally these would be optional. But the interface makes it hard to - // typecheck them then. - if (!options_dict.Get("flags", &options.flags) || - !options_dict.Get("elementNumBytes", &options.element_num_bytes) || - !options_dict.Get("capacityNumBytes", &options.capacity_num_bytes)) { - return dictionary; - } - - result = MojoCreateDataPipe(&options, &producer_handle, &consumer_handle); - } else { - return dictionary; - } - - CHECK_EQ(MOJO_RESULT_OK, result); - - dictionary.Set("result", result); - dictionary.Set("producerHandle", mojo::Handle(producer_handle)); - dictionary.Set("consumerHandle", mojo::Handle(consumer_handle)); - return dictionary; -} - -gin::Dictionary WriteData(const gin::Arguments& args, - mojo::Handle handle, - const gin::ArrayBufferView& buffer, - MojoWriteDataFlags flags) { - uint32_t num_bytes = static_cast(buffer.num_bytes()); - MojoResult result = - MojoWriteData(handle.value(), buffer.bytes(), &num_bytes, flags); - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", result); - dictionary.Set("numBytes", num_bytes); - return dictionary; -} - -gin::Dictionary ReadData(const gin::Arguments& args, - mojo::Handle handle, - MojoReadDataFlags flags) { - uint32_t num_bytes = 0; - MojoResult result = MojoReadData( - handle.value(), NULL, &num_bytes, MOJO_READ_DATA_FLAG_QUERY); - if (result != MOJO_RESULT_OK) { - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", result); - return dictionary; - } - - v8::Handle array_buffer = - v8::ArrayBuffer::New(args.isolate(), num_bytes); - gin::ArrayBuffer buffer; - ConvertFromV8(args.isolate(), array_buffer, &buffer); - CHECK_EQ(num_bytes, buffer.num_bytes()); - - result = MojoReadData(handle.value(), buffer.bytes(), &num_bytes, flags); - CHECK_EQ(num_bytes, buffer.num_bytes()); - - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); - dictionary.Set("result", result); - dictionary.Set("buffer", array_buffer); - return dictionary; -} - -// Asynchronously read all of the data available for the specified data pipe -// consumer handle until the remote handle is closed or an error occurs. A -// Promise is returned whose settled value is an object like this: -// {result: core.RESULT_OK, buffer: dataArrayBuffer}. If the read failed, -// then the Promise is rejected, the result will be the actual error code, -// and the buffer will contain whatever was read before the error occurred. -// The drainData data pipe handle argument is closed automatically. - -v8::Handle DoDrainData(gin::Arguments* args, - gin::Handle handle) { - return (new DrainData(args->isolate(), handle->release()))->GetPromise(); -} - -bool IsHandle(gin::Arguments* args, v8::Handle val) { - gin::Handle ignore_handle; - return gin::Converter>::FromV8( - args->isolate(), val, &ignore_handle); -} - - -gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin }; - -} // namespace - -const char Core::kModuleName[] = "mojo/public/js/core"; - -v8::Local Core::GetModule(v8::Isolate* isolate) { - gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); - v8::Local templ = data->GetObjectTemplate( - &g_wrapper_info); - - if (templ.IsEmpty()) { - templ = - gin::ObjectTemplateBuilder(isolate) - // TODO(mpcomplete): Should these just be methods on the JS Handle - // object? - .SetMethod("close", CloseHandle) - .SetMethod("wait", WaitHandle) - .SetMethod("waitMany", WaitMany) - .SetMethod("createMessagePipe", CreateMessagePipe) - .SetMethod("writeMessage", WriteMessage) - .SetMethod("readMessage", ReadMessage) - .SetMethod("createDataPipe", CreateDataPipe) - .SetMethod("writeData", WriteData) - .SetMethod("readData", ReadData) - .SetMethod("drainData", DoDrainData) - .SetMethod("isHandle", IsHandle) - - .SetValue("RESULT_OK", MOJO_RESULT_OK) - .SetValue("RESULT_CANCELLED", MOJO_RESULT_CANCELLED) - .SetValue("RESULT_UNKNOWN", MOJO_RESULT_UNKNOWN) - .SetValue("RESULT_INVALID_ARGUMENT", MOJO_RESULT_INVALID_ARGUMENT) - .SetValue("RESULT_DEADLINE_EXCEEDED", MOJO_RESULT_DEADLINE_EXCEEDED) - .SetValue("RESULT_NOT_FOUND", MOJO_RESULT_NOT_FOUND) - .SetValue("RESULT_ALREADY_EXISTS", MOJO_RESULT_ALREADY_EXISTS) - .SetValue("RESULT_PERMISSION_DENIED", MOJO_RESULT_PERMISSION_DENIED) - .SetValue("RESULT_RESOURCE_EXHAUSTED", - MOJO_RESULT_RESOURCE_EXHAUSTED) - .SetValue("RESULT_FAILED_PRECONDITION", - MOJO_RESULT_FAILED_PRECONDITION) - .SetValue("RESULT_ABORTED", MOJO_RESULT_ABORTED) - .SetValue("RESULT_OUT_OF_RANGE", MOJO_RESULT_OUT_OF_RANGE) - .SetValue("RESULT_UNIMPLEMENTED", MOJO_RESULT_UNIMPLEMENTED) - .SetValue("RESULT_INTERNAL", MOJO_RESULT_INTERNAL) - .SetValue("RESULT_UNAVAILABLE", MOJO_RESULT_UNAVAILABLE) - .SetValue("RESULT_DATA_LOSS", MOJO_RESULT_DATA_LOSS) - .SetValue("RESULT_BUSY", MOJO_RESULT_BUSY) - .SetValue("RESULT_SHOULD_WAIT", MOJO_RESULT_SHOULD_WAIT) - - .SetValue("DEADLINE_INDEFINITE", MOJO_DEADLINE_INDEFINITE) - - .SetValue("HANDLE_SIGNAL_NONE", MOJO_HANDLE_SIGNAL_NONE) - .SetValue("HANDLE_SIGNAL_READABLE", MOJO_HANDLE_SIGNAL_READABLE) - .SetValue("HANDLE_SIGNAL_WRITABLE", MOJO_HANDLE_SIGNAL_WRITABLE) - .SetValue("HANDLE_SIGNAL_PEER_CLOSED", - MOJO_HANDLE_SIGNAL_PEER_CLOSED) - - .SetValue("CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE", - MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE) - - .SetValue("WRITE_MESSAGE_FLAG_NONE", MOJO_WRITE_MESSAGE_FLAG_NONE) - - .SetValue("READ_MESSAGE_FLAG_NONE", MOJO_READ_MESSAGE_FLAG_NONE) - .SetValue("READ_MESSAGE_FLAG_MAY_DISCARD", - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD) - - .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_NONE", - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE) - - .SetValue("WRITE_DATA_FLAG_NONE", MOJO_WRITE_DATA_FLAG_NONE) - .SetValue("WRITE_DATA_FLAG_ALL_OR_NONE", - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) - - .SetValue("READ_DATA_FLAG_NONE", MOJO_READ_DATA_FLAG_NONE) - .SetValue("READ_DATA_FLAG_ALL_OR_NONE", - MOJO_READ_DATA_FLAG_ALL_OR_NONE) - .SetValue("READ_DATA_FLAG_DISCARD", MOJO_READ_DATA_FLAG_DISCARD) - .SetValue("READ_DATA_FLAG_QUERY", MOJO_READ_DATA_FLAG_QUERY) - .SetValue("READ_DATA_FLAG_PEEK", MOJO_READ_DATA_FLAG_PEEK) - .Build(); - - data->SetObjectTemplate(&g_wrapper_info, templ); - } - - return templ->NewInstance(); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/core.h b/mojo/edk/js/core.h deleted file mode 100644 index 445fb0033..000000000 --- a/mojo/edk/js/core.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 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 MOJO_EDK_JS_CORE_H_ -#define MOJO_EDK_JS_CORE_H_ - -#include "v8/include/v8.h" - -namespace mojo { -namespace js { - -class Core { - public: - static const char kModuleName[]; - static v8::Local GetModule(v8::Isolate* isolate); -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_CORE_H_ diff --git a/mojo/edk/js/drain_data.cc b/mojo/edk/js/drain_data.cc deleted file mode 100644 index 0667c64ca..000000000 --- a/mojo/edk/js/drain_data.cc +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2014 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 "mojo/edk/js/drain_data.h" - -#include "gin/array_buffer.h" -#include "gin/converter.h" -#include "gin/dictionary.h" -#include "gin/per_context_data.h" -#include "gin/per_isolate_data.h" -#include "mojo/public/cpp/environment/environment.h" -#include "mojo/public/cpp/system/core.h" - -namespace mojo { -namespace js { - -DrainData::DrainData(v8::Isolate* isolate, mojo::Handle handle) - : isolate_(isolate), - handle_(DataPipeConsumerHandle(handle.value())), - wait_id_(0) { - - v8::Handle context(isolate_->GetCurrentContext()); - runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr(); - - WaitForData(); -} - -v8::Handle DrainData::GetPromise() { - CHECK(resolver_.IsEmpty()); - v8::Handle resolver( - v8::Promise::Resolver::New(isolate_)); - resolver_.Reset(isolate_, resolver); - return resolver->GetPromise(); -} - -DrainData::~DrainData() { - if (wait_id_) - Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_); - resolver_.Reset(); -} - -void DrainData::WaitForData() { - wait_id_ = Environment::GetDefaultAsyncWaiter()->AsyncWait( - handle_.get().value(), - MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE, - &DrainData::WaitCompleted, - this); -} - -void DrainData::DataReady(MojoResult result) { - wait_id_ = 0; - if (result != MOJO_RESULT_OK) { - DeliverData(result); - return; - } - while (result == MOJO_RESULT_OK) { - result = ReadData(); - if (result == MOJO_RESULT_SHOULD_WAIT) - WaitForData(); - else if (result != MOJO_RESULT_OK) - DeliverData(result); - } -} - -MojoResult DrainData::ReadData() { - const void* buffer; - uint32_t num_bytes = 0; - MojoResult result = BeginReadDataRaw( - handle_.get(), &buffer, &num_bytes, MOJO_READ_DATA_FLAG_NONE); - if (result != MOJO_RESULT_OK) - return result; - const char* p = static_cast(buffer); - DataBuffer* data_buffer = new DataBuffer(p, p + num_bytes); - data_buffers_.push_back(data_buffer); - return EndReadDataRaw(handle_.get(), num_bytes); -} - -void DrainData::DeliverData(MojoResult result) { - if (!runner_) { - delete this; - return; - } - - size_t total_bytes = 0; - for (unsigned i = 0; i < data_buffers_.size(); i++) - total_bytes += data_buffers_[i]->size(); - - // Create a total_bytes length ArrayBuffer return value. - gin::Runner::Scope scope(runner_.get()); - v8::Handle array_buffer = - v8::ArrayBuffer::New(isolate_, total_bytes); - gin::ArrayBuffer buffer; - ConvertFromV8(isolate_, array_buffer, &buffer); - CHECK_EQ(total_bytes, buffer.num_bytes()); - - // Copy the data_buffers into the ArrayBuffer. - char* array_buffer_ptr = static_cast(buffer.bytes()); - size_t offset = 0; - for (size_t i = 0; i < data_buffers_.size(); i++) { - size_t num_bytes = data_buffers_[i]->size(); - if (num_bytes == 0) - continue; - const char* data_buffer_ptr = &((*data_buffers_[i])[0]); - memcpy(array_buffer_ptr + offset, data_buffer_ptr, num_bytes); - offset += num_bytes; - } - - // The "settled" value of the promise always includes all of the data - // that was read before either an error occurred or the remote pipe handle - // was closed. The latter is indicated by MOJO_RESULT_FAILED_PRECONDITION. - - v8::Handle resolver( - v8::Local::New(isolate_, resolver_)); - - gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate_); - dictionary.Set("result", result); - dictionary.Set("buffer", array_buffer); - v8::Handle settled_value(ConvertToV8(isolate_, dictionary)); - - if (result == MOJO_RESULT_FAILED_PRECONDITION) - resolver->Resolve(settled_value); - else - resolver->Reject(settled_value); - - delete this; -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/drain_data.h b/mojo/edk/js/drain_data.h deleted file mode 100644 index 3c066dcfc..000000000 --- a/mojo/edk/js/drain_data.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2014 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 MOJO_EDK_JS_DRAIN_DATA_H_ -#define MOJO_EDK_JS_DRAIN_DATA_H_ - -#include "base/memory/scoped_vector.h" -#include "gin/runner.h" -#include "mojo/public/c/environment/async_waiter.h" -#include "mojo/public/cpp/system/core.h" -#include "v8/include/v8.h" - -namespace mojo { -namespace js { - -// This class is the implementation of the Mojo JavaScript core module's -// drainData() method. It is not intended to be used directly. The caller -// allocates a DrainData on the heap and returns GetPromise() to JS. The -// implementation deletes itself after reading as much data as possible -// and rejecting or resolving the Promise. - -class DrainData { - public: - // Starts waiting for data on the specified data pipe consumer handle. - // See WaitForData(). The constructor does not block. - DrainData(v8::Isolate* isolate, mojo::Handle handle); - - // Returns a Promise that will be settled when no more data can be read. - // Should be called just once on a newly allocated DrainData object. - v8::Handle GetPromise(); - - private: - ~DrainData(); - - // Registers an "async waiter" that calls DataReady() via WaitCompleted(). - void WaitForData(); - static void WaitCompleted(void* self, MojoResult result) { - static_cast(self)->DataReady(result); - } - - // Use ReadData() to read whatever is availble now on handle_ and save - // it in data_buffers_. - void DataReady(MojoResult result); - MojoResult ReadData(); - - // When the remote data pipe handle is closed, or an error occurs, deliver - // all of the buffered data to the JS Promise and then delete this. - void DeliverData(MojoResult result); - - using DataBuffer = std::vector; - - v8::Isolate* isolate_; - ScopedDataPipeConsumerHandle handle_; - MojoAsyncWaitID wait_id_; - base::WeakPtr runner_; - v8::UniquePersistent resolver_; - ScopedVector data_buffers_; -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_DRAIN_DATA_H_ diff --git a/mojo/edk/js/handle.cc b/mojo/edk/js/handle.cc deleted file mode 100644 index de8f33838..000000000 --- a/mojo/edk/js/handle.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2014 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 "mojo/edk/js/handle.h" - -#include "mojo/edk/js/handle_close_observer.h" - -namespace mojo { -namespace js { - -gin::WrapperInfo HandleWrapper::kWrapperInfo = { gin::kEmbedderNativeGin }; - -HandleWrapper::HandleWrapper(MojoHandle handle) - : handle_(mojo::Handle(handle)) { -} - -HandleWrapper::~HandleWrapper() { - NotifyCloseObservers(); -} - -void HandleWrapper::Close() { - NotifyCloseObservers(); - handle_.reset(); -} - -void HandleWrapper::AddCloseObserver(HandleCloseObserver* observer) { - close_observers_.AddObserver(observer); -} - -void HandleWrapper::RemoveCloseObserver(HandleCloseObserver* observer) { - close_observers_.RemoveObserver(observer); -} - -void HandleWrapper::NotifyCloseObservers() { - if (!handle_.is_valid()) - return; - - FOR_EACH_OBSERVER(HandleCloseObserver, close_observers_, OnWillCloseHandle()); -} - -} // namespace js -} // namespace mojo - -namespace gin { - -v8::Handle Converter::ToV8(v8::Isolate* isolate, - const mojo::Handle& val) { - if (!val.is_valid()) - return v8::Null(isolate); - return mojo::js::HandleWrapper::Create(isolate, val.value()).ToV8(); -} - -bool Converter::FromV8(v8::Isolate* isolate, - v8::Handle val, - mojo::Handle* out) { - if (val->IsNull()) { - *out = mojo::Handle(); - return true; - } - - gin::Handle handle; - if (!Converter >::FromV8( - isolate, val, &handle)) - return false; - - *out = handle->get(); - return true; -} - -v8::Handle Converter::ToV8( - v8::Isolate* isolate, mojo::MessagePipeHandle val) { - return Converter::ToV8(isolate, val); -} - -bool Converter::FromV8(v8::Isolate* isolate, - v8::Handle val, - mojo::MessagePipeHandle* out) { - return Converter::FromV8(isolate, val, out); -} - - -} // namespace gin diff --git a/mojo/edk/js/handle.h b/mojo/edk/js/handle.h deleted file mode 100644 index 36e53c8e3..000000000 --- a/mojo/edk/js/handle.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2014 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 MOJO_EDK_JS_HANDLE_H_ -#define MOJO_EDK_JS_HANDLE_H_ - -#include "base/observer_list.h" -#include "gin/converter.h" -#include "gin/handle.h" -#include "gin/wrappable.h" -#include "mojo/public/cpp/system/core.h" - -namespace mojo { -namespace js { -class HandleCloseObserver; - -// Wrapper for mojo Handles exposed to JavaScript. This ensures the Handle -// is Closed when its JS object is garbage collected. -class HandleWrapper : public gin::Wrappable { - public: - static gin::WrapperInfo kWrapperInfo; - - static gin::Handle Create(v8::Isolate* isolate, - MojoHandle handle) { - return gin::CreateHandle(isolate, new HandleWrapper(handle)); - } - - mojo::Handle get() const { return handle_.get(); } - mojo::Handle release() { return handle_.release(); } - void Close(); - - void AddCloseObserver(HandleCloseObserver* observer); - void RemoveCloseObserver(HandleCloseObserver* observer); - - protected: - HandleWrapper(MojoHandle handle); - ~HandleWrapper() override; - void NotifyCloseObservers(); - - mojo::ScopedHandle handle_; - base::ObserverList close_observers_; -}; - -} // namespace js -} // namespace mojo - -namespace gin { - -// Note: It's important to use this converter rather than the one for -// MojoHandle, since that will do a simple int32 conversion. It's unfortunate -// there's no way to prevent against accidental use. -// TODO(mpcomplete): define converters for all Handle subtypes. -template<> -struct Converter { - static v8::Handle ToV8(v8::Isolate* isolate, - const mojo::Handle& val); - static bool FromV8(v8::Isolate* isolate, v8::Handle val, - mojo::Handle* out); -}; - -template<> -struct Converter { - static v8::Handle ToV8(v8::Isolate* isolate, - mojo::MessagePipeHandle val); - static bool FromV8(v8::Isolate* isolate, - v8::Handle val, - mojo::MessagePipeHandle* out); -}; - -// We need to specialize the normal gin::Handle converter in order to handle -// converting |null| to a wrapper for an empty mojo::Handle. -template<> -struct Converter > { - static v8::Handle ToV8( - v8::Isolate* isolate, const gin::Handle& val) { - return val.ToV8(); - } - - static bool FromV8(v8::Isolate* isolate, v8::Handle val, - gin::Handle* out) { - if (val->IsNull()) { - *out = mojo::js::HandleWrapper::Create(isolate, MOJO_HANDLE_INVALID); - return true; - } - - mojo::js::HandleWrapper* object = NULL; - if (!Converter::FromV8(isolate, val, &object)) { - return false; - } - *out = gin::Handle(val, object); - return true; - } -}; - -} // namespace gin - -#endif // MOJO_EDK_JS_HANDLE_H_ diff --git a/mojo/edk/js/handle_close_observer.h b/mojo/edk/js/handle_close_observer.h deleted file mode 100644 index 3e537fd7d..000000000 --- a/mojo/edk/js/handle_close_observer.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 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 MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_ -#define MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_ - -namespace mojo { -namespace js { - -class HandleCloseObserver { - public: - virtual void OnWillCloseHandle() = 0; - - protected: - virtual ~HandleCloseObserver() {} -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_ diff --git a/mojo/edk/js/handle_unittest.cc b/mojo/edk/js/handle_unittest.cc deleted file mode 100644 index 53f474edb..000000000 --- a/mojo/edk/js/handle_unittest.cc +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2014 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 "base/macros.h" -#include "mojo/edk/js/handle.h" -#include "mojo/edk/js/handle_close_observer.h" -#include "mojo/public/cpp/system/core.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace js { - -class HandleWrapperTest : public testing::Test, - public HandleCloseObserver { - public: - HandleWrapperTest() : closes_observed_(0) {} - - void OnWillCloseHandle() override { closes_observed_++; } - - protected: - int closes_observed_; - - private: - DISALLOW_COPY_AND_ASSIGN(HandleWrapperTest); -}; - -class TestHandleWrapper : public HandleWrapper { - public: - explicit TestHandleWrapper(MojoHandle handle) : HandleWrapper(handle) {} - - private: - DISALLOW_COPY_AND_ASSIGN(TestHandleWrapper); -}; - -// Test that calling Close() on a HandleWrapper for an invalid handle does not -// notify observers. -TEST_F(HandleWrapperTest, CloseWithInvalidHandle) { - { - TestHandleWrapper wrapper(MOJO_HANDLE_INVALID); - wrapper.AddCloseObserver(this); - ASSERT_EQ(0, closes_observed_); - wrapper.Close(); - EXPECT_EQ(0, closes_observed_); - } - EXPECT_EQ(0, closes_observed_); -} - -// Test that destroying a HandleWrapper for an invalid handle does not notify -// observers. -TEST_F(HandleWrapperTest, DestroyWithInvalidHandle) { - { - TestHandleWrapper wrapper(MOJO_HANDLE_INVALID); - wrapper.AddCloseObserver(this); - ASSERT_EQ(0, closes_observed_); - } - EXPECT_EQ(0, closes_observed_); -} - -// Test that calling Close on a HandleWrapper for a valid handle notifies -// observers once. -TEST_F(HandleWrapperTest, CloseWithValidHandle) { - { - mojo::MessagePipe pipe; - TestHandleWrapper wrapper(pipe.handle0.release().value()); - wrapper.AddCloseObserver(this); - ASSERT_EQ(0, closes_observed_); - wrapper.Close(); - EXPECT_EQ(1, closes_observed_); - // Check that calling close again doesn't notify observers. - wrapper.Close(); - EXPECT_EQ(1, closes_observed_); - } - // Check that destroying a closed HandleWrapper doesn't notify observers. - EXPECT_EQ(1, closes_observed_); -} - -// Test that destroying a HandleWrapper for a valid handle notifies observers. -TEST_F(HandleWrapperTest, DestroyWithValidHandle) { - { - mojo::MessagePipe pipe; - TestHandleWrapper wrapper(pipe.handle0.release().value()); - wrapper.AddCloseObserver(this); - ASSERT_EQ(0, closes_observed_); - } - EXPECT_EQ(1, closes_observed_); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/mojo_runner_delegate.cc b/mojo/edk/js/mojo_runner_delegate.cc deleted file mode 100644 index 152b12cf3..000000000 --- a/mojo/edk/js/mojo_runner_delegate.cc +++ /dev/null @@ -1,78 +0,0 @@ -// 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 "mojo/edk/js/mojo_runner_delegate.h" - -#include "base/bind.h" -#include "base/path_service.h" -#include "gin/converter.h" -#include "gin/modules/console.h" -#include "gin/modules/module_registry.h" -#include "gin/modules/timer.h" -#include "gin/try_catch.h" -#include "mojo/edk/js/core.h" -#include "mojo/edk/js/handle.h" -#include "mojo/edk/js/support.h" -#include "mojo/edk/js/threading.h" - -namespace mojo { -namespace js { - -namespace { - -// TODO(abarth): Rather than loading these modules from the file system, we -// should load them from the network via Mojo IPC. -std::vector GetModuleSearchPaths() { - std::vector search_paths(2); - PathService::Get(base::DIR_SOURCE_ROOT, &search_paths[0]); - PathService::Get(base::DIR_EXE, &search_paths[1]); - search_paths[1] = search_paths[1].AppendASCII("gen"); - return search_paths; -} - -void StartCallback(base::WeakPtr runner, - MojoHandle pipe, - v8::Handle module) { - v8::Isolate* isolate = runner->GetContextHolder()->isolate(); - v8::Handle start; - CHECK(gin::ConvertFromV8(isolate, module, &start)); - - v8::Handle args[] = { - gin::ConvertToV8(isolate, Handle(pipe)) }; - runner->Call(start, runner->global(), 1, args); -} - -} // namespace - -MojoRunnerDelegate::MojoRunnerDelegate() - : ModuleRunnerDelegate(GetModuleSearchPaths()) { - AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule); - AddBuiltinModule(gin::TimerModule::kName, gin::TimerModule::GetModule); - AddBuiltinModule(js::Core::kModuleName, js::Core::GetModule); - AddBuiltinModule(js::Support::kModuleName, js::Support::GetModule); - AddBuiltinModule(js::Threading::kModuleName, js::Threading::GetModule); -} - -MojoRunnerDelegate::~MojoRunnerDelegate() { -} - -void MojoRunnerDelegate::Start(gin::Runner* runner, - MojoHandle pipe, - const std::string& module) { - gin::Runner::Scope scope(runner); - gin::ModuleRegistry* registry = - gin::ModuleRegistry::From(runner->GetContextHolder()->context()); - registry->LoadModule(runner->GetContextHolder()->isolate(), module, - base::Bind(StartCallback, runner->GetWeakPtr(), pipe)); - AttemptToLoadMoreModules(runner); -} - -void MojoRunnerDelegate::UnhandledException(gin::ShellRunner* runner, - gin::TryCatch& try_catch) { - gin::ModuleRunnerDelegate::UnhandledException(runner, try_catch); - LOG(ERROR) << try_catch.GetStackTrace(); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/mojo_runner_delegate.h b/mojo/edk/js/mojo_runner_delegate.h deleted file mode 100644 index 423eefb1b..000000000 --- a/mojo/edk/js/mojo_runner_delegate.h +++ /dev/null @@ -1,33 +0,0 @@ -// 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 MOJO_EDK_JS_MOJO_RUNNER_DELEGATE_H_ -#define MOJO_EDK_JS_MOJO_RUNNER_DELEGATE_H_ - -#include "base/macros.h" -#include "gin/modules/module_runner_delegate.h" -#include "mojo/public/c/system/core.h" - -namespace mojo { -namespace js { - -class MojoRunnerDelegate : public gin::ModuleRunnerDelegate { - public: - MojoRunnerDelegate(); - ~MojoRunnerDelegate() override; - - void Start(gin::Runner* runner, MojoHandle pipe, const std::string& module); - - private: - // From ModuleRunnerDelegate: - void UnhandledException(gin::ShellRunner* runner, - gin::TryCatch& try_catch) override; - - DISALLOW_COPY_AND_ASSIGN(MojoRunnerDelegate); -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_MOJO_RUNNER_DELEGATE_H_ diff --git a/mojo/edk/js/support.cc b/mojo/edk/js/support.cc deleted file mode 100644 index 7d4402428..000000000 --- a/mojo/edk/js/support.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2014 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 "mojo/edk/js/support.h" - -#include "base/bind.h" -#include "gin/arguments.h" -#include "gin/converter.h" -#include "gin/function_template.h" -#include "gin/object_template_builder.h" -#include "gin/per_isolate_data.h" -#include "gin/public/wrapper_info.h" -#include "gin/wrappable.h" -#include "mojo/edk/js/handle.h" -#include "mojo/edk/js/waiting_callback.h" -#include "mojo/public/cpp/system/core.h" - -namespace mojo { -namespace js { - -namespace { - -WaitingCallback* AsyncWait(const gin::Arguments& args, - gin::Handle handle, - MojoHandleSignals signals, - v8::Handle callback) { - return WaitingCallback::Create(args.isolate(), callback, handle, signals) - .get(); -} - -void CancelWait(WaitingCallback* waiting_callback) { - waiting_callback->Cancel(); -} - -gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin }; - -} // namespace - -const char Support::kModuleName[] = "mojo/public/js/support"; - -v8::Local Support::GetModule(v8::Isolate* isolate) { - gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); - v8::Local templ = data->GetObjectTemplate( - &g_wrapper_info); - - if (templ.IsEmpty()) { - templ = gin::ObjectTemplateBuilder(isolate) - .SetMethod("asyncWait", AsyncWait) - .SetMethod("cancelWait", CancelWait) - .Build(); - - data->SetObjectTemplate(&g_wrapper_info, templ); - } - - return templ->NewInstance(); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/support.h b/mojo/edk/js/support.h deleted file mode 100644 index b49dd237a..000000000 --- a/mojo/edk/js/support.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 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 MOJO_EDK_JS_SUPPORT_H_ -#define MOJO_EDK_JS_SUPPORT_H_ - -#include "v8/include/v8.h" - -namespace mojo { -namespace js { - -class Support { - public: - static const char kModuleName[]; - static v8::Local GetModule(v8::Isolate* isolate); -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_SUPPORT_H_ diff --git a/mojo/edk/js/test/BUILD.gn b/mojo/edk/js/test/BUILD.gn deleted file mode 100644 index d5439121b..000000000 --- a/mojo/edk/js/test/BUILD.gn +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2014 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. - -import("//testing/test.gni") - -test("js_unittests") { - deps = [ - "../../js", - "../../js:js_unittests", - "../../test:run_all_unittests", - "../../test:test_support", - "../../../public/cpp/environment", - "../../../public/cpp/system", - "../../../public/cpp/utility", - "../../../public/interfaces/bindings/tests:test_interfaces", - "../../../public/interfaces/bindings/tests:test_interfaces_experimental", - "//base", - "//gin:gin_test", - "//mojo/environment:chromium", - ] - - sources = [ - "run_js_tests.cc", - ] -} - -test("js_integration_tests") { - deps = [ - "../../js", - "../../js/tests:js_to_cpp_tests", - "../../test:run_all_unittests", - "../../test:test_support", - "../../../public/cpp/bindings", - "../../../public/interfaces/bindings/tests:test_interfaces", - "//base", - "//gin:gin_test", - "//mojo/environment:chromium", - ] - - sources = [ - "run_js_integration_tests.cc", - ] -} diff --git a/mojo/edk/js/test/hexdump.js b/mojo/edk/js/test/hexdump.js deleted file mode 100644 index b36c47f33..000000000 --- a/mojo/edk/js/test/hexdump.js +++ /dev/null @@ -1,34 +0,0 @@ -// 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. - -define(function() { - function hexify(value, length) { - var hex = value.toString(16); - while (hex.length < length) - hex = "0" + hex; - return hex; - } - - function dumpArray(bytes) { - var dumped = ""; - for (var i = 0; i < bytes.length; ++i) { - dumped += hexify(bytes[i], 2); - - if (i % 16 == 15) { - dumped += "\n"; - continue; - } - - if (i % 2 == 1) - dumped += " "; - if (i % 8 == 7) - dumped += " "; - } - return dumped; - } - - var exports = {}; - exports.dumpArray = dumpArray; - return exports; -}); diff --git a/mojo/edk/js/test/run_js_integration_tests.cc b/mojo/edk/js/test/run_js_integration_tests.cc deleted file mode 100644 index 1a6f1d642..000000000 --- a/mojo/edk/js/test/run_js_integration_tests.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2014 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 "base/files/file_path.h" -#include "base/path_service.h" -#include "gin/modules/console.h" -#include "gin/modules/module_registry.h" -#include "gin/modules/timer.h" -#include "gin/test/file_runner.h" -#include "gin/test/gtest.h" -#include "mojo/edk/js/core.h" -#include "mojo/edk/js/support.h" -#include "mojo/edk/js/threading.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace js { -namespace { - -class TestRunnerDelegate : public gin::FileRunnerDelegate { - public: - TestRunnerDelegate() { - AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule); - AddBuiltinModule(Core::kModuleName, Core::GetModule); - AddBuiltinModule(gin::TimerModule::kName, gin::TimerModule::GetModule); - AddBuiltinModule(Threading::kModuleName, Threading::GetModule); - } - private: - DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate); -}; - -void RunTest(std::string test, bool addSupportModule) { - base::FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); - path = path.AppendASCII("mojo") - .AppendASCII("edk") - .AppendASCII("js") - .AppendASCII("tests") - .AppendASCII(test); - TestRunnerDelegate delegate; - if (addSupportModule) - delegate.AddBuiltinModule(Support::kModuleName, Support::GetModule); - gin::RunTestFromFile(path, &delegate, true); -} - -TEST(JSTest, connection) { - RunTest("connection_tests.js", false); -} - -TEST(JSTest, sample_service) { - RunTest("sample_service_tests.js", true); -} - -} // namespace -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/test/run_js_tests.cc b/mojo/edk/js/test/run_js_tests.cc deleted file mode 100644 index b1155072f..000000000 --- a/mojo/edk/js/test/run_js_tests.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2014 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 "base/files/file_path.h" -#include "base/path_service.h" -#include "gin/modules/console.h" -#include "gin/modules/module_registry.h" -#include "gin/test/file_runner.h" -#include "gin/test/gtest.h" -#include "mojo/edk/js/core.h" -#include "mojo/edk/js/support.h" -#include "mojo/public/cpp/environment/environment.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace js { -namespace { - -class TestRunnerDelegate : public gin::FileRunnerDelegate { - public: - TestRunnerDelegate() { - AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule); - AddBuiltinModule(Core::kModuleName, Core::GetModule); - AddBuiltinModule(Support::kModuleName, Support::GetModule); - } - - private: - DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate); -}; - -void RunTest(std::string test, bool run_until_idle) { - base::FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); - path = path.AppendASCII("mojo") - .AppendASCII("public") - .AppendASCII("js") - .AppendASCII(test); - TestRunnerDelegate delegate; - gin::RunTestFromFile(path, &delegate, run_until_idle); -} - -// TODO(abarth): Should we autogenerate these stubs from GYP? -TEST(JSTest, core) { - RunTest("core_unittests.js", true); -} - -TEST(JSTest, codec) { - RunTest("codec_unittests.js", true); -} - -TEST(JSTest, struct) { - RunTest("struct_unittests.js", true); -} - -TEST(JSTest, union) { - RunTest("union_unittests.js", true); -} - -TEST(JSTest, validation) { - RunTest("validation_unittests.js", true); -} - -} // namespace -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/tests/BUILD.gn b/mojo/edk/js/tests/BUILD.gn deleted file mode 100644 index 0d9b3be00..000000000 --- a/mojo/edk/js/tests/BUILD.gn +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2014 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. - -import("../../mojo_edk.gni") -import("../../../public/tools/bindings/mojom.gni") - -mojo_edk_source_set("js_to_cpp_tests") { - testonly = true - - deps = [ - ":js_to_cpp_bindings", - "//gin:gin_test", - ] - - mojo_edk_deps = [ - "mojo/edk/js", - "mojo/edk/test:test_support", - ] - - mojo_sdk_deps = [ - "mojo/public/cpp/bindings", - "mojo/public/cpp/system", - "mojo/public/interfaces/bindings/tests:test_interfaces", - "mojo/public/interfaces/bindings/tests:test_interfaces_experimental", - ] - - sources = [ - "js_to_cpp_tests.cc", - ] -} - -mojom("js_to_cpp_bindings") { - sources = [ - "js_to_cpp.mojom", - ] -} diff --git a/mojo/edk/js/tests/connection_tests.js b/mojo/edk/js/tests/connection_tests.js deleted file mode 100644 index ff59aeb0b..000000000 --- a/mojo/edk/js/tests/connection_tests.js +++ /dev/null @@ -1,240 +0,0 @@ -// 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. - -// Mock out the support module to avoid depending on the message loop. -define("mojo/public/js/support", ["timer"], function(timer) { - var waitingCallbacks = []; - - function WaitCookie(id) { - this.id = id; - } - - function asyncWait(handle, flags, callback) { - var id = waitingCallbacks.length; - waitingCallbacks.push(callback); - return new WaitCookie(id); - } - - function cancelWait(cookie) { - waitingCallbacks[cookie.id] = null; - } - - function numberOfWaitingCallbacks() { - var count = 0; - for (var i = 0; i < waitingCallbacks.length; ++i) { - if (waitingCallbacks[i]) - ++count; - } - return count; - } - - function pumpOnce(result) { - var callbacks = waitingCallbacks; - waitingCallbacks = []; - for (var i = 0; i < callbacks.length; ++i) { - if (callbacks[i]) - callbacks[i](result); - } - } - - // Queue up a pumpOnce call to execute after the stack unwinds. Use - // this to trigger a pump after all Promises are executed. - function queuePump(result) { - timer.createOneShot(0, pumpOnce.bind(undefined, result)); - } - - var exports = {}; - exports.asyncWait = asyncWait; - exports.cancelWait = cancelWait; - exports.numberOfWaitingCallbacks = numberOfWaitingCallbacks; - exports.pumpOnce = pumpOnce; - exports.queuePump = queuePump; - return exports; -}); - -define([ - "gin/test/expect", - "mojo/public/js/support", - "mojo/public/js/core", - "mojo/public/js/connection", - "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom", - "mojo/public/interfaces/bindings/tests/sample_service.mojom", - "mojo/public/js/threading", - "gc", -], function(expect, - mockSupport, - core, - connection, - sample_interfaces, - sample_service, - threading, - gc) { - testClientServer(); - testWriteToClosedPipe(); - testRequestResponse().then(function() { - this.result = "PASS"; - gc.collectGarbage(); // should not crash - threading.quit(); - }.bind(this)).catch(function(e) { - this.result = "FAIL: " + (e.stack || e); - threading.quit(); - }.bind(this)); - - function createPeerConnection(handle, stubClass, proxyClass) { - var c = new connection.Connection(handle, stubClass, proxyClass); - if (c.local) - c.local.peer = c.remote; - if (c.remote) - c.remote.peer = c.local; - return c; - } - - function testClientServer() { - var receivedFrobinate = false; - - // ServiceImpl ------------------------------------------------------------ - - function ServiceImpl() { - } - - ServiceImpl.prototype = Object.create( - sample_service.Service.stubClass.prototype); - - ServiceImpl.prototype.frobinate = function(foo, baz, port) { - receivedFrobinate = true; - - expect(foo.name).toBe("Example name"); - expect(baz).toBeTruthy(); - expect(core.close(port)).toBe(core.RESULT_OK); - - return Promise.resolve(42); - }; - - var pipe = core.createMessagePipe(); - var anotherPipe = core.createMessagePipe(); - var sourcePipe = core.createMessagePipe(); - - var connection0 = createPeerConnection( - pipe.handle0, ServiceImpl); - - var connection1 = createPeerConnection( - pipe.handle1, undefined, sample_service.Service.proxyClass); - - var foo = new sample_service.Foo(); - foo.bar = new sample_service.Bar(); - foo.name = "Example name"; - foo.source = sourcePipe.handle0; - connection1.remote.frobinate(foo, true, anotherPipe.handle0); - - mockSupport.pumpOnce(core.RESULT_OK); - - expect(receivedFrobinate).toBeTruthy(); - - connection0.close(); - connection1.close(); - - expect(mockSupport.numberOfWaitingCallbacks()).toBe(0); - - // sourcePipe.handle0 was closed automatically when sent over IPC. - expect(core.close(sourcePipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT); - // sourcePipe.handle1 hasn't been closed yet. - expect(core.close(sourcePipe.handle1)).toBe(core.RESULT_OK); - - // anotherPipe.handle0 was closed automatically when sent over IPC. - expect(core.close(anotherPipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT); - // anotherPipe.handle1 hasn't been closed yet. - expect(core.close(anotherPipe.handle1)).toBe(core.RESULT_OK); - - // The Connection object is responsible for closing these handles. - expect(core.close(pipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT); - expect(core.close(pipe.handle1)).toBe(core.RESULT_INVALID_ARGUMENT); - } - - function testWriteToClosedPipe() { - var pipe = core.createMessagePipe(); - - var connection1 = createPeerConnection( - pipe.handle1, function() {}, sample_service.Service.proxyClass); - - // Close the other end of the pipe. - core.close(pipe.handle0); - - // Not observed yet because we haven't pumped events yet. - expect(connection1.encounteredError()).toBeFalsy(); - - var foo = new sample_service.Foo(); - foo.bar = new sample_service.Bar(); - connection1.remote.frobinate(null, true, null); - - // Write failures are not reported. - expect(connection1.encounteredError()).toBeFalsy(); - - // Pump events, and then we should start observing the closed pipe. - mockSupport.pumpOnce(core.RESULT_OK); - - expect(connection1.encounteredError()).toBeTruthy(); - - connection1.close(); - } - - function testRequestResponse() { - - // ProviderImpl ------------------------------------------------------------ - - function ProviderImpl() { - } - - ProviderImpl.prototype = - Object.create(sample_interfaces.Provider.stubClass.prototype); - - ProviderImpl.prototype.echoString = function(a) { - mockSupport.queuePump(core.RESULT_OK); - return Promise.resolve({a: a}); - }; - - ProviderImpl.prototype.echoStrings = function(a, b) { - mockSupport.queuePump(core.RESULT_OK); - return Promise.resolve({a: a, b: b}); - }; - - var pipe = core.createMessagePipe(); - - var connection0 = createPeerConnection( - pipe.handle0, - ProviderImpl); - - var connection1 = createPeerConnection( - pipe.handle1, - undefined, - sample_interfaces.Provider.proxyClass); - - var origReadMessage = core.readMessage; - // echoString - mockSupport.queuePump(core.RESULT_OK); - return connection1.remote.echoString("hello").then(function(response) { - expect(response.a).toBe("hello"); - }).then(function() { - // echoStrings - mockSupport.queuePump(core.RESULT_OK); - return connection1.remote.echoStrings("hello", "world"); - }).then(function(response) { - expect(response.a).toBe("hello"); - expect(response.b).toBe("world"); - }).then(function() { - // Mock a read failure, expect it to fail. - core.readMessage = function() { - return { result: core.RESULT_UNKNOWN }; - }; - mockSupport.queuePump(core.RESULT_OK); - return connection1.remote.echoString("goodbye"); - }).then(function() { - throw Error("Expected echoString to fail."); - }, function(error) { - expect(error.message).toBe("Connection error: " + core.RESULT_UNKNOWN); - - // Clean up. - core.readMessage = origReadMessage; - }); - } -}); diff --git a/mojo/edk/js/tests/js_to_cpp.mojom b/mojo/edk/js/tests/js_to_cpp.mojom deleted file mode 100644 index 688b22b3d..000000000 --- a/mojo/edk/js/tests/js_to_cpp.mojom +++ /dev/null @@ -1,54 +0,0 @@ -module js_to_cpp; - -// This struct encompasses all of the basic types, so that they -// may be sent from C++ to JS and back for validation. -struct EchoArgs { - int64 si64; - int32 si32; - int16 si16; - int8 si8; - uint64 ui64; - uint32 ui32; - uint16 ui16; - uint8 ui8; - float float_val; - float float_inf; - float float_nan; - double double_val; - double double_inf; - double double_nan; - string? name; - array? string_array; - handle? message_handle; - handle? data_handle; -}; - -struct EchoArgsList { - EchoArgsList? next; - EchoArgs? item; -}; - -// Note: For messages which control test flow, pick numbers that are unlikely -// to be hit as a result of our deliberate corruption of response messages. -interface CppSide { - // Sent for all tests to notify that the JS side is now ready. - StartTest@88888888(); - - // Indicates end for echo, bit-flip, and back-pointer tests. - TestFinished@99999999(); - - // Responses from specific tests. - PingResponse(); - EchoResponse(EchoArgsList list); - BitFlipResponse(EchoArgsList arg); - BackPointerResponse(EchoArgsList arg); -}; - -interface JsSide { - SetCppSide(CppSide cpp); - - Ping(); - Echo(int32 numIterations, EchoArgs arg); - BitFlip(EchoArgs arg); - BackPointer(EchoArgs arg); -}; diff --git a/mojo/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc deleted file mode 100644 index 0578afd1d..000000000 --- a/mojo/edk/js/tests/js_to_cpp_tests.cc +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright 2014 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 - -#include "base/at_exit.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "gin/array_buffer.h" -#include "gin/public/isolate_holder.h" -#include "mojo/edk/js/mojo_runner_delegate.h" -#include "mojo/edk/js/tests/js_to_cpp.mojom.h" -#include "mojo/edk/test/test_utils.h" -#include "mojo/public/cpp/bindings/binding.h" -#include "mojo/public/cpp/system/core.h" -#include "mojo/public/cpp/system/macros.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace js { - -// Global value updated by some checks to prevent compilers from optimizing -// reads out of existence. -uint32_t g_waste_accumulator = 0; - -namespace { - -// Negative numbers with different values in each byte, the last of -// which can survive promotion to double and back. -const int8_t kExpectedInt8Value = -65; -const int16_t kExpectedInt16Value = -16961; -const int32_t kExpectedInt32Value = -1145258561; -const int64_t kExpectedInt64Value = -77263311946305LL; - -// Positive numbers with different values in each byte, the last of -// which can survive promotion to double and back. -const uint8_t kExpectedUInt8Value = 65; -const uint16_t kExpectedUInt16Value = 16961; -const uint32_t kExpectedUInt32Value = 1145258561; -const uint64_t kExpectedUInt64Value = 77263311946305LL; - -// Double/float values, including special case constants. -const double kExpectedDoubleVal = 3.14159265358979323846; -const double kExpectedDoubleInf = std::numeric_limits::infinity(); -const double kExpectedDoubleNan = std::numeric_limits::quiet_NaN(); -const float kExpectedFloatVal = static_cast(kExpectedDoubleVal); -const float kExpectedFloatInf = std::numeric_limits::infinity(); -const float kExpectedFloatNan = std::numeric_limits::quiet_NaN(); - -// NaN has the property that it is not equal to itself. -#define EXPECT_NAN(x) EXPECT_NE(x, x) - -void CheckDataPipe(MojoHandle data_pipe_handle) { - unsigned char buffer[100]; - uint32_t buffer_size = static_cast(sizeof(buffer)); - MojoResult result = MojoReadData( - data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE); - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(64u, buffer_size); - for (int i = 0; i < 64; ++i) { - EXPECT_EQ(i, buffer[i]); - } -} - -void CheckMessagePipe(MojoHandle message_pipe_handle) { - unsigned char buffer[100]; - uint32_t buffer_size = static_cast(sizeof(buffer)); - MojoResult result = MojoReadMessage( - message_pipe_handle, buffer, &buffer_size, 0, 0, 0); - EXPECT_EQ(MOJO_RESULT_OK, result); - EXPECT_EQ(64u, buffer_size); - for (int i = 0; i < 64; ++i) { - EXPECT_EQ(255 - i, buffer[i]); - } -} - -js_to_cpp::EchoArgsPtr BuildSampleEchoArgs() { - js_to_cpp::EchoArgsPtr args(js_to_cpp::EchoArgs::New()); - args->si64 = kExpectedInt64Value; - args->si32 = kExpectedInt32Value; - args->si16 = kExpectedInt16Value; - args->si8 = kExpectedInt8Value; - args->ui64 = kExpectedUInt64Value; - args->ui32 = kExpectedUInt32Value; - args->ui16 = kExpectedUInt16Value; - args->ui8 = kExpectedUInt8Value; - args->float_val = kExpectedFloatVal; - args->float_inf = kExpectedFloatInf; - args->float_nan = kExpectedFloatNan; - args->double_val = kExpectedDoubleVal; - args->double_inf = kExpectedDoubleInf; - args->double_nan = kExpectedDoubleNan; - args->name = "coming"; - Array string_array(3); - string_array[0] = "one"; - string_array[1] = "two"; - string_array[2] = "three"; - args->string_array = string_array.Pass(); - return args.Pass(); -} - -void CheckSampleEchoArgs(const js_to_cpp::EchoArgs& arg) { - EXPECT_EQ(kExpectedInt64Value, arg.si64); - EXPECT_EQ(kExpectedInt32Value, arg.si32); - EXPECT_EQ(kExpectedInt16Value, arg.si16); - EXPECT_EQ(kExpectedInt8Value, arg.si8); - EXPECT_EQ(kExpectedUInt64Value, arg.ui64); - EXPECT_EQ(kExpectedUInt32Value, arg.ui32); - EXPECT_EQ(kExpectedUInt16Value, arg.ui16); - EXPECT_EQ(kExpectedUInt8Value, arg.ui8); - EXPECT_EQ(kExpectedFloatVal, arg.float_val); - EXPECT_EQ(kExpectedFloatInf, arg.float_inf); - EXPECT_NAN(arg.float_nan); - EXPECT_EQ(kExpectedDoubleVal, arg.double_val); - EXPECT_EQ(kExpectedDoubleInf, arg.double_inf); - EXPECT_NAN(arg.double_nan); - EXPECT_EQ(std::string("coming"), arg.name.get()); - EXPECT_EQ(std::string("one"), arg.string_array[0].get()); - EXPECT_EQ(std::string("two"), arg.string_array[1].get()); - EXPECT_EQ(std::string("three"), arg.string_array[2].get()); - CheckDataPipe(arg.data_handle.get().value()); - CheckMessagePipe(arg.message_handle.get().value()); -} - -void CheckSampleEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) { - if (list.is_null()) - return; - CheckSampleEchoArgs(*list->item); - CheckSampleEchoArgsList(list->next); -} - -// More forgiving checks are needed in the face of potentially corrupt -// messages. The values don't matter so long as all accesses are within -// bounds. -void CheckCorruptedString(const String& arg) { - if (arg.is_null()) - return; - for (size_t i = 0; i < arg.size(); ++i) - g_waste_accumulator += arg[i]; -} - -void CheckCorruptedStringArray(const Array& string_array) { - if (string_array.is_null()) - return; - for (size_t i = 0; i < string_array.size(); ++i) - CheckCorruptedString(string_array[i]); -} - -void CheckCorruptedDataPipe(MojoHandle data_pipe_handle) { - unsigned char buffer[100]; - uint32_t buffer_size = static_cast(sizeof(buffer)); - MojoResult result = MojoReadData( - data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE); - if (result != MOJO_RESULT_OK) - return; - for (uint32_t i = 0; i < buffer_size; ++i) - g_waste_accumulator += buffer[i]; -} - -void CheckCorruptedMessagePipe(MojoHandle message_pipe_handle) { - unsigned char buffer[100]; - uint32_t buffer_size = static_cast(sizeof(buffer)); - MojoResult result = MojoReadMessage( - message_pipe_handle, buffer, &buffer_size, 0, 0, 0); - if (result != MOJO_RESULT_OK) - return; - for (uint32_t i = 0; i < buffer_size; ++i) - g_waste_accumulator += buffer[i]; -} - -void CheckCorruptedEchoArgs(const js_to_cpp::EchoArgsPtr& arg) { - if (arg.is_null()) - return; - CheckCorruptedString(arg->name); - CheckCorruptedStringArray(arg->string_array); - if (arg->data_handle.is_valid()) - CheckCorruptedDataPipe(arg->data_handle.get().value()); - if (arg->message_handle.is_valid()) - CheckCorruptedMessagePipe(arg->message_handle.get().value()); -} - -void CheckCorruptedEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) { - if (list.is_null()) - return; - CheckCorruptedEchoArgs(list->item); - CheckCorruptedEchoArgsList(list->next); -} - -// Base Provider implementation class. It's expected that tests subclass and -// override the appropriate Provider functions. When test is done quit the -// run_loop(). -class CppSideConnection : public js_to_cpp::CppSide { - public: - CppSideConnection() - : run_loop_(nullptr), - js_side_(nullptr), - mishandled_messages_(0), - binding_(this) {} - ~CppSideConnection() override {} - - void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; } - base::RunLoop* run_loop() { return run_loop_; } - - void set_js_side(js_to_cpp::JsSide* js_side) { js_side_ = js_side; } - js_to_cpp::JsSide* js_side() { return js_side_; } - - void Bind(InterfaceRequest request) { - binding_.Bind(request.Pass()); - // Keep the pipe open even after validation errors. - binding_.internal_router()->EnableTestingMode(); - } - - // js_to_cpp::CppSide: - void StartTest() override { NOTREACHED(); } - - void TestFinished() override { NOTREACHED(); } - - void PingResponse() override { mishandled_messages_ += 1; } - - void EchoResponse(js_to_cpp::EchoArgsListPtr list) override { - mishandled_messages_ += 1; - } - - void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override { - mishandled_messages_ += 1; - } - - void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override { - mishandled_messages_ += 1; - } - - protected: - base::RunLoop* run_loop_; - js_to_cpp::JsSide* js_side_; - int mishandled_messages_; - mojo::Binding binding_; - - private: - DISALLOW_COPY_AND_ASSIGN(CppSideConnection); -}; - -// Trivial test to verify a message sent from JS is received. -class PingCppSideConnection : public CppSideConnection { - public: - PingCppSideConnection() : got_message_(false) {} - ~PingCppSideConnection() override {} - - // js_to_cpp::CppSide: - void StartTest() override { js_side_->Ping(); } - - void PingResponse() override { - got_message_ = true; - run_loop()->Quit(); - } - - bool DidSucceed() { - return got_message_ && !mishandled_messages_; - } - - private: - bool got_message_; - DISALLOW_COPY_AND_ASSIGN(PingCppSideConnection); -}; - -// Test that parameters are passed with correct values. -class EchoCppSideConnection : public CppSideConnection { - public: - EchoCppSideConnection() : - message_count_(0), - termination_seen_(false) { - } - ~EchoCppSideConnection() override {} - - // js_to_cpp::CppSide: - void StartTest() override { - js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs()); - } - - void EchoResponse(js_to_cpp::EchoArgsListPtr list) override { - const js_to_cpp::EchoArgsPtr& special_arg = list->item; - message_count_ += 1; - EXPECT_EQ(-1, special_arg->si64); - EXPECT_EQ(-1, special_arg->si32); - EXPECT_EQ(-1, special_arg->si16); - EXPECT_EQ(-1, special_arg->si8); - EXPECT_EQ(std::string("going"), special_arg->name.To()); - CheckSampleEchoArgsList(list->next); - } - - void TestFinished() override { - termination_seen_ = true; - run_loop()->Quit(); - } - - bool DidSucceed() { - return termination_seen_ && - !mishandled_messages_ && - message_count_ == kExpectedMessageCount; - } - - private: - static const int kExpectedMessageCount = 10; - int message_count_; - bool termination_seen_; - DISALLOW_COPY_AND_ASSIGN(EchoCppSideConnection); -}; - -// Test that corrupted messages don't wreak havoc. -class BitFlipCppSideConnection : public CppSideConnection { - public: - BitFlipCppSideConnection() : termination_seen_(false) {} - ~BitFlipCppSideConnection() override {} - - // js_to_cpp::CppSide: - void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); } - - void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override { - CheckCorruptedEchoArgsList(list); - } - - void TestFinished() override { - termination_seen_ = true; - run_loop()->Quit(); - } - - bool DidSucceed() { - return termination_seen_; - } - - private: - bool termination_seen_; - DISALLOW_COPY_AND_ASSIGN(BitFlipCppSideConnection); -}; - -// Test that severely random messages don't wreak havoc. -class BackPointerCppSideConnection : public CppSideConnection { - public: - BackPointerCppSideConnection() : termination_seen_(false) {} - ~BackPointerCppSideConnection() override {} - - // js_to_cpp::CppSide: - void StartTest() override { js_side_->BackPointer(BuildSampleEchoArgs()); } - - void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override { - CheckCorruptedEchoArgsList(list); - } - - void TestFinished() override { - termination_seen_ = true; - run_loop()->Quit(); - } - - bool DidSucceed() { - return termination_seen_; - } - - private: - bool termination_seen_; - DISALLOW_COPY_AND_ASSIGN(BackPointerCppSideConnection); -}; - -} // namespace - -class JsToCppTest : public testing::Test { - public: - JsToCppTest() {} - - void RunTest(const std::string& test, CppSideConnection* cpp_side) { - cpp_side->set_run_loop(&run_loop_); - - js_to_cpp::JsSidePtr js_side; - auto js_side_proxy = GetProxy(&js_side); - - cpp_side->set_js_side(js_side.get()); - js_to_cpp::CppSidePtr cpp_side_ptr; - cpp_side->Bind(GetProxy(&cpp_side_ptr)); - - js_side->SetCppSide(cpp_side_ptr.Pass()); - - gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode, - gin::ArrayBufferAllocator::SharedInstance()); - gin::IsolateHolder instance; - MojoRunnerDelegate delegate; - gin::ShellRunner runner(&delegate, instance.isolate()); - delegate.Start(&runner, js_side_proxy.PassMessagePipe().release().value(), - test); - - run_loop_.Run(); - } - - private: - base::ShadowingAtExitManager at_exit_; - base::MessageLoop loop; - base::RunLoop run_loop_; - - DISALLOW_COPY_AND_ASSIGN(JsToCppTest); -}; - -TEST_F(JsToCppTest, Ping) { - PingCppSideConnection cpp_side_connection; - RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection); - EXPECT_TRUE(cpp_side_connection.DidSucceed()); -} - -TEST_F(JsToCppTest, Echo) { - EchoCppSideConnection cpp_side_connection; - RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection); - EXPECT_TRUE(cpp_side_connection.DidSucceed()); -} - -TEST_F(JsToCppTest, BitFlip) { - BitFlipCppSideConnection cpp_side_connection; - RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection); - EXPECT_TRUE(cpp_side_connection.DidSucceed()); -} - -TEST_F(JsToCppTest, BackPointer) { - BackPointerCppSideConnection cpp_side_connection; - RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection); - EXPECT_TRUE(cpp_side_connection.DidSucceed()); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/tests/js_to_cpp_tests.js b/mojo/edk/js/tests/js_to_cpp_tests.js deleted file mode 100644 index ddecc4b14..000000000 --- a/mojo/edk/js/tests/js_to_cpp_tests.js +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2014 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. - -define('mojo/edk/js/tests/js_to_cpp_tests', [ - 'console', - 'mojo/edk/js/tests/js_to_cpp.mojom', - 'mojo/public/js/bindings', - 'mojo/public/js/connection', - 'mojo/public/js/connector', - 'mojo/public/js/core', -], function (console, jsToCpp, bindings, connection, connector, core) { - var retainedJsSide; - var retainedJsSideStub; - var sampleData; - var sampleMessage; - var BAD_VALUE = 13; - var DATA_PIPE_PARAMS = { - flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, - elementNumBytes: 1, - capacityNumBytes: 64 - }; - - function JsSideConnection() { - } - - JsSideConnection.prototype = - Object.create(jsToCpp.JsSide.stubClass.prototype); - - JsSideConnection.prototype.setCppSide = function(cppSide) { - this.cppSide_ = cppSide; - this.cppSide_.startTest(); - }; - - JsSideConnection.prototype.ping = function (arg) { - this.cppSide_.pingResponse(); - }; - - JsSideConnection.prototype.echo = function (numIterations, arg) { - var dataPipe1; - var dataPipe2; - var i; - var messagePipe1; - var messagePipe2; - var specialArg; - - // Ensure expected negative values are negative. - if (arg.si64 > 0) - arg.si64 = BAD_VALUE; - - if (arg.si32 > 0) - arg.si32 = BAD_VALUE; - - if (arg.si16 > 0) - arg.si16 = BAD_VALUE; - - if (arg.si8 > 0) - arg.si8 = BAD_VALUE; - - for (i = 0; i < numIterations; ++i) { - dataPipe1 = core.createDataPipe(DATA_PIPE_PARAMS); - dataPipe2 = core.createDataPipe(DATA_PIPE_PARAMS); - messagePipe1 = core.createMessagePipe(); - messagePipe2 = core.createMessagePipe(); - - arg.data_handle = dataPipe1.consumerHandle; - arg.message_handle = messagePipe1.handle1; - - specialArg = new jsToCpp.EchoArgs(); - specialArg.si64 = -1; - specialArg.si32 = -1; - specialArg.si16 = -1; - specialArg.si8 = -1; - specialArg.name = 'going'; - specialArg.data_handle = dataPipe2.consumerHandle; - specialArg.message_handle = messagePipe2.handle1; - - writeDataPipe(dataPipe1, sampleData); - writeDataPipe(dataPipe2, sampleData); - writeMessagePipe(messagePipe1, sampleMessage); - writeMessagePipe(messagePipe2, sampleMessage); - - this.cppSide_.echoResponse(createEchoArgsList(specialArg, arg)); - - core.close(dataPipe1.producerHandle); - core.close(dataPipe2.producerHandle); - core.close(messagePipe1.handle0); - core.close(messagePipe2.handle0); - } - this.cppSide_.testFinished(); - }; - - JsSideConnection.prototype.bitFlip = function (arg) { - var iteration = 0; - var dataPipe; - var messagePipe; - var proto = connector.Connector.prototype; - var stopSignalled = false; - - proto.realAccept = proto.accept; - proto.accept = function (message) { - var offset = iteration / 8; - var mask; - var value; - if (offset < message.buffer.arrayBuffer.byteLength) { - mask = 1 << (iteration % 8); - value = message.buffer.getUint8(offset) ^ mask; - message.buffer.setUint8(offset, value); - return this.realAccept(message); - } - stopSignalled = true; - return false; - }; - - while (!stopSignalled) { - dataPipe = core.createDataPipe(DATA_PIPE_PARAMS); - messagePipe = core.createMessagePipe(); - writeDataPipe(dataPipe, sampleData); - writeMessagePipe(messagePipe, sampleMessage); - arg.data_handle = dataPipe.consumerHandle; - arg.message_handle = messagePipe.handle1; - - this.cppSide_.bitFlipResponse(createEchoArgsList(arg)); - - core.close(dataPipe.producerHandle); - core.close(messagePipe.handle0); - iteration += 1; - } - - proto.accept = proto.realAccept; - proto.realAccept = null; - this.cppSide_.testFinished(); - }; - - JsSideConnection.prototype.backPointer = function (arg) { - var iteration = 0; - var dataPipe; - var messagePipe; - var proto = connector.Connector.prototype; - var stopSignalled = false; - - proto.realAccept = proto.accept; - proto.accept = function (message) { - var delta = 8 * (1 + iteration % 32); - var offset = 8 * ((iteration / 32) | 0); - if (offset < message.buffer.arrayBuffer.byteLength - 4) { - message.buffer.dataView.setUint32(offset, 0x100000000 - delta, true); - message.buffer.dataView.setUint32(offset + 4, 0xffffffff, true); - return this.realAccept(message); - } - stopSignalled = true; - return false; - }; - - while (!stopSignalled) { - dataPipe = core.createDataPipe(DATA_PIPE_PARAMS); - messagePipe = core.createMessagePipe(); - writeDataPipe(dataPipe, sampleData); - writeMessagePipe(messagePipe, sampleMessage); - arg.data_handle = dataPipe.consumerHandle; - arg.message_handle = messagePipe.handle1; - - this.cppSide_.backPointerResponse(createEchoArgsList(arg)); - - core.close(dataPipe.producerHandle); - core.close(messagePipe.handle0); - iteration += 1; - } - - proto.accept = proto.realAccept; - proto.realAccept = null; - this.cppSide_.testFinished(); - }; - - function writeDataPipe(pipe, data) { - var writeResult = core.writeData( - pipe.producerHandle, data, core.WRITE_DATA_FLAG_ALL_OR_NONE); - - if (writeResult.result != core.RESULT_OK) { - console.log('ERROR: Data pipe write result was ' + writeResult.result); - return false; - } - if (writeResult.numBytes != data.length) { - console.log('ERROR: Data pipe write length was ' + writeResult.numBytes); - return false; - } - return true; - } - - function writeMessagePipe(pipe, arrayBuffer) { - var result = core.writeMessage(pipe.handle0, arrayBuffer, [], 0); - if (result != core.RESULT_OK) { - console.log('ERROR: Message pipe write result was ' + result); - return false; - } - return true; - } - - function createEchoArgsListElement(item, next) { - var list = new jsToCpp.EchoArgsList(); - list.item = item; - list.next = next; - return list; - } - - function createEchoArgsList() { - var genuineArray = Array.prototype.slice.call(arguments); - return genuineArray.reduceRight(function (previous, current) { - return createEchoArgsListElement(current, previous); - }, null); - } - - return function(jsSideRequestHandle) { - var i; - sampleData = new Uint8Array(DATA_PIPE_PARAMS.capacityNumBytes); - for (i = 0; i < sampleData.length; ++i) { - sampleData[i] = i; - } - sampleMessage = new Uint8Array(DATA_PIPE_PARAMS.capacityNumBytes); - for (i = 0; i < sampleMessage.length; ++i) { - sampleMessage[i] = 255 - i; - } - retainedJsSideStub = - connection.bindHandleToStub(jsSideRequestHandle, jsToCpp.JsSide); - retainedJsSide = new JsSideConnection; - bindings.StubBindings(retainedJsSideStub).delegate = retainedJsSide; - }; -}); diff --git a/mojo/edk/js/tests/sample_service_tests.js b/mojo/edk/js/tests/sample_service_tests.js deleted file mode 100644 index ac8ce2ead..000000000 --- a/mojo/edk/js/tests/sample_service_tests.js +++ /dev/null @@ -1,171 +0,0 @@ -// 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. - -define([ - "console", - "mojo/edk/js/test/hexdump", - "gin/test/expect", - "mojo/public/interfaces/bindings/tests/sample_service.mojom", - "mojo/public/interfaces/bindings/tests/sample_import.mojom", - "mojo/public/interfaces/bindings/tests/sample_import2.mojom", - "mojo/public/js/core", - ], function(console, hexdump, expect, sample, imported, imported2, core) { - - var global = this; - - // Set this variable to true to print the binary message in hex. - var dumpMessageAsHex = false; - - function makeFoo() { - var bar = new sample.Bar(); - bar.alpha = 20; - bar.beta = 40; - bar.gamma = 60; - bar.type = sample.Bar.Type.VERTICAL; - - var extra_bars = new Array(3); - for (var i = 0; i < extra_bars.length; ++i) { - var base = i * 100; - var type = i % 2 ? - sample.Bar.Type.VERTICAL : sample.Bar.Type.HORIZONTAL; - extra_bars[i] = new sample.Bar(); - extra_bars[i].alpha = base; - extra_bars[i].beta = base + 20; - extra_bars[i].gamma = base + 40; - extra_bars[i].type = type; - } - - var data = new Array(10); - for (var i = 0; i < data.length; ++i) { - data[i] = data.length - i; - } - - var source = 0xFFFF; // Invent a dummy handle. - - var foo = new sample.Foo(); - foo.name = "foopy"; - foo.x = 1; - foo.y = 2; - foo.a = false; - foo.b = true; - foo.c = false; - foo.bar = bar; - foo.extra_bars = extra_bars; - foo.data = data; - foo.source = source; - return foo; - } - - // Check that the given |Foo| is identical to the one made by |MakeFoo()|. - function checkFoo(foo) { - expect(foo.name).toBe("foopy"); - expect(foo.x).toBe(1); - expect(foo.y).toBe(2); - expect(foo.a).toBeFalsy(); - expect(foo.b).toBeTruthy(); - expect(foo.c).toBeFalsy(); - expect(foo.bar.alpha).toBe(20); - expect(foo.bar.beta).toBe(40); - expect(foo.bar.gamma).toBe(60); - expect(foo.bar.type).toBe(sample.Bar.Type.VERTICAL); - - expect(foo.extra_bars.length).toBe(3); - for (var i = 0; i < foo.extra_bars.length; ++i) { - var base = i * 100; - var type = i % 2 ? - sample.Bar.Type.VERTICAL : sample.Bar.Type.HORIZONTAL; - expect(foo.extra_bars[i].alpha).toBe(base); - expect(foo.extra_bars[i].beta).toBe(base + 20); - expect(foo.extra_bars[i].gamma).toBe(base + 40); - expect(foo.extra_bars[i].type).toBe(type); - } - - expect(foo.data.length).toBe(10); - for (var i = 0; i < foo.data.length; ++i) - expect(foo.data[i]).toBe(foo.data.length - i); - - expect(foo.source).toBe(0xFFFF); - } - - // Check that values are set to the defaults if we don't override them. - function checkDefaultValues() { - var bar = new sample.Bar(); - expect(bar.alpha).toBe(255); - expect(bar.type).toBe(sample.Bar.Type.VERTICAL); - - var foo = new sample.Foo(); - expect(foo.name).toBe("Fooby"); - expect(foo.a).toBeTruthy(); - expect(foo.data).toBeNull(); - - var defaults = new sample.DefaultsTest(); - expect(defaults.a0).toBe(-12); - expect(defaults.a1).toBe(sample.kTwelve); - expect(defaults.a2).toBe(1234); - expect(defaults.a3).toBe(34567); - expect(defaults.a4).toBe(123456); - expect(defaults.a5).toBe(3456789012); - expect(defaults.a6).toBe(-111111111111); - // JS doesn't have a 64 bit integer type so this is just checking that the - // expected and actual values have the same closest double value. - expect(defaults.a7).toBe(9999999999999999999); - expect(defaults.a8).toBe(0x12345); - expect(defaults.a9).toBe(-0x12345); - expect(defaults.a10).toBe(1234); - expect(defaults.a11).toBe(true); - expect(defaults.a12).toBe(false); - expect(defaults.a13).toBe(123.25); - expect(defaults.a14).toBe(1234567890.123); - expect(defaults.a15).toBe(1E10); - expect(defaults.a16).toBe(-1.2E+20); - expect(defaults.a17).toBe(1.23E-20); - expect(defaults.a20).toBe(sample.Bar.Type.BOTH); - expect(defaults.a21).toBeNull(); - expect(defaults.a22).toBeTruthy(); - expect(defaults.a22.shape).toBe(imported.Shape.RECTANGLE); - expect(defaults.a22.color).toBe(imported2.Color.BLACK); - expect(defaults.a21).toBeNull(); - expect(defaults.a23).toBe(0xFFFFFFFFFFFFFFFF); - expect(defaults.a24).toBe(0x123456789); - expect(defaults.a25).toBe(-0x123456789); - } - - function ServiceImpl() { - } - - ServiceImpl.prototype = Object.create(sample.Service.stubClass.prototype); - - ServiceImpl.prototype.frobinate = function(foo, baz, port) { - checkFoo(foo); - expect(baz).toBe(sample.Service.BazOptions.EXTRA); - expect(core.isHandle(port)).toBeTruthy(); - global.result = "PASS"; - }; - - function SimpleMessageReceiver() { - } - - SimpleMessageReceiver.prototype.acceptAndExpectResponse = function(message) { - if (dumpMessageAsHex) { - var uint8Array = new Uint8Array(message.buffer.arrayBuffer); - console.log(hexdump.dumpArray(uint8Array)); - } - // Imagine some IPC happened here. - var serviceImpl = new ServiceImpl(); - return serviceImpl.acceptWithResponder(message, { accept: function() {} }); - }; - - var serviceProxy = new sample.Service.proxyClass; - serviceProxy.receiver_ = new SimpleMessageReceiver; - - checkDefaultValues(); - - var foo = makeFoo(); - checkFoo(foo); - - var pipe = core.createMessagePipe(); - serviceProxy.frobinate(foo, sample.Service.BazOptions.EXTRA, pipe.handle0); - expect(core.close(pipe.handle0)).toBe(core.RESULT_OK); - expect(core.close(pipe.handle1)).toBe(core.RESULT_OK); -}); diff --git a/mojo/edk/js/threading.cc b/mojo/edk/js/threading.cc deleted file mode 100644 index b571e3e60..000000000 --- a/mojo/edk/js/threading.cc +++ /dev/null @@ -1,47 +0,0 @@ -// 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 "mojo/edk/js/threading.h" - -#include "base/message_loop/message_loop.h" -#include "gin/object_template_builder.h" -#include "gin/per_isolate_data.h" -#include "mojo/edk/js/handle.h" - -namespace mojo { -namespace js { - -namespace { - -void Quit() { - base::MessageLoop::current()->QuitNow(); -} - -gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin }; - -} // namespace - -const char Threading::kModuleName[] = "mojo/public/js/threading"; - -v8::Local Threading::GetModule(v8::Isolate* isolate) { - gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); - v8::Local templ = data->GetObjectTemplate( - &g_wrapper_info); - - if (templ.IsEmpty()) { - templ = gin::ObjectTemplateBuilder(isolate) - .SetMethod("quit", Quit) - .Build(); - - data->SetObjectTemplate(&g_wrapper_info, templ); - } - - return templ->NewInstance(); -} - -Threading::Threading() { -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/threading.h b/mojo/edk/js/threading.h deleted file mode 100644 index 7cf0d532f..000000000 --- a/mojo/edk/js/threading.h +++ /dev/null @@ -1,25 +0,0 @@ -// 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 MOJO_EDK_JS_THREADING_H_ -#define MOJO_EDK_JS_THREADING_H_ - -#include "gin/public/wrapper_info.h" -#include "v8/include/v8.h" - -namespace mojo { -namespace js { - -class Threading { - public: - static const char kModuleName[]; - static v8::Local GetModule(v8::Isolate* isolate); - private: - Threading(); -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_THREADING_H_ diff --git a/mojo/edk/js/waiting_callback.cc b/mojo/edk/js/waiting_callback.cc deleted file mode 100644 index d5c48c539..000000000 --- a/mojo/edk/js/waiting_callback.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2014 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 "mojo/edk/js/waiting_callback.h" - -#include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "gin/per_context_data.h" -#include "mojo/public/cpp/environment/environment.h" - -namespace mojo { -namespace js { - -namespace { - -v8::Handle GetHiddenPropertyName(v8::Isolate* isolate) { - return gin::StringToSymbol(isolate, "::mojo::js::WaitingCallback"); -} - -} // namespace - -gin::WrapperInfo WaitingCallback::kWrapperInfo = { gin::kEmbedderNativeGin }; - -// static -gin::Handle WaitingCallback::Create( - v8::Isolate* isolate, - v8::Handle callback, - gin::Handle handle_wrapper, - MojoHandleSignals signals) { - gin::Handle waiting_callback = gin::CreateHandle( - isolate, new WaitingCallback(isolate, callback, handle_wrapper)); - waiting_callback->wait_id_ = Environment::GetDefaultAsyncWaiter()->AsyncWait( - handle_wrapper->get().value(), - signals, - MOJO_DEADLINE_INDEFINITE, - &WaitingCallback::CallOnHandleReady, - waiting_callback.get()); - return waiting_callback; -} - -void WaitingCallback::Cancel() { - if (!wait_id_) - return; - - handle_wrapper_->RemoveCloseObserver(this); - handle_wrapper_ = NULL; - Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_); - wait_id_ = 0; -} - -WaitingCallback::WaitingCallback(v8::Isolate* isolate, - v8::Handle callback, - gin::Handle handle_wrapper) - : wait_id_(0), handle_wrapper_(handle_wrapper.get()), weak_factory_(this) { - handle_wrapper_->AddCloseObserver(this); - v8::Handle context = isolate->GetCurrentContext(); - runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr(); - GetWrapper(isolate)->SetHiddenValue(GetHiddenPropertyName(isolate), callback); -} - -WaitingCallback::~WaitingCallback() { - Cancel(); -} - -// static -void WaitingCallback::CallOnHandleReady(void* closure, MojoResult result) { - static_cast(closure)->OnHandleReady(result); -} - -void WaitingCallback::ClearWaitId() { - wait_id_ = 0; - handle_wrapper_->RemoveCloseObserver(this); - handle_wrapper_ = nullptr; -} - -void WaitingCallback::OnHandleReady(MojoResult result) { - ClearWaitId(); - CallCallback(result); -} - -void WaitingCallback::CallCallback(MojoResult result) { - // ClearWaitId must already have been called. - DCHECK(!wait_id_); - DCHECK(!handle_wrapper_); - - if (!runner_) - return; - - gin::Runner::Scope scope(runner_.get()); - v8::Isolate* isolate = runner_->GetContextHolder()->isolate(); - - v8::Handle hidden_value = - GetWrapper(isolate)->GetHiddenValue(GetHiddenPropertyName(isolate)); - v8::Handle callback; - CHECK(gin::ConvertFromV8(isolate, hidden_value, &callback)); - - v8::Handle args[] = { gin::ConvertToV8(isolate, result) }; - runner_->Call(callback, runner_->global(), 1, args); -} - -void WaitingCallback::OnWillCloseHandle() { - Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_); - - // This may be called from GC, so we can't execute Javascript now, call - // ClearWaitId explicitly, and CallCallback asynchronously. - ClearWaitId(); - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&WaitingCallback::CallCallback, weak_factory_.GetWeakPtr(), - MOJO_RESULT_INVALID_ARGUMENT)); -} - -} // namespace js -} // namespace mojo diff --git a/mojo/edk/js/waiting_callback.h b/mojo/edk/js/waiting_callback.h deleted file mode 100644 index 5a9dfaffa..000000000 --- a/mojo/edk/js/waiting_callback.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2014 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 MOJO_EDK_JS_WAITING_CALLBACK_H_ -#define MOJO_EDK_JS_WAITING_CALLBACK_H_ - -#include "base/memory/weak_ptr.h" -#include "gin/handle.h" -#include "gin/runner.h" -#include "gin/wrappable.h" -#include "mojo/edk/js/handle.h" -#include "mojo/edk/js/handle_close_observer.h" -#include "mojo/public/c/environment/async_waiter.h" -#include "mojo/public/cpp/system/core.h" - -namespace mojo { -namespace js { - -class WaitingCallback : public gin::Wrappable, - public HandleCloseObserver { - public: - static gin::WrapperInfo kWrapperInfo; - - // Creates a new WaitingCallback. - static gin::Handle Create( - v8::Isolate* isolate, - v8::Handle callback, - gin::Handle handle_wrapper, - MojoHandleSignals signals); - - // Cancels the callback. Does nothing if a callback is not pending. This is - // implicitly invoked from the destructor but can be explicitly invoked as - // necessary. - void Cancel(); - - private: - WaitingCallback(v8::Isolate* isolate, - v8::Handle callback, - gin::Handle handle_wrapper); - ~WaitingCallback() override; - - // Callback from MojoAsyncWaiter. |closure| is the WaitingCallback. - static void CallOnHandleReady(void* closure, MojoResult result); - - // Invoked from CallOnHandleReady() (CallOnHandleReady() must be static). - void OnHandleReady(MojoResult result); - - // Invoked by the HandleWrapper if the handle is closed while this wait is - // still in progress. - void OnWillCloseHandle() override; - - void ClearWaitId(); - void CallCallback(MojoResult result); - - base::WeakPtr runner_; - MojoAsyncWaitID wait_id_; - - HandleWrapper* handle_wrapper_; - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(WaitingCallback); -}; - -} // namespace js -} // namespace mojo - -#endif // MOJO_EDK_JS_WAITING_CALLBACK_H_ diff --git a/mojo/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn index 9a534450d..7800a59b9 100644 --- a/mojo/edk/system/BUILD.gn +++ b/mojo/edk/system/BUILD.gn @@ -94,6 +94,10 @@ component("system") { "raw_channel.cc", "raw_channel.h", "raw_channel_posix.cc", + "ref_counted.h", + "ref_counted_internal.h", + "ref_ptr.h", + "ref_ptr_internal.h", "remote_consumer_data_pipe_impl.cc", "remote_consumer_data_pipe_impl.h", "remote_data_pipe_ack.h", @@ -138,8 +142,8 @@ component("system") { group("tests") { testonly = true deps = [ + ":mojo_system_perftests", ":mojo_system_unittests", - ":mojo_message_pipe_perftests", ] } @@ -196,6 +200,7 @@ test("mojo_system_unittests") { "options_validation_unittest.cc", "platform_handle_dispatcher_unittest.cc", "raw_channel_unittest.cc", + "ref_counted_unittest.cc", "remote_data_pipe_impl_unittest.cc", "remote_message_pipe_unittest.cc", "run_all_unittests.cc", @@ -225,11 +230,12 @@ test("mojo_system_unittests") { allow_circular_includes_from = [ "../embedder:embedder_unittests" ] } -test("mojo_message_pipe_perftests") { +test("mojo_system_perftests") { sources = [ "message_pipe_perftest.cc", "message_pipe_test_utils.cc", "message_pipe_test_utils.h", + "ref_counted_perftest.cc", ] deps = [ diff --git a/mojo/edk/system/channel_manager_unittest.cc b/mojo/edk/system/channel_manager_unittest.cc index 2d38b619d..23bf1439b 100644 --- a/mojo/edk/system/channel_manager_unittest.cc +++ b/mojo/edk/system/channel_manager_unittest.cc @@ -9,12 +9,12 @@ #include "base/run_loop.h" #include "base/task_runner.h" #include "base/thread_task_runner_handle.h" -#include "base/threading/simple_thread.h" #include "mojo/edk/embedder/platform_channel_pair.h" #include "mojo/edk/embedder/simple_platform_support.h" #include "mojo/edk/system/channel.h" #include "mojo/edk/system/channel_endpoint.h" #include "mojo/edk/system/message_pipe_dispatcher.h" +#include "mojo/edk/test/simple_test_thread.h" #include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" @@ -102,7 +102,7 @@ TEST_F(ChannelManagerTest, TwoChannels) { EXPECT_EQ(MOJO_RESULT_OK, d2->Close()); } -class OtherThread : public base::SimpleThread { +class OtherThread : public mojo::test::SimpleTestThread { public: // Note: There should be no other refs to the channel identified by // |channel_id| outside the channel manager. @@ -110,8 +110,7 @@ class OtherThread : public base::SimpleThread { ChannelManager* channel_manager, ChannelId channel_id, const base::Closure& quit_closure) - : base::SimpleThread("other_thread"), - task_runner_(task_runner), + : task_runner_(task_runner), channel_manager_(channel_manager), channel_id_(channel_id), quit_closure_(quit_closure) {} diff --git a/mojo/edk/system/channel_test_base.cc b/mojo/edk/system/channel_test_base.cc index df90d3a6d..1d9a7cd34 100644 --- a/mojo/edk/system/channel_test_base.cc +++ b/mojo/edk/system/channel_test_base.cc @@ -16,7 +16,7 @@ namespace system { namespace test { ChannelTestBase::ChannelTestBase() - : io_thread_(mojo::test::TestIOThread::kAutoStart) {} + : io_thread_(mojo::test::TestIOThread::StartMode::AUTO) {} ChannelTestBase::~ChannelTestBase() { } diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc index 0ef0aa126..57495d8b8 100644 --- a/mojo/edk/system/core_unittest.cc +++ b/mojo/edk/system/core_unittest.cc @@ -997,12 +997,12 @@ TEST_F(CoreTest, DataPipe) { MakeUserPointer(&num_bytes), MOJO_READ_DATA_FLAG_PEEK)); - // Read the remaining two characters, in two-phase mode (all-or-none). + // Read the remaining two characters, in two-phase mode. num_bytes = 2; ASSERT_EQ(MOJO_RESULT_OK, core()->BeginReadData(ch, MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); + MOJO_READ_DATA_FLAG_NONE)); // Note: Count on still being able to do the contiguous read here. ASSERT_EQ(2u, num_bytes); @@ -1236,7 +1236,7 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { ASSERT_EQ(MOJO_RESULT_OK, core()->BeginReadData(ch, MakeUserPointer(&read_ptr), MakeUserPointer(&num_bytes), - MOJO_READ_DATA_FLAG_ALL_OR_NONE)); + MOJO_READ_DATA_FLAG_NONE)); EXPECT_EQ(MOJO_RESULT_BUSY, core()->WriteMessage(h_passing[0], UserPointer(kHello), kHelloSize, MakeUserPointer(&ch), 1, diff --git a/mojo/edk/system/data_pipe.cc b/mojo/edk/system/data_pipe.cc index a18c72de7..e5dbe4fe0 100644 --- a/mojo/edk/system/data_pipe.cc +++ b/mojo/edk/system/data_pipe.cc @@ -309,8 +309,7 @@ MojoResult DataPipe::ProducerWriteData(UserPointer elements, MojoResult DataPipe::ProducerBeginWriteData( UserPointer buffer, - UserPointer buffer_num_bytes, - bool all_or_none) { + UserPointer buffer_num_bytes) { MutexLocker locker(&mutex_); DCHECK(has_local_producer_no_lock()); @@ -319,15 +318,7 @@ MojoResult DataPipe::ProducerBeginWriteData( if (!consumer_open_no_lock()) return MOJO_RESULT_FAILED_PRECONDITION; - uint32_t min_num_bytes_to_write = 0; - if (all_or_none) { - min_num_bytes_to_write = buffer_num_bytes.Get(); - if (min_num_bytes_to_write % element_num_bytes() != 0) - return MOJO_RESULT_INVALID_ARGUMENT; - } - - MojoResult rv = impl_->ProducerBeginWriteData(buffer, buffer_num_bytes, - min_num_bytes_to_write); + MojoResult rv = impl_->ProducerBeginWriteData(buffer, buffer_num_bytes); if (rv != MOJO_RESULT_OK) return rv; // Note: No need to awake producer awakables, even though we're going from @@ -533,23 +524,14 @@ MojoResult DataPipe::ConsumerQueryData(UserPointer num_bytes) { MojoResult DataPipe::ConsumerBeginReadData( UserPointer buffer, - UserPointer buffer_num_bytes, - bool all_or_none) { + UserPointer buffer_num_bytes) { MutexLocker locker(&mutex_); DCHECK(has_local_consumer_no_lock()); if (consumer_in_two_phase_read_no_lock()) return MOJO_RESULT_BUSY; - uint32_t min_num_bytes_to_read = 0; - if (all_or_none) { - min_num_bytes_to_read = buffer_num_bytes.Get(); - if (min_num_bytes_to_read % element_num_bytes() != 0) - return MOJO_RESULT_INVALID_ARGUMENT; - } - - MojoResult rv = impl_->ConsumerBeginReadData(buffer, buffer_num_bytes, - min_num_bytes_to_read); + MojoResult rv = impl_->ConsumerBeginReadData(buffer, buffer_num_bytes); if (rv != MOJO_RESULT_OK) return rv; DCHECK(consumer_in_two_phase_read_no_lock()); @@ -563,6 +545,8 @@ MojoResult DataPipe::ConsumerEndReadData(uint32_t num_bytes_read) { if (!consumer_in_two_phase_read_no_lock()) return MOJO_RESULT_FAILED_PRECONDITION; + HandleSignalsState old_consumer_state = + impl_->ConsumerGetHandleSignalsState(); HandleSignalsState old_producer_state = impl_->ProducerGetHandleSignalsState(); MojoResult rv; @@ -579,7 +563,7 @@ MojoResult DataPipe::ConsumerEndReadData(uint32_t num_bytes_read) { // during the two-phase read), so awake consumer awakables. HandleSignalsState new_consumer_state = impl_->ConsumerGetHandleSignalsState(); - if (new_consumer_state.satisfies(MOJO_HANDLE_SIGNAL_READABLE)) + if (!new_consumer_state.equals(old_consumer_state)) AwakeConsumerAwakablesForStateChangeNoLock(new_consumer_state); HandleSignalsState new_producer_state = impl_->ProducerGetHandleSignalsState(); diff --git a/mojo/edk/system/data_pipe.h b/mojo/edk/system/data_pipe.h index c5a1c6452..180c98103 100644 --- a/mojo/edk/system/data_pipe.h +++ b/mojo/edk/system/data_pipe.h @@ -107,8 +107,7 @@ class DataPipe final : public ChannelEndpointClient { UserPointer num_bytes, bool all_or_none); MojoResult ProducerBeginWriteData(UserPointer buffer, - UserPointer buffer_num_bytes, - bool all_or_none); + UserPointer buffer_num_bytes); MojoResult ProducerEndWriteData(uint32_t num_bytes_written); HandleSignalsState ProducerGetHandleSignalsState(); MojoResult ProducerAddAwakable(Awakable* awakable, @@ -140,8 +139,7 @@ class DataPipe final : public ChannelEndpointClient { bool all_or_none); MojoResult ConsumerQueryData(UserPointer num_bytes); MojoResult ConsumerBeginReadData(UserPointer buffer, - UserPointer buffer_num_bytes, - bool all_or_none); + UserPointer buffer_num_bytes); MojoResult ConsumerEndReadData(uint32_t num_bytes_read); HandleSignalsState ConsumerGetHandleSignalsState(); MojoResult ConsumerAddAwakable(Awakable* awakable, diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc index 5a1bafb45..059f80127 100644 --- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc +++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc @@ -107,12 +107,12 @@ MojoResult DataPipeConsumerDispatcher::BeginReadDataImplNoLock( mutex().AssertHeld(); // These flags may not be used in two-phase mode. - if ((flags & MOJO_READ_DATA_FLAG_DISCARD) || + if ((flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE) || + (flags & MOJO_READ_DATA_FLAG_DISCARD) || (flags & MOJO_READ_DATA_FLAG_QUERY) || (flags & MOJO_READ_DATA_FLAG_PEEK)) return MOJO_RESULT_INVALID_ARGUMENT; - return data_pipe_->ConsumerBeginReadData( - buffer, buffer_num_bytes, (flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE)); + return data_pipe_->ConsumerBeginReadData(buffer, buffer_num_bytes); } MojoResult DataPipeConsumerDispatcher::EndReadDataImplNoLock( diff --git a/mojo/edk/system/data_pipe_impl.h b/mojo/edk/system/data_pipe_impl.h index eff673538..6dfddc70c 100644 --- a/mojo/edk/system/data_pipe_impl.h +++ b/mojo/edk/system/data_pipe_impl.h @@ -64,8 +64,7 @@ class DataPipeImpl { uint32_t min_num_bytes_to_write) = 0; virtual MojoResult ProducerBeginWriteData( UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_write) = 0; + UserPointer buffer_num_bytes) = 0; virtual MojoResult ProducerEndWriteData(uint32_t num_bytes_written) = 0; // Note: A producer should not be writable during a two-phase write. virtual HandleSignalsState ProducerGetHandleSignalsState() const = 0; @@ -92,8 +91,7 @@ class DataPipeImpl { virtual MojoResult ConsumerQueryData(UserPointer num_bytes) = 0; virtual MojoResult ConsumerBeginReadData( UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_read) = 0; + UserPointer buffer_num_bytes) = 0; virtual MojoResult ConsumerEndReadData(uint32_t num_bytes_read) = 0; // Note: A consumer should not be writable during a two-phase read. virtual HandleSignalsState ConsumerGetHandleSignalsState() const = 0; diff --git a/mojo/edk/system/data_pipe_impl_unittest.cc b/mojo/edk/system/data_pipe_impl_unittest.cc index 3b19c5b8f..65c0904ef 100644 --- a/mojo/edk/system/data_pipe_impl_unittest.cc +++ b/mojo/edk/system/data_pipe_impl_unittest.cc @@ -124,9 +124,8 @@ class DataPipeImplTest : public testing::Test { return dpp()->ProducerWriteData(elements, num_bytes, all_or_none); } MojoResult ProducerBeginWriteData(UserPointer buffer, - UserPointer buffer_num_bytes, - bool all_or_none) { - return dpp()->ProducerBeginWriteData(buffer, buffer_num_bytes, all_or_none); + UserPointer buffer_num_bytes) { + return dpp()->ProducerBeginWriteData(buffer, buffer_num_bytes); } MojoResult ProducerEndWriteData(uint32_t num_bytes_written) { return dpp()->ProducerEndWriteData(num_bytes_written); @@ -158,9 +157,8 @@ class DataPipeImplTest : public testing::Test { return dpc()->ConsumerQueryData(num_bytes); } MojoResult ConsumerBeginReadData(UserPointer buffer, - UserPointer buffer_num_bytes, - bool all_or_none) { - return dpc()->ConsumerBeginReadData(buffer, buffer_num_bytes, all_or_none); + UserPointer buffer_num_bytes) { + return dpc()->ConsumerBeginReadData(buffer, buffer_num_bytes); } MojoResult ConsumerEndReadData(uint32_t num_bytes_read) { return dpc()->ConsumerEndReadData(num_bytes_read); @@ -224,7 +222,7 @@ class LocalDataPipeImplTestHelper : public DataPipeImplTestHelper { class RemoteDataPipeImplTestHelper : public DataPipeImplTestHelper { public: RemoteDataPipeImplTestHelper() - : io_thread_(mojo::test::TestIOThread::kAutoStart) {} + : io_thread_(mojo::test::TestIOThread::StartMode::AUTO) {} ~RemoteDataPipeImplTestHelper() override {} void SetUp() override { @@ -611,7 +609,7 @@ TYPED_TEST(DataPipeImplTest, SimpleReadWrite) { uint32_t context; int32_t elements[10] = {}; - uint32_t num_bytes = 0; + uint32_t num_bytes = 0u; // Try reading; nothing there yet. num_bytes = @@ -621,7 +619,7 @@ TYPED_TEST(DataPipeImplTest, SimpleReadWrite) { MakeUserPointer(&num_bytes), false, false)); // Query; nothing there yet. - num_bytes = 0; + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerQueryData(MakeUserPointer(&num_bytes))); EXPECT_EQ(0u, num_bytes); @@ -668,7 +666,7 @@ TYPED_TEST(DataPipeImplTest, SimpleReadWrite) { // implementation/configured limits) that not all the data has arrived yet. // (The theoretically-correct assertion here is that |num_bytes| is |1 * ...| // or |2 * ...|.) - num_bytes = 0; + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerQueryData(MakeUserPointer(&num_bytes))); EXPECT_EQ(2 * sizeof(elements[0]), num_bytes); @@ -687,7 +685,7 @@ TYPED_TEST(DataPipeImplTest, SimpleReadWrite) { // Query. // TODO(vtl): See previous TODO. (If we got 2 elements there, however, we // should get 1 here.) - num_bytes = 0; + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerQueryData(MakeUserPointer(&num_bytes))); EXPECT_EQ(1 * sizeof(elements[0]), num_bytes); @@ -704,7 +702,7 @@ TYPED_TEST(DataPipeImplTest, SimpleReadWrite) { EXPECT_EQ(-1, elements[1]); // Query. Still has 1 element remaining. - num_bytes = 0; + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerQueryData(MakeUserPointer(&num_bytes))); EXPECT_EQ(1 * sizeof(elements[0]), num_bytes); @@ -731,7 +729,7 @@ TYPED_TEST(DataPipeImplTest, SimpleReadWrite) { EXPECT_EQ(-1, elements[1]); // Query. - num_bytes = 0; + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerQueryData(MakeUserPointer(&num_bytes))); EXPECT_EQ(0u, num_bytes); @@ -872,7 +870,7 @@ TYPED_TEST(DataPipeImplTest, BasicProducerWaiting) { num_bytes = static_cast(3u * sizeof(elements[0])); EXPECT_EQ(MOJO_RESULT_OK, this->ProducerBeginWriteData(MakeUserPointer(&buffer), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(buffer); EXPECT_EQ(static_cast(1u * sizeof(elements[0])), num_bytes); @@ -891,7 +889,7 @@ TYPED_TEST(DataPipeImplTest, BasicProducerWaiting) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_buffer), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(read_buffer); // Since we only read one element (after having written three in all), the // two-phase read should only allow us to read one. This checks an @@ -1212,11 +1210,10 @@ TYPED_TEST(DataPipeImplTest, ConsumerWaitingTwoPhase) { // Write two elements. int32_t* elements = nullptr; void* buffer = nullptr; - // Request room for three (but we'll only write two). - uint32_t num_bytes = static_cast(3u * sizeof(elements[0])); + uint32_t num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ProducerBeginWriteData(MakeUserPointer(&buffer), - MakeUserPointer(&num_bytes), true)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(buffer); EXPECT_GE(num_bytes, static_cast(3u * sizeof(elements[0]))); elements = static_cast(buffer); @@ -1236,12 +1233,11 @@ TYPED_TEST(DataPipeImplTest, ConsumerWaitingTwoPhase) { hss.satisfiable_signals); // Read one element. - // Request two in all-or-none mode, but only read one. const void* read_buffer = nullptr; - num_bytes = static_cast(2u * sizeof(elements[0])); + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_buffer), - MakeUserPointer(&num_bytes), true)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(read_buffer); EXPECT_EQ(static_cast(2u * sizeof(elements[0])), num_bytes); const int32_t* read_elements = static_cast(read_buffer); @@ -1265,7 +1261,7 @@ TYPED_TEST(DataPipeImplTest, ConsumerWaitingTwoPhase) { num_bytes = static_cast(3u * sizeof(elements[0])); EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_buffer), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(read_buffer); EXPECT_EQ(static_cast(1u * sizeof(elements[0])), num_bytes); read_elements = static_cast(read_buffer); @@ -1320,11 +1316,11 @@ TYPED_TEST(DataPipeImplTest, BasicTwoPhaseWaiting) { EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); - uint32_t num_bytes = static_cast(1u * sizeof(int32_t)); void* write_ptr = nullptr; + uint32_t num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(write_ptr); EXPECT_GE(num_bytes, static_cast(1u * sizeof(int32_t))); @@ -1370,11 +1366,11 @@ TYPED_TEST(DataPipeImplTest, BasicTwoPhaseWaiting) { // Start another two-phase write and check that it's readable even in the // middle of it. - num_bytes = static_cast(1u * sizeof(int32_t)); write_ptr = nullptr; + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(write_ptr); EXPECT_GE(num_bytes, static_cast(1u * sizeof(int32_t))); @@ -1392,11 +1388,11 @@ TYPED_TEST(DataPipeImplTest, BasicTwoPhaseWaiting) { EXPECT_EQ(MOJO_RESULT_OK, this->ProducerEndWriteData(0u)); // Start a two-phase read. - num_bytes = static_cast(1u * sizeof(int32_t)); const void* read_ptr = nullptr; + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(read_ptr); EXPECT_EQ(static_cast(1u * sizeof(int32_t)), num_bytes); @@ -1649,180 +1645,6 @@ TYPED_TEST(DataPipeImplTest, AllOrNone) { this->ConsumerClose(); } -TYPED_TEST(DataPipeImplTest, TwoPhaseAllOrNone) { - const MojoCreateDataPipeOptions options = { - kSizeOfOptions, // |struct_size|. - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. - static_cast(sizeof(int32_t)), // |element_num_bytes|. - 10 * sizeof(int32_t) // |capacity_num_bytes|. - }; - this->Create(options); - this->DoTransfer(); - - Waiter waiter; - HandleSignalsState hss; - - // Try writing way too much (two-phase). - uint32_t num_bytes = 20u * sizeof(int32_t); - void* write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - this->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - - // Try writing an amount which isn't a multiple of the element size - // (two-phase). - static_assert(sizeof(int32_t) > 1u, "Wow! int32_t's have size 1"); - num_bytes = 1u; - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - this->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - - // Try reading way too much (two-phase). - num_bytes = 20u * sizeof(int32_t); - const void* read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - this->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - - // Add waiter. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - this->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 1, - nullptr)); - - // Write half (two-phase). - num_bytes = 5u * sizeof(int32_t); - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - this->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - // May provide more space than requested. - EXPECT_GE(num_bytes, 5u * sizeof(int32_t)); - EXPECT_TRUE(write_ptr); - Seq(0, 5, static_cast(write_ptr)); - EXPECT_EQ(MOJO_RESULT_OK, this->ProducerEndWriteData(5u * sizeof(int32_t))); - - // Wait for data. - // TODO(vtl): (See corresponding TODO in AllOrNone.) - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr)); - hss = HandleSignalsState(); - this->ConsumerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // Try reading an amount which isn't a multiple of the element size - // (two-phase). - num_bytes = 1u; - read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - this->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - - // Read one (two-phase). - num_bytes = 1u * sizeof(int32_t); - read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - this->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - EXPECT_GE(num_bytes, 1u * sizeof(int32_t)); - EXPECT_EQ(0, static_cast(read_ptr)[0]); - EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerEndReadData(1u * sizeof(int32_t))); - - // We should have four left, leaving room for six. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - this->ConsumerQueryData(MakeUserPointer(&num_bytes))); - EXPECT_EQ(4u * sizeof(int32_t), num_bytes); - - // Assuming a tight circular buffer of the specified capacity, we can't do a - // two-phase write of six now. - num_bytes = 6u * sizeof(int32_t); - write_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, - this->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), true)); - - // TODO(vtl): Hack (see also the TODO above): We can't currently wait for a - // specified amount of space to be available, so poll. - for (size_t i = 0; i < kMaxPoll; i++) { - // Write six elements (simple), filling the buffer. - num_bytes = 6u * sizeof(int32_t); - int32_t buffer[100]; - Seq(100, 6, buffer); - MojoResult result = this->ProducerWriteData( - UserPointer(buffer), MakeUserPointer(&num_bytes), true); - if (result == MOJO_RESULT_OK) - break; - EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, result); - - test::Sleep(test::EpsilonDeadline()); - } - EXPECT_EQ(6u * sizeof(int32_t), num_bytes); - - // TODO(vtl): Hack: poll again. - for (size_t i = 0; i < kMaxPoll; i++) { - // We have ten. - num_bytes = 0u; - EXPECT_EQ(MOJO_RESULT_OK, - this->ConsumerQueryData(MakeUserPointer(&num_bytes))); - if (num_bytes >= 10u * sizeof(int32_t)) - break; - - test::Sleep(test::EpsilonDeadline()); - } - EXPECT_EQ(10u * sizeof(int32_t), num_bytes); - - // Note: Whether a two-phase read of ten would fail here or not is - // implementation-dependent. - - // Add waiter. - waiter.Init(); - ASSERT_EQ(MOJO_RESULT_OK, - this->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - 2, nullptr)); - - // Close the producer. - this->ProducerClose(); - - // A two-phase read of nine should work. - num_bytes = 9u * sizeof(int32_t); - read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_OK, - this->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - EXPECT_GE(num_bytes, 9u * sizeof(int32_t)); - EXPECT_EQ(1, static_cast(read_ptr)[0]); - EXPECT_EQ(2, static_cast(read_ptr)[1]); - EXPECT_EQ(3, static_cast(read_ptr)[2]); - EXPECT_EQ(4, static_cast(read_ptr)[3]); - EXPECT_EQ(100, static_cast(read_ptr)[4]); - EXPECT_EQ(101, static_cast(read_ptr)[5]); - EXPECT_EQ(102, static_cast(read_ptr)[6]); - EXPECT_EQ(103, static_cast(read_ptr)[7]); - EXPECT_EQ(104, static_cast(read_ptr)[8]); - EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerEndReadData(9u * sizeof(int32_t))); - - // Wait for peer closed. - EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr)); - hss = HandleSignalsState(); - this->ConsumerRemoveAwakable(&waiter, &hss); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); - - // A two-phase read of two should fail, with "failed precondition". - num_bytes = 2u * sizeof(int32_t); - read_ptr = nullptr; - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - this->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), true)); - - this->ConsumerClose(); -} - // Tests that |ProducerWriteData()| and |ConsumerReadData()| writes and reads, // respectively, as much as possible, even if it may have to "wrap around" the // internal circular buffer. (Note that the two-phase write and read need not do @@ -1888,14 +1710,14 @@ TYPED_TEST(DataPipeImplTest, WrapAround) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(write_buffer_ptr); EXPECT_EQ(80u, num_bytes); EXPECT_EQ(MOJO_RESULT_OK, this->ProducerEndWriteData(0u)); } // TODO(vtl): (See corresponding TODO in TwoPhaseAllOrNone.) - size_t total_num_bytes = 0; + size_t total_num_bytes = 0u; for (size_t i = 0; i < kMaxPoll; i++) { // Write as much data as we can (using |ProducerWriteData()|). We should // write 90 bytes (eventually). @@ -1935,7 +1757,7 @@ TYPED_TEST(DataPipeImplTest, WrapAround) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(read_buffer_ptr); EXPECT_EQ(90u, num_bytes); EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerEndReadData(0u)); @@ -1990,7 +1812,7 @@ TYPED_TEST(DataPipeImplTest, WriteCloseProducerRead) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(write_buffer_ptr); EXPECT_GT(num_bytes, 0u); @@ -2011,7 +1833,7 @@ TYPED_TEST(DataPipeImplTest, WriteCloseProducerRead) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(read_buffer_ptr); EXPECT_EQ(2u * kTestDataSize, num_bytes); @@ -2027,7 +1849,7 @@ TYPED_TEST(DataPipeImplTest, WriteCloseProducerRead) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(read_buffer_ptr); EXPECT_EQ(kTestDataSize, num_bytes); @@ -2071,7 +1893,7 @@ TYPED_TEST(DataPipeImplTest, TwoPhaseWriteReadCloseConsumer) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(write_buffer_ptr); ASSERT_GT(num_bytes, kTestDataSize); @@ -2089,7 +1911,7 @@ TYPED_TEST(DataPipeImplTest, TwoPhaseWriteReadCloseConsumer) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(read_buffer_ptr); EXPECT_EQ(kTestDataSize, num_bytes); @@ -2127,7 +1949,7 @@ TYPED_TEST(DataPipeImplTest, TwoPhaseWriteReadCloseConsumer) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, this->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); this->ProducerClose(); } @@ -2151,7 +1973,7 @@ TYPED_TEST(DataPipeImplTest, TwoPhaseWriteCloseBoth) { uint32_t num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ProducerBeginWriteData(MakeUserPointer(&write_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_TRUE(write_buffer_ptr); ASSERT_GT(num_bytes, kTestDataSize); @@ -2232,7 +2054,7 @@ TYPED_TEST(DataPipeImplTest, WriteCloseProducerReadNoData) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, this->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); // Ditto for discard. num_bytes = 10u; @@ -2242,6 +2064,223 @@ TYPED_TEST(DataPipeImplTest, WriteCloseProducerReadNoData) { this->ConsumerClose(); } +// Tests the behavior of writing, reading (all the data), closing the producer, +// and then waiting for more data (with no data remaining). +TYPED_TEST(DataPipeImplTest, WriteReadCloseProducerWaitNoData) { + const int64_t kTestData = 123456789012345LL; + const uint32_t kTestDataSize = static_cast(sizeof(kTestData)); + + const MojoCreateDataPipeOptions options = { + kSizeOfOptions, // |struct_size|. + MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. + kTestDataSize, // |element_num_bytes|. + 100u * kTestDataSize // |capacity_num_bytes|. + }; + this->Create(options); + this->DoTransfer(); + + Waiter waiter; + HandleSignalsState hss; + + // Add waiter. + waiter.Init(); + ASSERT_EQ(MOJO_RESULT_OK, + this->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 0, + nullptr)); + + // Write some data, so we'll have something to read. + uint32_t num_bytes = kTestDataSize; + EXPECT_EQ(MOJO_RESULT_OK, + this->ProducerWriteData(UserPointer(&kTestData), + MakeUserPointer(&num_bytes), false)); + EXPECT_EQ(kTestDataSize, num_bytes); + + // Wait. + EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr)); + hss = HandleSignalsState(); + this->ConsumerRemoveAwakable(&waiter, &hss); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + hss.satisfiable_signals); + + // Read that data. + int64_t data[10] = {}; + num_bytes = static_cast(sizeof(data)); + EXPECT_EQ(MOJO_RESULT_OK, + this->ConsumerReadData(UserPointer(data), + MakeUserPointer(&num_bytes), false, false)); + EXPECT_EQ(kTestDataSize, num_bytes); + EXPECT_EQ(kTestData, data[0]); + + // Add waiter again. + waiter.Init(); + ASSERT_EQ(MOJO_RESULT_OK, + this->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 0, + nullptr)); + + // Close the producer. + this->ProducerClose(); + + // Wait. + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + waiter.Wait(test::TinyDeadline(), nullptr)); + hss = HandleSignalsState(); + this->ConsumerRemoveAwakable(&waiter, &hss); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); + + this->ConsumerClose(); +} + +// During a two-phase read, the consumer is not readable so it may be waited +// upon (to become readable again). If the producer is closed and the two-phase +// read consumes the remaining data, that wait should become unsatisfiable. +TYPED_TEST(DataPipeImplTest, BeginReadCloseProducerWaitEndReadNoData) { + const int64_t kTestData = 123456789012345LL; + const uint32_t kTestDataSize = static_cast(sizeof(kTestData)); + + const MojoCreateDataPipeOptions options = { + kSizeOfOptions, // |struct_size|. + MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. + kTestDataSize, // |element_num_bytes|. + 100u * kTestDataSize // |capacity_num_bytes|. + }; + this->Create(options); + this->DoTransfer(); + + Waiter waiter; + HandleSignalsState hss; + + // Add waiter (for the consumer to become readable). + waiter.Init(); + ASSERT_EQ(MOJO_RESULT_OK, + this->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 0, + nullptr)); + + // Write some data, so we'll have something to read. + uint32_t num_bytes = kTestDataSize; + EXPECT_EQ(MOJO_RESULT_OK, + this->ProducerWriteData(UserPointer(&kTestData), + MakeUserPointer(&num_bytes), false)); + EXPECT_EQ(kTestDataSize, num_bytes); + + // Wait. + EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr)); + hss = HandleSignalsState(); + this->ConsumerRemoveAwakable(&waiter, &hss); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + hss.satisfiable_signals); + + // Start a two-phase read. + num_bytes = 0u; + const void* read_ptr = nullptr; + EXPECT_EQ(MOJO_RESULT_OK, + this->ConsumerBeginReadData(MakeUserPointer(&read_ptr), + MakeUserPointer(&num_bytes))); + EXPECT_EQ(kTestDataSize, num_bytes); + EXPECT_EQ(kTestData, static_cast(read_ptr)[0]); + + // Add waiter (for the producer to be closed). + waiter.Init(); + ASSERT_EQ(MOJO_RESULT_OK, + this->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 0, nullptr)); + + // Close the producer. + this->ProducerClose(); + + // Wait for producer close to be detected. + EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr)); + hss = HandleSignalsState(); + this->ConsumerRemoveAwakable(&waiter, &hss); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + hss.satisfiable_signals); + + // Add waiter (for the consumer to become readable). + waiter.Init(); + ASSERT_EQ(MOJO_RESULT_OK, + this->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 0, + nullptr)); + + // Complete the two-phase read. + EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerEndReadData(kTestDataSize)); + + // Wait. + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + waiter.Wait(test::TinyDeadline(), nullptr)); + hss = HandleSignalsState(); + this->ConsumerRemoveAwakable(&waiter, &hss); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); + + this->ConsumerClose(); +} + +// During a two-phase write, the producer is not writable so it may be waited +// upon (to become writable again). If the consumer is closed, that wait should +// become unsatisfiable. +TYPED_TEST(DataPipeImplTest, BeginWriteCloseConsumerWaitEndWrite) { + const MojoCreateDataPipeOptions options = { + kSizeOfOptions, // |struct_size|. + MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|. + 1u, // |element_num_bytes|. + 100u // |capacity_num_bytes|. + }; + this->Create(options); + this->DoTransfer(); + + Waiter waiter1; + Waiter waiter2; + HandleSignalsState hss; + + // Start a two-phase write. + void* write_ptr = nullptr; + uint32_t num_bytes = 0u; + EXPECT_EQ(MOJO_RESULT_OK, + this->ProducerBeginWriteData(MakeUserPointer(&write_ptr), + MakeUserPointer(&num_bytes))); + + // Add waiter (for the consumer to be closed). + waiter1.Init(); + ASSERT_EQ(MOJO_RESULT_OK, + this->ProducerAddAwakable(&waiter1, MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 0, nullptr)); + + // Add a separate waiter (for the producer to become writable). + waiter2.Init(); + ASSERT_EQ(MOJO_RESULT_OK, + this->ProducerAddAwakable(&waiter2, MOJO_HANDLE_SIGNAL_WRITABLE, 0, + nullptr)); + + // Close the consumer. + this->ConsumerClose(); + + // Wait for the consumer close to be detected. + // Note: If we didn't wait for the consumer close to be detected before + // completing the two-phase write, wait might succeed (in the remote cases). + // This is because the first |Awake()| "wins". + EXPECT_EQ(MOJO_RESULT_OK, waiter1.Wait(test::TinyDeadline(), nullptr)); + hss = HandleSignalsState(); + this->ProducerRemoveAwakable(&waiter1, &hss); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); + + // Complete the two-phase write (with nothing written). + EXPECT_EQ(MOJO_RESULT_OK, this->ProducerEndWriteData(0u)); + + // Wait. + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + waiter2.Wait(test::TinyDeadline(), nullptr)); + hss = HandleSignalsState(); + this->ProducerRemoveAwakable(&waiter2, &hss); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); + + this->ProducerClose(); +} + // Test that two-phase reads/writes behave correctly when given invalid // arguments. TYPED_TEST(DataPipeImplTest, TwoPhaseMoreInvalidArguments) { @@ -2278,11 +2317,11 @@ TYPED_TEST(DataPipeImplTest, TwoPhaseMoreInvalidArguments) { EXPECT_EQ(0u, num_bytes); // Try ending a two-phase write with an invalid amount (too much). - num_bytes = 0u; void* write_ptr = nullptr; + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, this->ProducerEndWriteData(num_bytes + static_cast(sizeof(int32_t)))); @@ -2301,11 +2340,11 @@ TYPED_TEST(DataPipeImplTest, TwoPhaseMoreInvalidArguments) { // Try ending a two-phase write with an invalid amount (not a multiple of the // element size). - num_bytes = 0u; write_ptr = nullptr; + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_GE(num_bytes, 1u); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, this->ProducerEndWriteData(1u)); @@ -2364,7 +2403,7 @@ TYPED_TEST(DataPipeImplTest, TwoPhaseMoreInvalidArguments) { const void* read_ptr = nullptr; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, this->ConsumerEndReadData(num_bytes + static_cast(sizeof(int32_t)))); @@ -2381,7 +2420,7 @@ TYPED_TEST(DataPipeImplTest, TwoPhaseMoreInvalidArguments) { read_ptr = nullptr; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_EQ(1u * sizeof(int32_t), num_bytes); EXPECT_EQ(123, static_cast(read_ptr)[0]); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, this->ConsumerEndReadData(1u)); @@ -2437,7 +2476,7 @@ TYPED_TEST(DataPipeImplTest, WriteCloseProducerTwoPhaseReadAllData) { num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, this->ConsumerBeginReadData(MakeUserPointer(&read_buffer_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); EXPECT_EQ(kTestDataSize, num_bytes); EXPECT_EQ(0, memcmp(read_buffer_ptr, kTestData, kTestDataSize)); diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.cc b/mojo/edk/system/data_pipe_producer_dispatcher.cc index ddb8b9f90..5221713dd 100644 --- a/mojo/edk/system/data_pipe_producer_dispatcher.cc +++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc @@ -84,8 +84,11 @@ MojoResult DataPipeProducerDispatcher::BeginWriteDataImplNoLock( MojoWriteDataFlags flags) { mutex().AssertHeld(); - return data_pipe_->ProducerBeginWriteData( - buffer, buffer_num_bytes, (flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); + // This flag may not be used in two-phase mode. + if ((flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)) + return MOJO_RESULT_INVALID_ARGUMENT; + + return data_pipe_->ProducerBeginWriteData(buffer, buffer_num_bytes); } MojoResult DataPipeProducerDispatcher::EndWriteDataImplNoLock( diff --git a/mojo/edk/system/dispatcher_unittest.cc b/mojo/edk/system/dispatcher_unittest.cc index 1f95c514d..b547182d8 100644 --- a/mojo/edk/system/dispatcher_unittest.cc +++ b/mojo/edk/system/dispatcher_unittest.cc @@ -9,10 +9,10 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_vector.h" #include "base/synchronization/waitable_event.h" -#include "base/threading/simple_thread.h" #include "mojo/edk/embedder/platform_shared_buffer.h" #include "mojo/edk/system/memory.h" #include "mojo/edk/system/waiter.h" +#include "mojo/edk/test/simple_test_thread.h" #include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" @@ -115,7 +115,7 @@ TEST(DispatcherTest, Basic) { EXPECT_EQ(0u, hss.satisfiable_signals); } -class ThreadSafetyStressThread : public base::SimpleThread { +class ThreadSafetyStressThread : public mojo::test::SimpleTestThread { public: enum DispatcherOp { CLOSE = 0, @@ -137,10 +137,7 @@ class ThreadSafetyStressThread : public base::SimpleThread { ThreadSafetyStressThread(base::WaitableEvent* event, scoped_refptr dispatcher, DispatcherOp op) - : base::SimpleThread("thread_safety_stress_thread"), - event_(event), - dispatcher_(dispatcher), - op_(op) { + : event_(event), dispatcher_(dispatcher), op_(op) { CHECK_LE(0, op_); CHECK_LT(op_, DISPATCHER_OP_COUNT); } diff --git a/mojo/edk/system/ipc_support_unittest.cc b/mojo/edk/system/ipc_support_unittest.cc index cb45eb3a7..11800d411 100644 --- a/mojo/edk/system/ipc_support_unittest.cc +++ b/mojo/edk/system/ipc_support_unittest.cc @@ -372,7 +372,7 @@ class IPCSupportTest : public testing::Test { public: // Note: Run master process delegate methods on the I/O thread. IPCSupportTest() - : test_io_thread_(TestIOThread::kAutoStart), + : test_io_thread_(TestIOThread::StartMode::AUTO), master_ipc_support_(&platform_support_, embedder::ProcessType::MASTER, test_io_thread_.task_runner(), @@ -686,7 +686,7 @@ MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessMasterSlaveInternal) { ASSERT_TRUE(client_platform_handle.is_valid()); embedder::SimplePlatformSupport platform_support; - TestIOThread test_io_thread(TestIOThread::kAutoStart); + TestIOThread test_io_thread(TestIOThread::StartMode::AUTO); TestSlaveProcessDelegate slave_process_delegate; // Note: Run process delegate methods on the I/O thread. IPCSupport ipc_support(&platform_support, embedder::ProcessType::SLAVE, diff --git a/mojo/edk/system/local_data_pipe_impl.cc b/mojo/edk/system/local_data_pipe_impl.cc index 3d2e9e5f0..8f878d56a 100644 --- a/mojo/edk/system/local_data_pipe_impl.cc +++ b/mojo/edk/system/local_data_pipe_impl.cc @@ -107,8 +107,7 @@ MojoResult LocalDataPipeImpl::ProducerWriteData( MojoResult LocalDataPipeImpl::ProducerBeginWriteData( UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_write) { + UserPointer buffer_num_bytes) { DCHECK(consumer_open()); // The index we need to start writing at. @@ -116,12 +115,6 @@ MojoResult LocalDataPipeImpl::ProducerBeginWriteData( (start_index_ + current_num_bytes_) % capacity_num_bytes(); size_t max_num_bytes_to_write = GetMaxNumBytesToWrite(); - if (min_num_bytes_to_write > max_num_bytes_to_write) { - // Don't return "should wait" since you can't wait for a specified amount - // of data. - return MOJO_RESULT_OUT_OF_RANGE; - } - // Don't go into a two-phase write if there's no room. if (max_num_bytes_to_write == 0) return MOJO_RESULT_SHOULD_WAIT; @@ -289,16 +282,8 @@ MojoResult LocalDataPipeImpl::ConsumerQueryData( MojoResult LocalDataPipeImpl::ConsumerBeginReadData( UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_read) { + UserPointer buffer_num_bytes) { size_t max_num_bytes_to_read = GetMaxNumBytesToRead(); - if (min_num_bytes_to_read > max_num_bytes_to_read) { - // Don't return "should wait" since you can't wait for a specified amount of - // data. - return producer_open() ? MOJO_RESULT_OUT_OF_RANGE - : MOJO_RESULT_FAILED_PRECONDITION; - } - // Don't go into a two-phase read if there's no data. if (max_num_bytes_to_read == 0) { return producer_open() ? MOJO_RESULT_SHOULD_WAIT diff --git a/mojo/edk/system/local_data_pipe_impl.h b/mojo/edk/system/local_data_pipe_impl.h index 46a353d72..aa1459084 100644 --- a/mojo/edk/system/local_data_pipe_impl.h +++ b/mojo/edk/system/local_data_pipe_impl.h @@ -31,9 +31,9 @@ class LocalDataPipeImpl final : public DataPipeImpl { UserPointer num_bytes, uint32_t max_num_bytes_to_write, uint32_t min_num_bytes_to_write) override; - MojoResult ProducerBeginWriteData(UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_write) override; + MojoResult ProducerBeginWriteData( + UserPointer buffer, + UserPointer buffer_num_bytes) override; MojoResult ProducerEndWriteData(uint32_t num_bytes_written) override; HandleSignalsState ProducerGetHandleSignalsState() const override; void ProducerStartSerialize(Channel* channel, @@ -54,9 +54,9 @@ class LocalDataPipeImpl final : public DataPipeImpl { uint32_t max_num_bytes_to_discard, uint32_t min_num_bytes_to_discard) override; MojoResult ConsumerQueryData(UserPointer num_bytes) override; - MojoResult ConsumerBeginReadData(UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_read) override; + MojoResult ConsumerBeginReadData( + UserPointer buffer, + UserPointer buffer_num_bytes) override; MojoResult ConsumerEndReadData(uint32_t num_bytes_read) override; HandleSignalsState ConsumerGetHandleSignalsState() const override; void ConsumerStartSerialize(Channel* channel, diff --git a/mojo/edk/system/message_pipe_dispatcher_unittest.cc b/mojo/edk/system/message_pipe_dispatcher_unittest.cc index a0eca42ca..11707d97e 100644 --- a/mojo/edk/system/message_pipe_dispatcher_unittest.cc +++ b/mojo/edk/system/message_pipe_dispatcher_unittest.cc @@ -15,11 +15,11 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_vector.h" -#include "base/threading/simple_thread.h" #include "mojo/edk/system/message_pipe.h" #include "mojo/edk/system/test_utils.h" #include "mojo/edk/system/waiter.h" #include "mojo/edk/system/waiter_test_utils.h" +#include "mojo/edk/test/simple_test_thread.h" #include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" @@ -472,15 +472,14 @@ TEST(MessagePipeDispatcherTest, BasicThreaded) { const size_t kMaxMessageSize = 2000; -class WriterThread : public base::SimpleThread { +class WriterThread : public mojo::test::SimpleTestThread { public: // |*messages_written| and |*bytes_written| belong to the thread while it's // alive. WriterThread(scoped_refptr write_dispatcher, size_t* messages_written, size_t* bytes_written) - : base::SimpleThread("writer_thread"), - write_dispatcher_(write_dispatcher), + : write_dispatcher_(write_dispatcher), messages_written_(messages_written), bytes_written_(bytes_written) { *messages_written_ = 0; @@ -523,14 +522,13 @@ class WriterThread : public base::SimpleThread { MOJO_DISALLOW_COPY_AND_ASSIGN(WriterThread); }; -class ReaderThread : public base::SimpleThread { +class ReaderThread : public mojo::test::SimpleTestThread { public: // |*messages_read| and |*bytes_read| belong to the thread while it's alive. ReaderThread(scoped_refptr read_dispatcher, size_t* messages_read, size_t* bytes_read) - : base::SimpleThread("reader_thread"), - read_dispatcher_(read_dispatcher), + : read_dispatcher_(read_dispatcher), messages_read_(messages_read), bytes_read_(bytes_read) { *messages_read_ = 0; diff --git a/mojo/edk/system/message_pipe_test_utils.cc b/mojo/edk/system/message_pipe_test_utils.cc index 8591c5afd..ef53862df 100644 --- a/mojo/edk/system/message_pipe_test_utils.cc +++ b/mojo/edk/system/message_pipe_test_utils.cc @@ -35,7 +35,7 @@ MojoResult WaitIfNecessary(scoped_refptr mp, ChannelThread::ChannelThread(embedder::PlatformSupport* platform_support) : platform_support_(platform_support), - test_io_thread_(mojo::test::TestIOThread::kManualStart) {} + test_io_thread_(mojo::test::TestIOThread::StartMode::MANUAL) {} ChannelThread::~ChannelThread() { Stop(); diff --git a/mojo/edk/system/raw_channel_unittest.cc b/mojo/edk/system/raw_channel_unittest.cc index 0d6f2c322..9089a6e5e 100644 --- a/mojo/edk/system/raw_channel_unittest.cc +++ b/mojo/edk/system/raw_channel_unittest.cc @@ -16,7 +16,6 @@ #include "base/logging.h" #include "base/memory/scoped_vector.h" #include "base/synchronization/waitable_event.h" -#include "base/threading/simple_thread.h" #include "mojo/edk/embedder/platform_channel_pair.h" #include "mojo/edk/embedder/platform_handle.h" #include "mojo/edk/embedder/scoped_platform_handle.h" @@ -25,6 +24,7 @@ #include "mojo/edk/system/test_utils.h" #include "mojo/edk/system/transport_data.h" #include "mojo/edk/test/scoped_test_dir.h" +#include "mojo/edk/test/simple_test_thread.h" #include "mojo/edk/test/test_io_thread.h" #include "mojo/edk/test/test_utils.h" #include "mojo/edk/util/make_unique.h" @@ -73,7 +73,7 @@ bool WriteTestMessageToHandle(const embedder::PlatformHandle& handle, class RawChannelTest : public testing::Test { public: - RawChannelTest() : io_thread_(mojo::test::TestIOThread::kManualStart) {} + RawChannelTest() : io_thread_(mojo::test::TestIOThread::StartMode::MANUAL) {} ~RawChannelTest() override {} void SetUp() override { @@ -308,12 +308,10 @@ TEST_F(RawChannelTest, OnReadMessage) { // RawChannelTest.WriteMessageAndOnReadMessage --------------------------------- -class RawChannelWriterThread : public base::SimpleThread { +class RawChannelWriterThread : public mojo::test::SimpleTestThread { public: RawChannelWriterThread(RawChannel* raw_channel, size_t write_count) - : base::SimpleThread("raw_channel_writer_thread"), - raw_channel_(raw_channel), - left_to_write_(write_count) {} + : raw_channel_(raw_channel), left_to_write_(write_count) {} ~RawChannelWriterThread() override { Join(); } diff --git a/mojo/edk/system/ref_counted.h b/mojo/edk/system/ref_counted.h new file mode 100644 index 000000000..d24d47186 --- /dev/null +++ b/mojo/edk/system/ref_counted.h @@ -0,0 +1,102 @@ +// Copyright 2015 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. + +// Provides a base class for reference-counted classes. + +#ifndef MOJO_EDK_SYSTEM_REF_COUNTED_H_ +#define MOJO_EDK_SYSTEM_REF_COUNTED_H_ + +#include + +#include +#include + +#include "mojo/edk/system/ref_counted_internal.h" +#include "mojo/edk/system/ref_ptr.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace system { + +// A base class for (thread-safe) reference-counted classes. Use like: +// +// class Foo : public RefCountedThreadSafe { +// ... +// }; +// +// |~Foo()| *may* be made private (e.g., to avoid accidental deletion of objects +// while there are still references to them), |Foo| should friend +// |RefCountedThreadSafe|; use |FRIEND_REF_COUNTED_THREAD_SAFE()| for this: +// +// class Foo : public RefCountedThreadSafe { +// ... +// private: +// FRIEND_REF_COUNTED_THREAD_SAFE(Foo); +// ~Foo(); +// ... +// }; +// +// Similarly, |Foo(...)| may be made private. In this case, there should either +// be a static factory method performing the requisite adoption: +// +// class Foo : public RefCountedThreadSafe { +// ... +// public: +// inline static RefPtr Create() { return AdoptRef(new Foo()); } +// ... +// private: +// Foo(); +// ... +// }; +// +// Or, to allow |MakeRefCounted()| to be used, use |FRIEND_MAKE_REF_COUNTED()|: +// +// class Foo : public RefCountedThreadSafe { +// ... +// private: +// FRIEND_MAKE_REF_COUNTED(Foo); +// Foo(); +// Foo(const Bar& bar, bool maybe); +// ... +// }; +// +// For now, we only have thread-safe reference counting, since that's all we +// need. It's easy enough to add thread-unsafe versions if necessary. +template +class RefCountedThreadSafe : public internal::RefCountedThreadSafeBase { + public: + void Release() const { + if (internal::RefCountedThreadSafeBase::Release()) + delete static_cast(this); + } + + protected: + RefCountedThreadSafe() {} + ~RefCountedThreadSafe() {} + +#ifndef NDEBUG + template + friend RefPtr AdoptRef(U*); + void Adopt() { internal::RefCountedThreadSafeBase::Adopt(); } +#endif + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe); +}; + +// If you subclass |RefCountedThreadSafe| and want to keep your destructor +// private, use this. (See the example above |RefCountedThreadSafe|.) +#define FRIEND_REF_COUNTED_THREAD_SAFE(T) \ + friend class ::mojo::system::RefCountedThreadSafe + +// If you want to keep your constructor(s) private and still want to use +// |MakeRefCounted()|, use this. (See the example above +// |RefCountedThreadSafe|.) +#define FRIEND_MAKE_REF_COUNTED(T) \ + friend class ::mojo::system::internal::MakeRefCountedHelper + +} // namespace system +} // namespace mojo + +#endif // MOJO_EDK_SYSTEM_REF_COUNTED_H_ diff --git a/mojo/edk/system/ref_counted_internal.h b/mojo/edk/system/ref_counted_internal.h new file mode 100644 index 000000000..e320385fa --- /dev/null +++ b/mojo/edk/system/ref_counted_internal.h @@ -0,0 +1,96 @@ +// Copyright 2015 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. + +// Internal implementation details for ref_counted.h. + +#ifndef MOJO_EDK_SYSTEM_REF_COUNTED_INTERNAL_H_ +#define MOJO_EDK_SYSTEM_REF_COUNTED_INTERNAL_H_ + +#include + +#include "base/atomicops.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace system { +namespace internal { + +class RefCountedThreadSafeBase { + public: + void AddRef() const { + assert(!adoption_required_); + assert(!destruction_started_); + base::subtle::NoBarrier_AtomicIncrement(&ref_count_, 1); + } + + void AssertHasOneRef() const { + assert(base::subtle::Acquire_Load(&ref_count_) == 1); + } + + protected: + RefCountedThreadSafeBase() + : ref_count_(1) +#ifndef NDEBUG + , + adoption_required_(true), + destruction_started_(false) +#endif + { + } + + ~RefCountedThreadSafeBase() { + assert(!adoption_required_); + // Should only be destroyed as a result of |Release()|. + assert(destruction_started_); + } + + // Returns true if the object should self-delete. + bool Release() const { + assert(!adoption_required_); + assert(!destruction_started_); + assert(base::subtle::Acquire_Load(&ref_count_) != 0); + // TODO(vtl): We could add the following: + // if (base::subtle::NoBarrier_Load(&ref_count_) == 1) { + // #ifndef NDEBUG + // destruction_started_= true; + // #endif + // return true; + // } + // This would be correct. On ARM (an Nexus 4), in *single-threaded* tests, + // this seems to make the destruction case marginally faster (barely + // measurable), and while the non-destruction case remains about the same + // (possibly marginally slower, but my measurements aren't good enough to + // have any confidence in that). I should try multithreaded/multicore tests. + if (base::subtle::Barrier_AtomicIncrement(&ref_count_, -1) == 0) { +#ifndef NDEBUG + destruction_started_ = true; +#endif + return true; + } + return false; + } + +#ifndef NDEBUG + void Adopt() { + assert(adoption_required_); + adoption_required_ = false; + } +#endif + + private: + mutable base::subtle::Atomic32 ref_count_; + +#ifndef NDEBUG + mutable bool adoption_required_; + mutable bool destruction_started_; +#endif + + MOJO_DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase); +}; + +} // namespace internal +} // namespace system +} // namespace mojo + +#endif // MOJO_EDK_SYSTEM_REF_COUNTED_INTERNAL_H_ diff --git a/mojo/edk/system/ref_counted_perftest.cc b/mojo/edk/system/ref_counted_perftest.cc new file mode 100644 index 000000000..265e0774a --- /dev/null +++ b/mojo/edk/system/ref_counted_perftest.cc @@ -0,0 +1,67 @@ +// Copyright 2015 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 + +#include "base/test/perf_log.h" +#include "mojo/edk/system/ref_counted.h" +#include "mojo/edk/system/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace system { +namespace { + +class MyClass : public RefCountedThreadSafe { + public: + static RefPtr Create() { + return RefPtr(AdoptRef(new MyClass())); + } + + private: + friend class RefCountedThreadSafe; + + MyClass() {} + ~MyClass() {} +}; + +TEST(RefCountedPerfTest, OneThreadCreateAdoptDestroy) { + uint64_t iterations = 0; + test::Stopwatch stopwatch; + stopwatch.Start(); + do { + for (size_t i = 0; i < 1000; i++, iterations++) { + RefPtr x = MyClass::Create(); + x = nullptr; + } + iterations++; + } while (stopwatch.Elapsed() < test::DeadlineFromMilliseconds(1000)); + double elapsed = stopwatch.Elapsed() / 1000000.0; + + base::LogPerfResult("OneThreadCreateAdoptDestroy", iterations / elapsed, + "iterations/s"); +} + +TEST(RefCountedPerfTest, OneThreadAssignRefPtr) { + RefPtr x = MyClass::Create(); + uint64_t iterations = 0; + test::Stopwatch stopwatch; + stopwatch.Start(); + do { + for (size_t i = 0; i < 1000; i++, iterations++) { + RefPtr y = x; + } + iterations++; + } while (stopwatch.Elapsed() < test::DeadlineFromMilliseconds(1000)); + double elapsed = stopwatch.Elapsed() / 1000000.0; + + base::LogPerfResult("OneThreadAssignRefPtr", iterations / elapsed, + "iterations/s"); +} + +// TODO(vtl): Add threaded perf tests. + +} // namespace +} // namespace system +} // namespace mojo diff --git a/mojo/edk/system/ref_counted_unittest.cc b/mojo/edk/system/ref_counted_unittest.cc new file mode 100644 index 000000000..1839fd5d0 --- /dev/null +++ b/mojo/edk/system/ref_counted_unittest.cc @@ -0,0 +1,610 @@ +// Copyright 2015 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. + +// This file tests both ref_counted.h and ref_ptr.h (which the former includes). +// TODO(vtl): Possibly we could separate these tests out better, since a lot of +// it is actually testing |RefPtr|. + +#include "mojo/edk/system/ref_counted.h" + +#include "build/build_config.h" +#include "mojo/public/cpp/system/macros.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(__clang__) +#define ALLOW_PESSIMIZING_MOVE(code_line) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wpessimizing-move\"") code_line; \ + _Pragma("clang diagnostic pop") +#else +#define ALLOW_PESSIMIZING_MOVE(code_line) code_line; +#endif + +#if defined(__clang__) +#define ALLOW_SELF_MOVE(code_line) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wself-move\"") code_line; \ + _Pragma("clang diagnostic pop") +#else +#define ALLOW_SELF_MOVE(code_line) code_line; +#endif + +namespace mojo { +namespace system { +namespace { + +class MyClass : public RefCountedThreadSafe { + protected: + MyClass(MyClass** created, bool* was_destroyed) + : was_destroyed_(was_destroyed) { + if (created) + *created = this; + } + virtual ~MyClass() { + if (was_destroyed_) + *was_destroyed_ = true; + } + + private: + FRIEND_REF_COUNTED_THREAD_SAFE(MyClass); + FRIEND_MAKE_REF_COUNTED(MyClass); + + bool* was_destroyed_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(MyClass); +}; + +class MySubclass final : public MyClass { + private: + FRIEND_REF_COUNTED_THREAD_SAFE(MySubclass); + FRIEND_MAKE_REF_COUNTED(MySubclass); + + MySubclass(MySubclass** created, bool* was_destroyed) + : MyClass(nullptr, was_destroyed) { + if (created) + *created = this; + } + ~MySubclass() override {} + + MOJO_DISALLOW_COPY_AND_ASSIGN(MySubclass); +}; + +TEST(RefCountedTest, Constructors) { + bool was_destroyed; + + { + // Default. + RefPtr r; + EXPECT_TRUE(r.get() == nullptr); + EXPECT_FALSE(r); + } + + { + // Nullptr. + RefPtr r(nullptr); + EXPECT_TRUE(r.get() == nullptr); + EXPECT_FALSE(r); + } + + { + MyClass* created = nullptr; + was_destroyed = false; + // Adopt, then RVO. + RefPtr r(MakeRefCounted(&created, &was_destroyed)); + EXPECT_TRUE(created); + EXPECT_EQ(created, r.get()); + EXPECT_TRUE(r); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MyClass* created = nullptr; + was_destroyed = false; + // Adopt, then move. + ALLOW_PESSIMIZING_MOVE(RefPtr r( + std::move(MakeRefCounted(&created, &was_destroyed)))) + EXPECT_TRUE(created); + EXPECT_EQ(created, r.get()); + EXPECT_TRUE(r); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MyClass* created = nullptr; + was_destroyed = false; + RefPtr r1(MakeRefCounted(&created, &was_destroyed)); + // Copy. + RefPtr r2(r1); + EXPECT_TRUE(created); + EXPECT_EQ(created, r1.get()); + EXPECT_EQ(created, r2.get()); + EXPECT_TRUE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MyClass* created = nullptr; + was_destroyed = false; + RefPtr r1(MakeRefCounted(&created, &was_destroyed)); + // From raw pointer. + RefPtr r2(created); + EXPECT_TRUE(created); + EXPECT_EQ(created, r1.get()); + EXPECT_EQ(created, r2.get()); + EXPECT_TRUE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MySubclass* created = nullptr; + was_destroyed = false; + // Adopt, then "move". + RefPtr r(MakeRefCounted(&created, &was_destroyed)); + EXPECT_TRUE(created); + EXPECT_EQ(static_cast(created), r.get()); + EXPECT_TRUE(r); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MySubclass* created = nullptr; + was_destroyed = false; + // Adopt, then "move". + ALLOW_PESSIMIZING_MOVE(RefPtr r( + std::move(MakeRefCounted(&created, &was_destroyed)))) + EXPECT_TRUE(created); + EXPECT_EQ(static_cast(created), r.get()); + EXPECT_TRUE(r); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MySubclass* created = nullptr; + was_destroyed = false; + RefPtr r1(MakeRefCounted(&created, &was_destroyed)); + // "Copy". + RefPtr r2(r1); + EXPECT_TRUE(created); + EXPECT_EQ(static_cast(created), r1.get()); + EXPECT_EQ(static_cast(created), r2.get()); + EXPECT_TRUE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MySubclass* created = nullptr; + was_destroyed = false; + RefPtr r1(MakeRefCounted(&created, &was_destroyed)); + // From raw pointer. + RefPtr r2(created); + EXPECT_TRUE(created); + EXPECT_EQ(static_cast(created), r1.get()); + EXPECT_EQ(static_cast(created), r2.get()); + EXPECT_TRUE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); +} + +TEST(RefCountedTest, NullAssignmentToNull) { + RefPtr r1; + // No-op null assignment using |nullptr|. + r1 = nullptr; + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_FALSE(r1); + + RefPtr r2; + // No-op null assignment using copy constructor. + r1 = r2; + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_TRUE(r2.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_FALSE(r2); + + // No-op null assignment using move constructor. + r1 = std::move(r2); + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_TRUE(r2.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_FALSE(r2); + + RefPtr r3; + // No-op null assignment using "copy" constructor. + r1 = r3; + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_TRUE(r3.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_FALSE(r3); + + // No-op null assignment using "move" constructor. + r1 = std::move(r3); + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_TRUE(r3.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_FALSE(r3); +} + +TEST(RefCountedTest, NonNullAssignmentToNull) { + bool was_destroyed; + + { + MyClass* created = nullptr; + was_destroyed = false; + RefPtr r1(MakeRefCounted(&created, &was_destroyed)); + RefPtr r2; + // Copy assignment (to null ref pointer). + r2 = r1; + EXPECT_EQ(created, r1.get()); + EXPECT_EQ(created, r2.get()); + EXPECT_TRUE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MyClass* created = nullptr; + was_destroyed = false; + RefPtr r1(MakeRefCounted(&created, &was_destroyed)); + RefPtr r2; + // Move assignment (to null ref pointer). + r2 = std::move(r1); + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_EQ(created, r2.get()); + EXPECT_FALSE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MySubclass* created = nullptr; + was_destroyed = false; + RefPtr r1(MakeRefCounted(&created, &was_destroyed)); + RefPtr r2; + // "Copy" assignment (to null ref pointer). + r2 = r1; + EXPECT_EQ(created, r1.get()); + EXPECT_EQ(static_cast(created), r2.get()); + EXPECT_TRUE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MySubclass* created = nullptr; + was_destroyed = false; + RefPtr r1(MakeRefCounted(&created, &was_destroyed)); + RefPtr r2; + // "Move" assignment (to null ref pointer). + r2 = std::move(r1); + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_EQ(static_cast(created), r2.get()); + EXPECT_FALSE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); +} + +TEST(RefCountedTest, NullAssignmentToNonNull) { + bool was_destroyed = false; + RefPtr r1(MakeRefCounted(nullptr, &was_destroyed)); + // Null assignment (to non-null ref pointer) using |nullptr|. + r1 = nullptr; + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_TRUE(was_destroyed); + + was_destroyed = false; + r1 = MakeRefCounted(nullptr, &was_destroyed); + RefPtr r2; + // Null assignment (to non-null ref pointer) using copy constructor. + r1 = r2; + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_TRUE(r2.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_FALSE(r2); + EXPECT_TRUE(was_destroyed); + + was_destroyed = false; + r1 = MakeRefCounted(nullptr, &was_destroyed); + // Null assignment using move constructor. + r1 = std::move(r2); + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_TRUE(r2.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_FALSE(r2); + EXPECT_TRUE(was_destroyed); + + was_destroyed = false; + r1 = MakeRefCounted(nullptr, &was_destroyed); + RefPtr r3; + // Null assignment (to non-null ref pointer) using "copy" constructor. + r1 = r3; + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_TRUE(r3.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_FALSE(r3); + EXPECT_TRUE(was_destroyed); + + was_destroyed = false; + r1 = MakeRefCounted(nullptr, &was_destroyed); + // Null assignment (to non-null ref pointer) using "move" constructor. + r1 = std::move(r3); + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_TRUE(r3.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_FALSE(r3); + EXPECT_TRUE(was_destroyed); +} + +TEST(RefCountedTest, NonNullAssignmentToNonNull) { + bool was_destroyed1; + bool was_destroyed2; + + { + was_destroyed1 = false; + was_destroyed2 = false; + RefPtr r1(MakeRefCounted(nullptr, &was_destroyed1)); + RefPtr r2(MakeRefCounted(nullptr, &was_destroyed2)); + // Copy assignment (to non-null ref pointer). + r2 = r1; + EXPECT_EQ(r1.get(), r2.get()); + EXPECT_TRUE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed1); + EXPECT_TRUE(was_destroyed2); + } + EXPECT_TRUE(was_destroyed1); + + { + was_destroyed1 = false; + was_destroyed2 = false; + RefPtr r1(MakeRefCounted(nullptr, &was_destroyed1)); + RefPtr r2(MakeRefCounted(nullptr, &was_destroyed2)); + // Move assignment (to non-null ref pointer). + r2 = std::move(r1); + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_FALSE(r2.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed1); + EXPECT_TRUE(was_destroyed2); + } + EXPECT_TRUE(was_destroyed1); + + { + was_destroyed1 = false; + was_destroyed2 = false; + RefPtr r1(MakeRefCounted(nullptr, &was_destroyed1)); + RefPtr r2(MakeRefCounted(nullptr, &was_destroyed2)); + // "Copy" assignment (to non-null ref pointer). + r2 = r1; + EXPECT_EQ(r1.get(), r2.get()); + EXPECT_TRUE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed1); + EXPECT_TRUE(was_destroyed2); + } + EXPECT_TRUE(was_destroyed1); + + { + was_destroyed1 = false; + was_destroyed2 = false; + RefPtr r1(MakeRefCounted(nullptr, &was_destroyed1)); + RefPtr r2(MakeRefCounted(nullptr, &was_destroyed2)); + // Move assignment (to non-null ref pointer). + r2 = std::move(r1); + EXPECT_TRUE(r1.get() == nullptr); + EXPECT_FALSE(r2.get() == nullptr); + EXPECT_FALSE(r1); + EXPECT_TRUE(r2); + EXPECT_FALSE(was_destroyed1); + EXPECT_TRUE(was_destroyed2); + } + EXPECT_TRUE(was_destroyed1); +} + +TEST(RefCountedTest, SelfAssignment) { + bool was_destroyed; + + { + MyClass* created = nullptr; + was_destroyed = false; + RefPtr r(MakeRefCounted(&created, &was_destroyed)); + // Copy. + r = r; + EXPECT_EQ(created, r.get()); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); + + { + MyClass* created = nullptr; + was_destroyed = false; + RefPtr r(MakeRefCounted(&created, &was_destroyed)); + // Move. + ALLOW_SELF_MOVE(r = std::move(r)) + EXPECT_EQ(created, r.get()); + EXPECT_FALSE(was_destroyed); + } + EXPECT_TRUE(was_destroyed); +} + +TEST(RefCountedTest, Swap) { + MyClass* created1 = nullptr; + bool was_destroyed1 = false; + RefPtr r1(MakeRefCounted(&created1, &was_destroyed1)); + EXPECT_TRUE(created1); + EXPECT_EQ(created1, r1.get()); + + MyClass* created2 = nullptr; + bool was_destroyed2 = false; + RefPtr r2(MakeRefCounted(&created2, &was_destroyed2)); + EXPECT_TRUE(created2); + EXPECT_EQ(created2, r2.get()); + EXPECT_NE(created1, created2); + + r1.swap(r2); + EXPECT_EQ(created2, r1.get()); + EXPECT_EQ(created1, r2.get()); +} + +TEST(RefCountedTest, GetAndDereferenceOperators) { + // Note: We check here that .get(), operator*, and operator-> are const, but + // return non-const pointers/refs. + + MyClass* created = nullptr; + const RefPtr r(MakeRefCounted(&created, nullptr)); + MyClass* ptr = r.get(); // Assign to non-const pointer. + EXPECT_EQ(created, ptr); + ptr = r.operator->(); // Assign to non-const pointer. + EXPECT_EQ(created, ptr); + MyClass& ref = *r; // "Assign" to non-const reference. + EXPECT_EQ(created, &ref); +} + +// You can manually call |AddRef()| and |Release()| if you want. +TEST(RefCountedTest, AddRefRelease) { + MyClass* created = nullptr; + bool was_destroyed = false; + { + RefPtr r(MakeRefCounted(&created, &was_destroyed)); + EXPECT_EQ(created, r.get()); + created->AddRef(); + } + EXPECT_FALSE(was_destroyed); + created->Release(); + EXPECT_TRUE(was_destroyed); +} + +TEST(RefCountedTest, Mix) { + MySubclass* created = nullptr; + bool was_destroyed = false; + RefPtr r1(MakeRefCounted(&created, &was_destroyed)); + ASSERT_FALSE(was_destroyed); + created->AssertHasOneRef(); + + RefPtr r2 = r1; + ASSERT_FALSE(was_destroyed); + + r1 = nullptr; + ASSERT_FALSE(was_destroyed); + created->AssertHasOneRef(); + + { + RefPtr r3 = r2; + { + RefPtr r4(r3); + r2 = nullptr; + ASSERT_FALSE(was_destroyed); + } + ASSERT_FALSE(was_destroyed); + created->AssertHasOneRef(); + + r1 = RefPtr(static_cast(r3.get())); + ASSERT_FALSE(was_destroyed); + } + ASSERT_FALSE(was_destroyed); + created->AssertHasOneRef(); + + EXPECT_EQ(created, r1.get()); + + r1 = nullptr; + EXPECT_TRUE(was_destroyed); +} + +class MyPublicClass : public RefCountedThreadSafe { + public: + // Overloaded constructors work with |MakeRefCounted()|. + MyPublicClass() : has_num_(false), num_(0) {} + explicit MyPublicClass(int num) : has_num_(true), num_(num) {} + + ~MyPublicClass() {} + + bool has_num() const { return has_num_; } + int num() const { return num_; } + + private: + bool has_num_; + int num_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(MyPublicClass); +}; + +// You can also just keep constructors and destructors public. Make sure that +// works (mostly that it compiles). +TEST(RefCountedTest, PublicCtorAndDtor) { + RefPtr r1 = MakeRefCounted(); + ASSERT_TRUE(r1); + EXPECT_FALSE(r1->has_num()); + + RefPtr r2 = MakeRefCounted(123); + ASSERT_TRUE(r2); + EXPECT_TRUE(r2->has_num()); + EXPECT_EQ(123, r2->num()); + EXPECT_NE(r1.get(), r2.get()); + + r1 = r2; + EXPECT_TRUE(r1->has_num()); + EXPECT_EQ(123, r1->num()); + EXPECT_EQ(r1.get(), r2.get()); + + r2 = nullptr; + EXPECT_FALSE(r2); + EXPECT_TRUE(r1->has_num()); + EXPECT_EQ(123, r1->num()); + + r1 = nullptr; + EXPECT_FALSE(r1); +} + +#ifndef NDEBUG +#if defined(OS_ANDROID) +// TODO(vtl) On Android, death tests don't seem to work properly with |assert()| +// (which presumably calls |abort()|. +#define MAYBE_DebugChecks DISABLED_DebugChecks +#else +#define MAYBE_DebugChecks DebugChecks +#endif +// The danger with having a public constructor or destructor is that certain +// things will compile. You should get some protection by assertions in Debug +// builds. +TEST(RefCountedTest, MAYBE_DebugChecks) { + { + MyPublicClass* p = new MyPublicClass(); + EXPECT_DEATH_IF_SUPPORTED(delete p, "!adoption_required_"); + } + + { + MyPublicClass* p = new MyPublicClass(); + EXPECT_DEATH_IF_SUPPORTED(RefPtr r(p), + "!adoption_required_"); + } + + { + RefPtr r(MakeRefCounted()); + EXPECT_DEATH_IF_SUPPORTED(delete r.get(), "destruction_started_"); + } +} +#endif + +// TODO(vtl): Add (threaded) stress tests. + +} // namespace +} // namespace system +} // namespace mojo diff --git a/mojo/edk/system/ref_ptr.h b/mojo/edk/system/ref_ptr.h new file mode 100644 index 000000000..9151deaae --- /dev/null +++ b/mojo/edk/system/ref_ptr.h @@ -0,0 +1,222 @@ +// Copyright 2015 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. + +// Provides a smart pointer class for intrusively reference-counted objects. + +#ifndef MOJO_EDK_SYSTEM_REF_PTR_H_ +#define MOJO_EDK_SYSTEM_REF_PTR_H_ + +#include + +#include +#include + +#include "mojo/edk/system/ref_ptr_internal.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace system { + +// A smart pointer class for intrusively reference-counted objects (e.g., those +// subclassing |RefCountedThreadSafe| -- see ref_counted.h). +// +// Such objects require *adoption* to obtain the first |RefPtr|, which is +// accomplished using |AdoptRef| (see below). (This is due to such objects being +// constructed with a reference count of 1. The adoption requirement is +// enforced, at least in Debug builds, by assertions.) +// +// E.g., if |Foo| is an intrusively reference-counted class: +// +// // The |AdoptRef| may be put in a static factory method (e.g., if |Foo|'s +// // constructor is private). +// RefPtr my_foo_ptr(AdoptRef(new Foo())); +// +// // Now OK, since "my Foo" has been adopted ... +// RefPtr another_ptr_to_my_foo(my_foo_ptr.get()); +// +// // ... though this would preferable in this situation. +// RefPtr yet_another_ptr_to_my_foo(my_foo_ptr); +// +// Unlike Chromium's |scoped_refptr|, |RefPtr| is only explicitly constructible +// from a plain pointer (and not assignable). It is however implicitly +// constructible from |nullptr|. So: +// +// RefPtr foo(plain_ptr_to_adopted_foo); // OK. +// foo = plain_ptr_to_adopted_foo; // Not OK (doesn't compile). +// foo = RefPtr(plain_ptr_to_adopted_foo); // OK. +// foo = nullptr; // OK. +// +// And if we have |void MyFunction(RefPtr foo)|, calling it using +// |MyFunction(nullptr)| is also valid. +// +// Implementation note: For copy/move constructors/operator=s, we often have +// templated versions, so that the operation can be done on a |RefPtr|, where +// |U| is a subclass of |T|. However, we also have non-templated versions with +// |U = T|, since the templated versions don't count as copy/move +// constructors/operator=s for the purposes of causing the default copy +// constructor/operator= to be deleted. E.g., if we didn't declare any +// non-templated versions, we'd get the default copy constructor/operator= (we'd +// only not get the default move constructor/operator= by virtue of having a +// destructor)! (In fact, it'd suffice to only declare a non-templated move +// constructor or move operator=, which would cause the copy +// constructor/operator= to be deleted, but for clarity we include explicit +// non-templated versions of everything.) +template +class RefPtr { + public: + RefPtr() : ptr_(nullptr) {} + RefPtr(std::nullptr_t) : ptr_(nullptr) {} + + // Explicit constructor from a plain pointer (to an object that must have + // already been adopted). (Note that in |T::T()|, references to |this| cannot + // be taken, since the object being constructed will not have been adopted + // yet.) + template + explicit RefPtr(U* p) + : ptr_(p) { + if (ptr_) + ptr_->AddRef(); + } + + // Copy constructor. + RefPtr(const RefPtr& r) : ptr_(r.ptr_) { + if (ptr_) + ptr_->AddRef(); + } + + template + RefPtr(const RefPtr& r) + : ptr_(r.ptr_) { + if (ptr_) + ptr_->AddRef(); + } + + // Move constructor. + RefPtr(RefPtr&& r) : ptr_(r.ptr_) { r.ptr_ = nullptr; } + + template + RefPtr(RefPtr&& r) + : ptr_(r.ptr_) { + r.ptr_ = nullptr; + } + + // Destructor. + ~RefPtr() { + if (ptr_) + ptr_->Release(); + } + + T* get() const { return ptr_; } + + T& operator*() const { + assert(ptr_); + return *ptr_; + } + + T* operator->() const { + assert(ptr_); + return ptr_; + } + + // Copy assignment. + RefPtr& operator=(const RefPtr& r) { + // Call |AddRef()| first so self-assignments work. + if (r.ptr_) + r.ptr_->AddRef(); + T* old_ptr = ptr_; + ptr_ = r.ptr_; + if (old_ptr) + old_ptr->Release(); + return *this; + } + + template + RefPtr& operator=(const RefPtr& r) { + // Call |AddRef()| first so self-assignments work. + if (r.ptr_) + r.ptr_->AddRef(); + T* old_ptr = ptr_; + ptr_ = r.ptr_; + if (old_ptr) + old_ptr->Release(); + return *this; + } + + // Move assignment. + // Note: Like |std::shared_ptr|, we support self-move and move assignment is + // equivalent to |RefPtr(std::move(r)).swap(*this)|. + RefPtr& operator=(RefPtr&& r) { + RefPtr(std::move(r)).swap(*this); + return *this; + } + + template + RefPtr& operator=(RefPtr&& r) { + RefPtr(std::move(r)).swap(*this); + return *this; + } + + void swap(RefPtr& r) { + T* p = ptr_; + ptr_ = r.ptr_; + r.ptr_ = p; + } + + explicit operator bool() const { return !!ptr_; } + + template + bool operator==(const RefPtr& rhs) const { + return ptr_ == rhs.ptr_; + } + + template + bool operator!=(const RefPtr& rhs) const { + return !operator==(rhs); + } + + template + bool operator<(const RefPtr& rhs) const { + return ptr_ < rhs.ptr_; + } + + private: + template + friend class RefPtr; + + friend RefPtr AdoptRef(T*); + + enum AdoptTag { ADOPT }; + RefPtr(T* ptr, AdoptTag) : ptr_(ptr) { assert(ptr_); } + + T* ptr_; +}; + +// Adopts a newly-created |T|. Typically used in a static factory method, like: +// +// // static +// RefPtr Foo::Create() { +// return AdoptRef(new Foo()); +// } +template +inline RefPtr AdoptRef(T* ptr) { +#ifndef NDEBUG + ptr->Adopt(); +#endif + return RefPtr(ptr, RefPtr::ADOPT); +} + +// Creates an intrusively reference counted |T|, producing a |RefPtr| (and +// performing the required adoption). Use like: +// +// RefPtr my_foo = MakeRefCounted(ctor_arg1, ctor_arg2); +template +RefPtr MakeRefCounted(Args&&... args) { + return internal::MakeRefCountedHelper::MakeRefCounted( + std::forward(args)...); +} + +} // namespace system +} // namespace mojo + +#endif // MOJO_EDK_SYSTEM_REF_PTR_H_ diff --git a/mojo/edk/system/ref_ptr_internal.h b/mojo/edk/system/ref_ptr_internal.h new file mode 100644 index 000000000..8684694ba --- /dev/null +++ b/mojo/edk/system/ref_ptr_internal.h @@ -0,0 +1,40 @@ +// Copyright 2015 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 MOJO_EDK_SYSTEM_REF_PTR_INTERNAL_H_ +#define MOJO_EDK_SYSTEM_REF_PTR_INTERNAL_H_ + +#include + +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace system { + +template +class RefPtr; + +template +RefPtr AdoptRef(T* ptr); + +namespace internal { + +// This is a wrapper class that can be friended for a particular |T|, if you +// want to make |T|'s constructor private, but still use |MakeRefCounted()| +// (below). (You can't friend partial specializations.) See |MakeRefCounted()| +// and |FRIEND_MAKE_REF_COUNTED()|. +template +class MakeRefCountedHelper { + public: + template + static RefPtr MakeRefCounted(Args&&... args) { + return AdoptRef(new T(std::forward(args)...)); + } +}; + +} // namespace internal +} // namespace system +} // namespace mojo + +#endif // MOJO_EDK_SYSTEM_REF_PTR_INTERNAL_H_ diff --git a/mojo/edk/system/remote_consumer_data_pipe_impl.cc b/mojo/edk/system/remote_consumer_data_pipe_impl.cc index ce0c24033..7bbafb3c7 100644 --- a/mojo/edk/system/remote_consumer_data_pipe_impl.cc +++ b/mojo/edk/system/remote_consumer_data_pipe_impl.cc @@ -177,8 +177,7 @@ MojoResult RemoteConsumerDataPipeImpl::ProducerWriteData( MojoResult RemoteConsumerDataPipeImpl::ProducerBeginWriteData( UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_write) { + UserPointer buffer_num_bytes) { DCHECK(consumer_open()); DCHECK(channel_endpoint_); @@ -186,12 +185,6 @@ MojoResult RemoteConsumerDataPipeImpl::ProducerBeginWriteData( DCHECK_EQ(consumer_num_bytes_ % element_num_bytes(), 0u); size_t max_num_bytes_to_write = capacity_num_bytes() - consumer_num_bytes_; - if (min_num_bytes_to_write > max_num_bytes_to_write) { - // Don't return "should wait" since you can't wait for a specified amount - // of data. - return MOJO_RESULT_OUT_OF_RANGE; - } - // Don't go into a two-phase write if there's no room. if (max_num_bytes_to_write == 0) return MOJO_RESULT_SHOULD_WAIT; @@ -343,8 +336,7 @@ MojoResult RemoteConsumerDataPipeImpl::ConsumerQueryData( MojoResult RemoteConsumerDataPipeImpl::ConsumerBeginReadData( UserPointer /*buffer*/, - UserPointer /*buffer_num_bytes*/, - uint32_t /*min_num_bytes_to_read*/) { + UserPointer /*buffer_num_bytes*/) { NOTREACHED(); return MOJO_RESULT_INTERNAL; } diff --git a/mojo/edk/system/remote_consumer_data_pipe_impl.h b/mojo/edk/system/remote_consumer_data_pipe_impl.h index 1bcd989e9..2131c9620 100644 --- a/mojo/edk/system/remote_consumer_data_pipe_impl.h +++ b/mojo/edk/system/remote_consumer_data_pipe_impl.h @@ -50,9 +50,9 @@ class RemoteConsumerDataPipeImpl final : public DataPipeImpl { UserPointer num_bytes, uint32_t max_num_bytes_to_write, uint32_t min_num_bytes_to_write) override; - MojoResult ProducerBeginWriteData(UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_write) override; + MojoResult ProducerBeginWriteData( + UserPointer buffer, + UserPointer buffer_num_bytes) override; MojoResult ProducerEndWriteData(uint32_t num_bytes_written) override; HandleSignalsState ProducerGetHandleSignalsState() const override; void ProducerStartSerialize(Channel* channel, @@ -73,9 +73,9 @@ class RemoteConsumerDataPipeImpl final : public DataPipeImpl { uint32_t max_num_bytes_to_discard, uint32_t min_num_bytes_to_discard) override; MojoResult ConsumerQueryData(UserPointer num_bytes) override; - MojoResult ConsumerBeginReadData(UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_read) override; + MojoResult ConsumerBeginReadData( + UserPointer buffer, + UserPointer buffer_num_bytes) override; MojoResult ConsumerEndReadData(uint32_t num_bytes_read) override; HandleSignalsState ConsumerGetHandleSignalsState() const override; void ConsumerStartSerialize(Channel* channel, diff --git a/mojo/edk/system/remote_data_pipe_impl_unittest.cc b/mojo/edk/system/remote_data_pipe_impl_unittest.cc index bb03b0754..23b464fdc 100644 --- a/mojo/edk/system/remote_data_pipe_impl_unittest.cc +++ b/mojo/edk/system/remote_data_pipe_impl_unittest.cc @@ -37,7 +37,8 @@ const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE | class RemoteDataPipeImplTest : public testing::Test { public: - RemoteDataPipeImplTest() : io_thread_(mojo::test::TestIOThread::kAutoStart) {} + RemoteDataPipeImplTest() + : io_thread_(mojo::test::TestIOThread::StartMode::AUTO) {} ~RemoteDataPipeImplTest() override {} void SetUp() override { @@ -290,11 +291,11 @@ TEST_F(RemoteDataPipeImplTest, SendConsumerDuringTwoPhaseWrite) { DataPipeConsumerDispatcher::Create(); consumer->Init(dp); - uint32_t num_bytes = static_cast(10u * sizeof(int32_t)); void* write_ptr = nullptr; + uint32_t num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); ASSERT_GE(num_bytes, 1u * sizeof(int32_t)); // Write the consumer to MP 0 (port 0). Wait and receive on MP 1 (port 0). @@ -400,21 +401,21 @@ TEST_F(RemoteDataPipeImplTest, SendConsumerDuringSecondTwoPhaseWrite) { DataPipeConsumerDispatcher::Create(); consumer->Init(dp); - uint32_t num_bytes = static_cast(10u * sizeof(int32_t)); void* write_ptr = nullptr; + uint32_t num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); ASSERT_GE(num_bytes, 1u * sizeof(int32_t)); *static_cast(write_ptr) = 123456; EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData( static_cast(1u * sizeof(int32_t)))); - num_bytes = static_cast(10u * sizeof(int32_t)); write_ptr = nullptr; + num_bytes = 0u; EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerBeginWriteData(MakeUserPointer(&write_ptr), - MakeUserPointer(&num_bytes), false)); + MakeUserPointer(&num_bytes))); ASSERT_GE(num_bytes, 1u * sizeof(int32_t)); // Write the consumer to MP 0 (port 0). Wait and receive on MP 1 (port 0). diff --git a/mojo/edk/system/remote_message_pipe_unittest.cc b/mojo/edk/system/remote_message_pipe_unittest.cc index 530d6c5c9..81a52cba4 100644 --- a/mojo/edk/system/remote_message_pipe_unittest.cc +++ b/mojo/edk/system/remote_message_pipe_unittest.cc @@ -45,7 +45,8 @@ const MojoHandleSignals kAllSignals = MOJO_HANDLE_SIGNAL_READABLE | class RemoteMessagePipeTest : public testing::Test { public: - RemoteMessagePipeTest() : io_thread_(mojo::test::TestIOThread::kAutoStart) {} + RemoteMessagePipeTest() + : io_thread_(mojo::test::TestIOThread::StartMode::AUTO) {} ~RemoteMessagePipeTest() override {} void SetUp() override { diff --git a/mojo/edk/system/remote_producer_data_pipe_impl.cc b/mojo/edk/system/remote_producer_data_pipe_impl.cc index 847949f38..abf1e962c 100644 --- a/mojo/edk/system/remote_producer_data_pipe_impl.cc +++ b/mojo/edk/system/remote_producer_data_pipe_impl.cc @@ -133,8 +133,7 @@ MojoResult RemoteProducerDataPipeImpl::ProducerWriteData( MojoResult RemoteProducerDataPipeImpl::ProducerBeginWriteData( UserPointer /*buffer*/, - UserPointer /*buffer_num_bytes*/, - uint32_t /*min_num_bytes_to_write*/) { + UserPointer /*buffer_num_bytes*/) { NOTREACHED(); return MOJO_RESULT_INTERNAL; } @@ -250,16 +249,8 @@ MojoResult RemoteProducerDataPipeImpl::ConsumerQueryData( MojoResult RemoteProducerDataPipeImpl::ConsumerBeginReadData( UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_read) { + UserPointer buffer_num_bytes) { size_t max_num_bytes_to_read = GetMaxNumBytesToRead(); - if (min_num_bytes_to_read > max_num_bytes_to_read) { - // Don't return "should wait" since you can't wait for a specified amount of - // data. - return producer_open() ? MOJO_RESULT_OUT_OF_RANGE - : MOJO_RESULT_FAILED_PRECONDITION; - } - // Don't go into a two-phase read if there's no data. if (max_num_bytes_to_read == 0) { return producer_open() ? MOJO_RESULT_SHOULD_WAIT diff --git a/mojo/edk/system/remote_producer_data_pipe_impl.h b/mojo/edk/system/remote_producer_data_pipe_impl.h index 617cf9d5b..cea3d288e 100644 --- a/mojo/edk/system/remote_producer_data_pipe_impl.h +++ b/mojo/edk/system/remote_producer_data_pipe_impl.h @@ -50,9 +50,9 @@ class RemoteProducerDataPipeImpl final : public DataPipeImpl { UserPointer num_bytes, uint32_t max_num_bytes_to_write, uint32_t min_num_bytes_to_write) override; - MojoResult ProducerBeginWriteData(UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_write) override; + MojoResult ProducerBeginWriteData( + UserPointer buffer, + UserPointer buffer_num_bytes) override; MojoResult ProducerEndWriteData(uint32_t num_bytes_written) override; HandleSignalsState ProducerGetHandleSignalsState() const override; void ProducerStartSerialize(Channel* channel, @@ -73,9 +73,9 @@ class RemoteProducerDataPipeImpl final : public DataPipeImpl { uint32_t max_num_bytes_to_discard, uint32_t min_num_bytes_to_discard) override; MojoResult ConsumerQueryData(UserPointer num_bytes) override; - MojoResult ConsumerBeginReadData(UserPointer buffer, - UserPointer buffer_num_bytes, - uint32_t min_num_bytes_to_read) override; + MojoResult ConsumerBeginReadData( + UserPointer buffer, + UserPointer buffer_num_bytes) override; MojoResult ConsumerEndReadData(uint32_t num_bytes_read) override; HandleSignalsState ConsumerGetHandleSignalsState() const override; void ConsumerStartSerialize(Channel* channel, diff --git a/mojo/edk/system/waiter_test_utils.cc b/mojo/edk/system/waiter_test_utils.cc index b2cdd882b..e26142010 100644 --- a/mojo/edk/system/waiter_test_utils.cc +++ b/mojo/edk/system/waiter_test_utils.cc @@ -9,7 +9,7 @@ namespace system { namespace test { SimpleWaiterThread::SimpleWaiterThread(MojoResult* result, uint32_t* context) - : base::SimpleThread("waiter_thread"), result_(result), context_(context) { + : result_(result), context_(context) { waiter_.Init(); *result_ = 5420734; // Totally invalid result. *context_ = 23489023; // "Random". @@ -31,8 +31,7 @@ WaiterThread::WaiterThread(scoped_refptr dispatcher, MojoResult* result_out, uint32_t* context_out, HandleSignalsState* signals_state_out) - : base::SimpleThread("waiter_thread"), - dispatcher_(dispatcher), + : dispatcher_(dispatcher), handle_signals_(handle_signals), deadline_(deadline), context_(context), diff --git a/mojo/edk/system/waiter_test_utils.h b/mojo/edk/system/waiter_test_utils.h index 350d87f28..ee2f68c72 100644 --- a/mojo/edk/system/waiter_test_utils.h +++ b/mojo/edk/system/waiter_test_utils.h @@ -8,10 +8,10 @@ #include #include "base/memory/ref_counted.h" -#include "base/threading/simple_thread.h" #include "mojo/edk/system/dispatcher.h" #include "mojo/edk/system/handle_signals_state.h" #include "mojo/edk/system/waiter.h" +#include "mojo/edk/test/simple_test_thread.h" #include "mojo/public/c/system/types.h" #include "mojo/public/cpp/system/macros.h" @@ -43,7 +43,7 @@ namespace test { // code). (We accept this unrealism for simplicity, since |AwakableList| is // thread-unsafe so making it more realistic would require adding nontrivial // synchronization machinery.) -class SimpleWaiterThread : public base::SimpleThread { +class SimpleWaiterThread : public mojo::test::SimpleTestThread { public: // For the duration of the lifetime of this object, |*result| belongs to it // (in the sense that it will write to it whenever it wants). @@ -65,7 +65,7 @@ class SimpleWaiterThread : public base::SimpleThread { // This is a more complex and realistic thread that has a |Waiter|, on which it // waits for the given deadline (with the given flags). Unlike // |SimpleWaiterThread|, it requires the machinery of |Dispatcher|. -class WaiterThread : public base::SimpleThread { +class WaiterThread : public mojo::test::SimpleTestThread { public: // Note: |*did_wait_out|, |*result_out|, |*context_out| and // |*signals_state_out| "belong" to this object (i.e., may be modified by, on diff --git a/mojo/edk/system/waiter_unittest.cc b/mojo/edk/system/waiter_unittest.cc index 29bd5bcd4..7406e06bb 100644 --- a/mojo/edk/system/waiter_unittest.cc +++ b/mojo/edk/system/waiter_unittest.cc @@ -11,9 +11,9 @@ #include -#include "base/threading/simple_thread.h" #include "mojo/edk/system/mutex.h" #include "mojo/edk/system/test_utils.h" +#include "mojo/edk/test/simple_test_thread.h" #include "mojo/public/cpp/system/macros.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,11 +23,10 @@ namespace { const unsigned kPollTimeMs = 10; -class WaitingThread : public base::SimpleThread { +class WaitingThread : public mojo::test::SimpleTestThread { public: explicit WaitingThread(MojoDeadline deadline) - : base::SimpleThread("waiting_thread"), - deadline_(deadline), + : deadline_(deadline), done_(false), result_(MOJO_RESULT_UNKNOWN), context_(static_cast(-1)) { diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn index cbe6e26a8..6a905ee2b 100644 --- a/mojo/edk/test/BUILD.gn +++ b/mojo/edk/test/BUILD.gn @@ -15,6 +15,8 @@ mojo_edk_source_set("test_support") { "scoped_ipc_support.h", "scoped_test_dir.cc", "scoped_test_dir.h", + "simple_test_thread.cc", + "simple_test_thread.h", "test_io_thread.cc", "test_io_thread.h", "test_utils.h", diff --git a/mojo/edk/test/simple_test_thread.cc b/mojo/edk/test/simple_test_thread.cc new file mode 100644 index 000000000..6cf9f11a7 --- /dev/null +++ b/mojo/edk/test/simple_test_thread.cc @@ -0,0 +1,25 @@ +// Copyright 2015 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 "mojo/edk/test/simple_test_thread.h" + +#include "base/logging.h" + +namespace mojo { +namespace test { + +SimpleTestThread::SimpleTestThread() : thread_(this, "SimpleTestThread") {} + +SimpleTestThread::~SimpleTestThread() {} + +void SimpleTestThread::Start() { + thread_.Start(); +} + +void SimpleTestThread::Join() { + thread_.Join(); +} + +} // namespace test +} // namespace mojo diff --git a/mojo/edk/test/simple_test_thread.h b/mojo/edk/test/simple_test_thread.h new file mode 100644 index 000000000..36ec7cde8 --- /dev/null +++ b/mojo/edk/test/simple_test_thread.h @@ -0,0 +1,40 @@ +// Copyright 2015 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 MOJO_EDK_TEST_SIMPLE_TEST_THREAD_ +#define MOJO_EDK_TEST_SIMPLE_TEST_THREAD_ + +#include "base/threading/simple_thread.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { +namespace test { + +// Class to help simple threads (with no message loops) in tests. +class SimpleTestThread : public base::DelegateSimpleThread::Delegate { + public: + SimpleTestThread(); + ~SimpleTestThread() override; + + // Starts the thread. + void Start(); + + // Joins the thread; this must be called if the thread was started. + void Join(); + + // Note: Subclasses must implement: + // virtual void Run() = 0; + // TODO(vtl): When we stop using |base::DelegateSimpleThread|, this will + // directly become part of our interface. + + private: + base::DelegateSimpleThread thread_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(SimpleTestThread); +}; + +} // namespace test +} // namespace mojo + +#endif // MOJO_EDK_TEST_SIMPLE_TEST_THREAD_ diff --git a/mojo/edk/test/test_io_thread.cc b/mojo/edk/test/test_io_thread.cc index b5b9042cf..e7bcac14d 100644 --- a/mojo/edk/test/test_io_thread.cc +++ b/mojo/edk/test/test_io_thread.cc @@ -21,13 +21,13 @@ void PostTaskAndWaitHelper(base::WaitableEvent* event, } // namespace -TestIOThread::TestIOThread(Mode mode) +TestIOThread::TestIOThread(StartMode start_mode) : io_thread_("test_io_thread"), io_thread_started_(false) { - switch (mode) { - case kAutoStart: + switch (start_mode) { + case StartMode::AUTO: Start(); return; - case kManualStart: + case StartMode::MANUAL: return; } CHECK(false) << "Invalid mode"; diff --git a/mojo/edk/test/test_io_thread.h b/mojo/edk/test/test_io_thread.h index 78b0b015f..0b0f5dd2e 100644 --- a/mojo/edk/test/test_io_thread.h +++ b/mojo/edk/test/test_io_thread.h @@ -17,8 +17,8 @@ namespace test { // Class to help create/run threads with I/O |MessageLoop|s in tests. class TestIOThread { public: - enum Mode { kAutoStart, kManualStart }; - explicit TestIOThread(Mode mode); + enum class StartMode { AUTO, MANUAL }; + explicit TestIOThread(StartMode start_mode); // Stops the I/O thread if necessary. ~TestIOThread(); diff --git a/mojo/gpu/texture_uploader.cc b/mojo/gpu/texture_uploader.cc index b559e7771..858c6ad9d 100644 --- a/mojo/gpu/texture_uploader.cc +++ b/mojo/gpu/texture_uploader.cc @@ -45,7 +45,7 @@ mojo::FramePtr TextureUploader::GetUploadFrame( mojo::TransferableResourcePtr resource = mojo::TransferableResource::New(); resource->id = resource_id; - resource->format = mojo::RESOURCE_FORMAT_RGBA_8888; + resource->format = mojo::ResourceFormat::RGBA_8888; resource->filter = GL_LINEAR; resource->size = size.Clone(); mojo::MailboxHolderPtr mailbox_holder = mojo::MailboxHolder::New(); @@ -59,7 +59,7 @@ mojo::FramePtr TextureUploader::GetUploadFrame( resource->is_software = false; mojo::QuadPtr quad = mojo::Quad::New(); - quad->material = mojo::MATERIAL_TEXTURE_CONTENT; + quad->material = mojo::Material::TEXTURE_CONTENT; mojo::RectPtr rect = mojo::Rect::New(); rect->width = size.width; diff --git a/mojo/services/accessibility/public/interfaces/accessibility.mojom b/mojo/services/accessibility/public/interfaces/accessibility.mojom deleted file mode 100644 index 5c80a4353..000000000 --- a/mojo/services/accessibility/public/interfaces/accessibility.mojom +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2014 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. - -[DartPackage="mojo_services"] -module mojo; - -import "geometry/public/interfaces/geometry.mojom"; - -interface AxProvider { - GetTree() => (array nodes); -}; - -struct AxNode { - // Must be non-zero. - uint32 id; - - // Can be zero if the node has no parent or next sibling. - uint32 parent_id; - uint32 next_sibling_id; - - mojo.Rect bounds; - - // At most one of the below will be present. - // TODO(aa): These should become a union. - AxLink? link; - AxText? text; -}; - -struct AxLink { - string url; -}; - -struct AxText { - string content; -}; diff --git a/mojo/services/camera_roll/public/interfaces/BUILD.gn b/mojo/services/camera/public/interfaces/BUILD.gn similarity index 94% rename from mojo/services/camera_roll/public/interfaces/BUILD.gn rename to mojo/services/camera/public/interfaces/BUILD.gn index 53657bf0e..c001bf52a 100644 --- a/mojo/services/camera_roll/public/interfaces/BUILD.gn +++ b/mojo/services/camera/public/interfaces/BUILD.gn @@ -7,7 +7,7 @@ import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni") mojom("interfaces") { sources = [ - "camera_roll.mojom", + "camera.mojom", ] import_dirs = [ get_path_info("../../../", "abspath") ] diff --git a/mojo/services/camera_roll/public/interfaces/camera_roll.mojom b/mojo/services/camera/public/interfaces/camera.mojom similarity index 83% rename from mojo/services/camera_roll/public/interfaces/camera_roll.mojom rename to mojo/services/camera/public/interfaces/camera.mojom index 850da78f6..b7cc5cb71 100644 --- a/mojo/services/camera_roll/public/interfaces/camera_roll.mojom +++ b/mojo/services/camera/public/interfaces/camera.mojom @@ -28,3 +28,10 @@ interface CameraRollService { // if such an index is out-of-bounds. GetPhoto(uint32 index) => (Photo? photo); }; + +// |CameraService| provides access to the device's camera video stream. +interface CameraService { + // Returns the most recent frame captured by the device's camera + // in preview mode. + GetLatestFrame() => (handle? content); +}; diff --git a/mojo/services/contacts/public/interfaces/BUILD.gn b/mojo/services/contacts/public/interfaces/BUILD.gn new file mode 100644 index 000000000..18425afc8 --- /dev/null +++ b/mojo/services/contacts/public/interfaces/BUILD.gn @@ -0,0 +1,12 @@ +# Copyright 2015 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. + +import("//build/module_args/mojo.gni") +import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni") + +mojom("interfaces") { + sources = [ + "contacts.mojom", + ] +} diff --git a/mojo/services/contacts/public/interfaces/contacts.mojom b/mojo/services/contacts/public/interfaces/contacts.mojom new file mode 100644 index 000000000..116e3f9f1 --- /dev/null +++ b/mojo/services/contacts/public/interfaces/contacts.mojom @@ -0,0 +1,36 @@ +// Copyright 2015 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. + +[DartPackage="mojo_services"] +module contacts; + +// A user contact. +struct Contact { + int64 id; + string name; +}; + +// Interface to query current user contacts. +interface ContactsService { + + // Returns the number of contacts that match the given |filter|. |filter| + // will be matched against the contact name without considering the case. A + // contact will match as soon as a part of the name match the filter. + GetCount(string? filter) => (uint64 count); + + // Returns an extract of the list of contacts matching |filter|. |filter| + // will be matched against the contact name without considering the case. A + // contact will match as soon as a part of the name match the filter. + // Contacts are ordered by |name|. The |offset| first contacts are skipped + // and at most |limit| contacts are returned. + Get(string? filter, uint32 offset, uint32 limit) => (array contacts); + + // Returns the emails associated with the contact with the given |id|. + GetEmails(int64 id) => (array emails); + + // Returns an url of the photo for the contact with the given |id|. If + // |high_resolution| is true, a high resolution photo is returned, otherwise + // a thumbnail is returned. + GetPhoto(int64 id, bool high_resolution) => (string? photo_url); +}; diff --git a/mojo/services/files/public/c/lib/directory_wrapper.cc b/mojo/services/files/public/c/lib/directory_wrapper.cc index d67615d36..986df54cc 100644 --- a/mojo/services/files/public/c/lib/directory_wrapper.cc +++ b/mojo/services/files/public/c/lib/directory_wrapper.cc @@ -71,7 +71,7 @@ std::unique_ptr DirectoryWrapper::Open(const char* path, // with it? mojo::files::FilePtr file; - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; directory_->OpenFile(path, mojo::GetProxy(&file), mojo_open_flags, Capture(&error)); if (!directory_.WaitForIncomingResponse()) { @@ -93,7 +93,7 @@ bool DirectoryWrapper::Chdir(const char* path) { return errno_setter.Set(EFAULT); mojo::files::DirectoryPtr new_directory; - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; directory_->OpenDirectory( path, mojo::GetProxy(&new_directory), mojo::files::kOpenFlagRead | mojo::files::kOpenFlagWrite, diff --git a/mojo/services/files/public/c/lib/file_fd_impl.cc b/mojo/services/files/public/c/lib/file_fd_impl.cc index 801da1488..1feabddf8 100644 --- a/mojo/services/files/public/c/lib/file_fd_impl.cc +++ b/mojo/services/files/public/c/lib/file_fd_impl.cc @@ -31,7 +31,7 @@ bool FileFDImpl::Close() { ErrnoImpl::Setter errno_setter(errno_impl()); MOJO_DCHECK(file_); - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; file_->Close(Capture(&error)); if (!file_.WaitForIncomingResponse()) return errno_setter.Set(ESTALE); @@ -43,7 +43,7 @@ std::unique_ptr FileFDImpl::Dup() { MOJO_DCHECK(file_); mojo::files::FilePtr new_file; - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; file_->Dup(mojo::GetProxy(&new_file), Capture(&error)); if (!file_.WaitForIncomingResponse()) { errno_setter.Set(ESTALE); @@ -62,7 +62,7 @@ bool FileFDImpl::Ftruncate(mojio_off_t length) { if (length < 0) return errno_setter.Set(EINVAL); - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; file_->Truncate(static_cast(length), Capture(&error)); if (!file_.WaitForIncomingResponse()) return errno_setter.Set(ESTALE); @@ -76,20 +76,20 @@ mojio_off_t FileFDImpl::Lseek(mojio_off_t offset, int whence) { mojo::files::Whence mojo_whence; switch (whence) { case MOJIO_SEEK_SET: - mojo_whence = mojo::files::WHENCE_FROM_START; + mojo_whence = mojo::files::Whence::FROM_START; break; case MOJIO_SEEK_CUR: - mojo_whence = mojo::files::WHENCE_FROM_CURRENT; + mojo_whence = mojo::files::Whence::FROM_CURRENT; break; case MOJIO_SEEK_END: - mojo_whence = mojo::files::WHENCE_FROM_END; + mojo_whence = mojo::files::Whence::FROM_END; break; default: errno_setter.Set(EINVAL); return -1; } - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; int64_t position = -1; file_->Seek(static_cast(offset), mojo_whence, Capture(&error, &position)); @@ -137,9 +137,10 @@ mojio_ssize_t FileFDImpl::Read(void* buf, size_t count) { return -1; } - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; mojo::Array bytes_read; - file_->Read(static_cast(count), 0, mojo::files::WHENCE_FROM_CURRENT, + file_->Read(static_cast(count), 0, + mojo::files::Whence::FROM_CURRENT, Capture(&error, &bytes_read)); if (!file_.WaitForIncomingResponse()) { errno_setter.Set(ESTALE); @@ -184,9 +185,9 @@ mojio_ssize_t FileFDImpl::Write(const void* buf, size_t count) { if (count > 0) memcpy(&bytes_to_write[0], buf, count); - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; uint32_t num_bytes_written = 0; - file_->Write(bytes_to_write.Pass(), 0, mojo::files::WHENCE_FROM_CURRENT, + file_->Write(bytes_to_write.Pass(), 0, mojo::files::Whence::FROM_CURRENT, Capture(&error, &num_bytes_written)); if (!file_.WaitForIncomingResponse()) { errno_setter.Set(ESTALE); @@ -216,7 +217,7 @@ bool FileFDImpl::Fstat(struct mojio_stat* buf) { } mojo::files::FileInformationPtr file_info; - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; file_->Stat(Capture(&error, &file_info)); if (!file_.WaitForIncomingResponse()) { errno_setter.Set(ESTALE); @@ -239,12 +240,12 @@ bool FileFDImpl::Fstat(struct mojio_stat* buf) { // Leave |st_ino| zero. buf->st_mode = MOJIO_S_IRWXU; switch (file_info->type) { - case mojo::files::FILE_TYPE_UNKNOWN: + case mojo::files::FileType::UNKNOWN: break; - case mojo::files::FILE_TYPE_REGULAR_FILE: + case mojo::files::FileType::REGULAR_FILE: buf->st_mode |= MOJIO_S_IFREG; break; - case mojo::files::FILE_TYPE_DIRECTORY: + case mojo::files::FileType::DIRECTORY: buf->st_mode |= MOJIO_S_IFDIR; break; default: diff --git a/mojo/services/files/public/c/lib/util.cc b/mojo/services/files/public/c/lib/util.cc index 13c24389e..ca06d8f37 100644 --- a/mojo/services/files/public/c/lib/util.cc +++ b/mojo/services/files/public/c/lib/util.cc @@ -12,28 +12,28 @@ namespace mojio { int ErrorToErrno(mojo::files::Error error) { switch (error) { // TODO(vtl): Real errno encodings.... - case mojo::files::ERROR_OK: + case mojo::files::Error::OK: return 0; - case mojo::files::ERROR_UNKNOWN: + case mojo::files::Error::UNKNOWN: // TODO(vtl): Something better? return EIO; - case mojo::files::ERROR_INVALID_ARGUMENT: + case mojo::files::Error::INVALID_ARGUMENT: return EINVAL; - case mojo::files::ERROR_PERMISSION_DENIED: + case mojo::files::Error::PERMISSION_DENIED: return EACCES; - case mojo::files::ERROR_OUT_OF_RANGE: + case mojo::files::Error::OUT_OF_RANGE: // TODO(vtl): But sometimes it might be E2BIG or EDOM or ENAMETOOLONG, // etc.? return EINVAL; - case mojo::files::ERROR_UNIMPLEMENTED: + case mojo::files::Error::UNIMPLEMENTED: // TODO(vtl): Sometimes ENOTSUP? return ENOSYS; - case mojo::files::ERROR_CLOSED: + case mojo::files::Error::CLOSED: return EBADF; - case mojo::files::ERROR_UNAVAILABLE: + case mojo::files::Error::UNAVAILABLE: // TODO(vtl): May sometimes be EAGAIN? return EACCES; - case mojo::files::ERROR_INTERNAL: + case mojo::files::Error::INTERNAL: // TODO(vtl): Something better? return EIO; } diff --git a/mojo/services/files/public/c/lib/util.h b/mojo/services/files/public/c/lib/util.h index 871be9138..628e9ce77 100644 --- a/mojo/services/files/public/c/lib/util.h +++ b/mojo/services/files/public/c/lib/util.h @@ -11,7 +11,7 @@ namespace mojio { // Converts a |mojo::files::Error| to an errno value. (Note that this converts -// |mojo::files::ERROR_OK| to 0, which is not a valid errno value. +// |mojo::files::Error::OK| to 0, which is not a valid errno value. int ErrorToErrno(mojo::files::Error error); } // namespace mojio diff --git a/mojo/services/files/public/c/tests/mojio_impl_test_base.cc b/mojo/services/files/public/c/tests/mojio_impl_test_base.cc index 6b606a010..e3aeba33f 100644 --- a/mojo/services/files/public/c/tests/mojio_impl_test_base.cc +++ b/mojo/services/files/public/c/tests/mojio_impl_test_base.cc @@ -25,10 +25,10 @@ void MojioImplTestBase::SetUp() { mojo::files::FilesPtr files; application_impl()->ConnectToService("mojo:files", &files); - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; files->OpenFileSystem(nullptr, mojo::GetProxy(&directory_), Capture(&error)); MOJO_CHECK(files.WaitForIncomingResponse()); - MOJO_CHECK(error == mojo::files::ERROR_OK); + MOJO_CHECK(error == mojo::files::Error::OK); } } // namespace test diff --git a/mojo/services/files/public/c/tests/test_utils.cc b/mojo/services/files/public/c/tests/test_utils.cc index 1cf4ecfb0..5fa1ef53c 100644 --- a/mojo/services/files/public/c/tests/test_utils.cc +++ b/mojo/services/files/public/c/tests/test_utils.cc @@ -17,13 +17,13 @@ void MakeDirAt(mojo::files::DirectoryPtr* root, const char* path) { MOJO_CHECK(path); mojo::files::DirectoryPtr& dir = *root; - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; dir->OpenDirectory(path, nullptr, mojo::files::kOpenFlagRead | mojo::files::kOpenFlagWrite | mojo::files::kOpenFlagCreate, Capture(&error)); MOJO_CHECK(dir.WaitForIncomingResponse()); - MOJO_CHECK(error == mojo::files::ERROR_OK); + MOJO_CHECK(error == mojo::files::Error::OK); } mojo::files::FilePtr OpenFileAt(mojo::files::DirectoryPtr* root, @@ -34,10 +34,10 @@ mojo::files::FilePtr OpenFileAt(mojo::files::DirectoryPtr* root, mojo::files::DirectoryPtr& dir = *root; mojo::files::FilePtr file; - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; dir->OpenFile(path, mojo::GetProxy(&file), open_flags, Capture(&error)); MOJO_CHECK(dir.WaitForIncomingResponse()); - if (error != mojo::files::ERROR_OK) + if (error != mojo::files::Error::OK) return nullptr; return file.Pass(); @@ -57,13 +57,13 @@ void CreateTestFileAt(mojo::files::DirectoryPtr* root, std::vector bytes_to_write(size); for (size_t i = 0; i < size; i++) bytes_to_write[i] = static_cast(i); - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; uint32_t num_bytes_written = 0; file->Write(mojo::Array::From(bytes_to_write), 0, - mojo::files::WHENCE_FROM_CURRENT, + mojo::files::Whence::FROM_CURRENT, Capture(&error, &num_bytes_written)); MOJO_CHECK(file.WaitForIncomingResponse()); - MOJO_CHECK(error == mojo::files::ERROR_OK); + MOJO_CHECK(error == mojo::files::Error::OK); MOJO_CHECK(num_bytes_written == bytes_to_write.size()); } @@ -74,11 +74,11 @@ int64_t GetFileSize(mojo::files::DirectoryPtr* root, const char* path) { return -1; // Seek to the end to get the file size (we could also stat). - mojo::files::Error error = mojo::files::ERROR_INTERNAL; + mojo::files::Error error = mojo::files::Error::INTERNAL; int64_t position = -1; - file->Seek(0, mojo::files::WHENCE_FROM_END, Capture(&error, &position)); + file->Seek(0, mojo::files::Whence::FROM_END, Capture(&error, &position)); MOJO_CHECK(file.WaitForIncomingResponse()); - MOJO_CHECK(error == mojo::files::ERROR_OK); + MOJO_CHECK(error == mojo::files::Error::OK); MOJO_CHECK(position >= 0); return position; } @@ -89,8 +89,8 @@ std::string GetFileContents(mojo::files::DirectoryPtr* root, const char* path) { MOJO_CHECK(file); mojo::Array bytes_read; - mojo::files::Error error = mojo::files::ERROR_INTERNAL; - file->Read(1024 * 1024, 0, mojo::files::WHENCE_FROM_START, + mojo::files::Error error = mojo::files::Error::INTERNAL; + file->Read(1024 * 1024, 0, mojo::files::Whence::FROM_START, Capture(&error, &bytes_read)); MOJO_CHECK(file.WaitForIncomingResponse()); if (!bytes_read.size()) diff --git a/mojo/services/files/public/c/tests/util_unittest.cc b/mojo/services/files/public/c/tests/util_unittest.cc index 3196d2df1..07b43c611 100644 --- a/mojo/services/files/public/c/tests/util_unittest.cc +++ b/mojo/services/files/public/c/tests/util_unittest.cc @@ -18,15 +18,15 @@ TEST(UtilTest, ErrorToErrno) { mojo::files::Error from; int to; } kExpectedMappings[] = { - {mojo::files::ERROR_OK, 0}, - {mojo::files::ERROR_UNKNOWN, EIO}, - {mojo::files::ERROR_INVALID_ARGUMENT, EINVAL}, - {mojo::files::ERROR_PERMISSION_DENIED, EACCES}, - {mojo::files::ERROR_OUT_OF_RANGE, EINVAL}, - {mojo::files::ERROR_UNIMPLEMENTED, ENOSYS}, - {mojo::files::ERROR_CLOSED, EBADF}, - {mojo::files::ERROR_UNAVAILABLE, EACCES}, - {mojo::files::ERROR_INTERNAL, EIO}, + {mojo::files::Error::OK, 0}, + {mojo::files::Error::UNKNOWN, EIO}, + {mojo::files::Error::INVALID_ARGUMENT, EINVAL}, + {mojo::files::Error::PERMISSION_DENIED, EACCES}, + {mojo::files::Error::OUT_OF_RANGE, EINVAL}, + {mojo::files::Error::UNIMPLEMENTED, ENOSYS}, + {mojo::files::Error::CLOSED, EBADF}, + {mojo::files::Error::UNAVAILABLE, EACCES}, + {mojo::files::Error::INTERNAL, EIO}, }; for (size_t i = 0; i < MOJO_ARRAYSIZE(kExpectedMappings); i++) { diff --git a/mojo/services/files/public/cpp/BUILD.gn b/mojo/services/files/public/cpp/BUILD.gn new file mode 100644 index 000000000..df74f7d98 --- /dev/null +++ b/mojo/services/files/public/cpp/BUILD.gn @@ -0,0 +1,59 @@ +# Copyright 2015 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. + +import("//build/module_args/mojo.gni") +import("$mojo_sdk_root/mojo/public/mojo_application.gni") +import("$mojo_sdk_root/mojo/public/mojo_sdk.gni") + +# files_impl is a helper library for *implementing* various interfaces in +# |mojo.files|. +mojo_sdk_source_set("files_impl") { + # TODO(vtl): This is needed because mojo_sdk.gni doesn't understand relative + # dependencies (so without this we can't depend on our interfaces!). Should + # this target even be a mojo_sdk_source_set? + restrict_external_deps = false + + public_configs = [ "../../../public/build/config:mojo_services" ] + + sources = [ + "input_stream_file.h", + "lib/input_stream_file.cc", + "lib/output_stream_file.cc", + "output_stream_file.h", + ] + + deps = [ + "../interfaces", + ] + + mojo_sdk_deps = [ + "mojo/public/cpp/bindings", + "mojo/public/cpp/bindings:callback", + "mojo/public/cpp/environment", + "mojo/public/cpp/system", + ] +} + +mojo_native_application("files_impl_apptests") { + output_name = "files_impl_apptests" + + testonly = true + + sources = [ + "tests/input_stream_file_unittest.cc", + "tests/output_stream_file_unittest.cc", + ] + + deps = [ + ":files_impl", + "../interfaces", + "$mojo_sdk_root/mojo/public/cpp/application:standalone", + "$mojo_sdk_root/mojo/public/cpp/application:test_support_standalone", + "$mojo_sdk_root/mojo/public/cpp/bindings", + "$mojo_sdk_root/mojo/public/cpp/environment", + "$mojo_sdk_root/mojo/public/cpp/system", + "$mojo_sdk_root/mojo/public/cpp/utility", + "//testing/gtest", + ] +} diff --git a/mojo/services/files/public/cpp/input_stream_file.h b/mojo/services/files/public/cpp/input_stream_file.h new file mode 100644 index 000000000..9a24f1a5e --- /dev/null +++ b/mojo/services/files/public/cpp/input_stream_file.h @@ -0,0 +1,159 @@ +// Copyright 2015 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. + +// This is a helper class for implementing a |mojo::files::File| that behaves +// like an "input stream" ("input" from the point of view of the client -- +// i.e., the client can write/stream input from it, but not write or seek). + +#ifndef MOJO_SERVICES_FILES_PUBLIC_CPP_INPUT_STREAM_FILE_H_ +#define MOJO_SERVICES_FILES_PUBLIC_CPP_INPUT_STREAM_FILE_H_ + +#include +#include + +#include +#include + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/services/files/public/interfaces/file.mojom.h" +#include "mojo/services/files/public/interfaces/types.mojom.h" + +namespace files_impl { + +class InputStreamFile : public mojo::files::File { + public: + // The |Client| receives data written to the stream "file" as well as other + // notifications (e.g., of the "file" being closed). From any of the methods + // below, the client may choose to destroy the |InputStreamFile|. + class Client { + public: + // Called to request data from the stream "file". This can provide data + // synchronously by returning true and setting |*error| and |*data|, or + // asynchronously by returning false and calling the callback when data is + // available. + // - In both cases, a data buffer with zero data can be used to signify + // "end of stream". + // - In both cases, a non-OK error code can be provided instead (in which + // case any data buffer is ignored). + // - In the asynchronous case, calls to |RequestData()| will not be + // overlapped, i.e., no more calls to |RequestData()| will be made until + // its callback has been called. + // - If this object is destroyed with a callback pending, the callback + // should *not* be called. + // - The callback should not be called from within |RequestData()| + // (instead, the client should complete synchronously by returning a + // buffer). + // - However, from within the callback, |RequestData()| may be called + // again. + // TODO(vtl): We should also support "nonblocking" I/O (i.e., always respond + // immediately, possibly with "would block"). + using RequestDataCallback = mojo::Callback data)>; + virtual bool RequestData(size_t max_num_bytes, + mojo::files::Error* error, + mojo::Array* data, + const RequestDataCallback& callback) = 0; + + // Called when the stream "file" is closed, via |Close()| or due to the + // other end of the message pipe being closed. (This will not be called due + // the |InputStreamFile| being destroyed.) + virtual void OnClosed() = 0; + + protected: + virtual ~Client() {} + }; + + // Static factory method. |client| may be null, but if not it should typically + // outlive us (see |set_client()|). + static std::unique_ptr Create( + Client* client, + mojo::InterfaceRequest request); + + ~InputStreamFile() override; + + // Sets the client (which may be null, in which case all |Read()|s from the + // stream "file" will just fail). If non-null, |client| must be valid whenever + // the run (a.k.a. message) loop is run, i.e., whenever a client method may be + // called. + // + // Note: Since it's unusual for reads to fail and then succeed later, one + // should avoid setting a null client and then setting a non-null client. + void set_client(Client* client) { client_ = client; } + + private: + InputStreamFile(Client* client, + mojo::InterfaceRequest request); + + // We should only be deleted by "ourself" (via the strong binding). + friend class mojo::Binding; + + // |mojo::files::File| implementation: + void Close(const CloseCallback& callback) override; + void Read(uint32_t num_bytes_to_read, + int64_t offset, + mojo::files::Whence whence, + const ReadCallback& callback) override; + void Write(mojo::Array bytes_to_write, + int64_t offset, + mojo::files::Whence whence, + const WriteCallback& callback) override; + void ReadToStream(mojo::ScopedDataPipeProducerHandle source, + int64_t offset, + mojo::files::Whence whence, + int64_t num_bytes_to_read, + const ReadToStreamCallback& callback) override; + void WriteFromStream(mojo::ScopedDataPipeConsumerHandle sink, + int64_t offset, + mojo::files::Whence whence, + const WriteFromStreamCallback& callback) override; + void Tell(const TellCallback& callback) override; + void Seek(int64_t offset, + mojo::files::Whence whence, + const SeekCallback& callback) override; + void Stat(const StatCallback& callback) override; + void Truncate(int64_t size, const TruncateCallback& callback) override; + void Touch(mojo::files::TimespecOrNowPtr atime, + mojo::files::TimespecOrNowPtr mtime, + const TouchCallback& callback) override; + void Dup(mojo::InterfaceRequest file, + const DupCallback& callback) override; + void Reopen(mojo::InterfaceRequest file, + uint32_t open_flags, + const ReopenCallback& callback) override; + void AsBuffer(const AsBufferCallback& callback) override; + void Ioctl(uint32_t request, + mojo::Array in_values, + const IoctlCallback& callback) override; + + // Warning: |this| may be destroyed by |StartRead()|. + void StartRead(); + void CompleteRead(mojo::files::Error error, mojo::Array data); + + struct PendingRead { + PendingRead(uint32_t num_bytes, const ReadCallback& callback); + ~PendingRead(); + + uint32_t num_bytes; + ReadCallback callback; + }; + + Client* client_; + bool is_closed_; + std::deque pending_read_queue_; + + // If non-null |*was_destroyed_| is set to true on destruction. + bool* was_destroyed_; + + mojo::Binding binding_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(InputStreamFile); +}; + +} // namespace files_impl + +#endif // MOJO_SERVICES_FILES_PUBLIC_CPP_INPUT_STREAM_FILE_H_ diff --git a/mojo/services/files/public/cpp/lib/input_stream_file.cc b/mojo/services/files/public/cpp/lib/input_stream_file.cc new file mode 100644 index 000000000..740844257 --- /dev/null +++ b/mojo/services/files/public/cpp/lib/input_stream_file.cc @@ -0,0 +1,302 @@ +// Copyright 2015 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 "mojo/services/files/public/cpp/input_stream_file.h" + +#include + +#include "mojo/public/cpp/environment/logging.h" + +namespace files_impl { + +InputStreamFile::PendingRead::PendingRead(uint32_t num_bytes, + const ReadCallback& callback) + : num_bytes(num_bytes), callback(callback) {} + +InputStreamFile::PendingRead::~PendingRead() {} + +// static +std::unique_ptr InputStreamFile::Create( + Client* client, + mojo::InterfaceRequest request) { + // TODO(vtl): Use make_unique when we have C++14. + return std::unique_ptr( + new InputStreamFile(client, request.Pass())); +} + +InputStreamFile::~InputStreamFile() { + if (was_destroyed_) + *was_destroyed_ = true; +} + +InputStreamFile::InputStreamFile( + Client* client, + mojo::InterfaceRequest request) + : client_(client), + is_closed_(false), + was_destroyed_(nullptr), + binding_(this, request.Pass()) { + binding_.set_connection_error_handler([this]() { + if (client_) + client_->OnClosed(); + }); +} + +void InputStreamFile::Close(const CloseCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Call pending read callbacks? + + is_closed_ = true; + callback.Run(mojo::files::Error::OK); + + if (client_) + client_->OnClosed(); +} + +void InputStreamFile::Read(uint32_t num_bytes_to_read, + int64_t offset, + mojo::files::Whence whence, + const ReadCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, mojo::Array()); + return; + } + + if (offset != 0 || whence != mojo::files::Whence::FROM_CURRENT) { + // TODO(vtl): Is this the "right" behavior? + callback.Run(mojo::files::Error::INVALID_ARGUMENT, mojo::Array()); + return; + } + + bool should_start_read = pending_read_queue_.empty(); + pending_read_queue_.push_back(PendingRead(num_bytes_to_read, callback)); + if (should_start_read) + StartRead(); +} + +void InputStreamFile::Write(mojo::Array bytes_to_write, + int64_t offset, + mojo::files::Whence whence, + const WriteCallback& callback) { + MOJO_DCHECK(!bytes_to_write.is_null()); + + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, 0); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE, 0); +} + +void InputStreamFile::ReadToStream(mojo::ScopedDataPipeProducerHandle source, + int64_t offset, + mojo::files::Whence whence, + int64_t num_bytes_to_read, + const ReadToStreamCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl) + MOJO_DLOG(ERROR) << "Not implemented"; + callback.Run(mojo::files::Error::UNIMPLEMENTED); +} + +void InputStreamFile::WriteFromStream(mojo::ScopedDataPipeConsumerHandle sink, + int64_t offset, + mojo::files::Whence whence, + const WriteFromStreamCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE); +} + +void InputStreamFile::Tell(const TellCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, 0); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE, 0); +} + +void InputStreamFile::Seek(int64_t offset, + mojo::files::Whence whence, + const SeekCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, 0); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE, 0); +} + +void InputStreamFile::Stat(const StatCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, nullptr); + return; + } + + // TODO(vtl) + MOJO_DLOG(ERROR) << "Not implemented"; + callback.Run(mojo::files::Error::UNIMPLEMENTED, nullptr); +} + +void InputStreamFile::Truncate(int64_t size, const TruncateCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE); +} + +void InputStreamFile::Touch(mojo::files::TimespecOrNowPtr atime, + mojo::files::TimespecOrNowPtr mtime, + const TouchCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE); +} + +void InputStreamFile::Dup(mojo::InterfaceRequest file, + const DupCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE); +} + +void InputStreamFile::Reopen(mojo::InterfaceRequest file, + uint32_t open_flags, + const ReopenCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE); +} + +void InputStreamFile::AsBuffer(const AsBufferCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, mojo::ScopedSharedBufferHandle()); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE, + mojo::ScopedSharedBufferHandle()); +} + +void InputStreamFile::Ioctl(uint32_t request, + mojo::Array in_values, + const IoctlCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, mojo::Array()); + return; + } + + callback.Run(mojo::files::Error::UNIMPLEMENTED, mojo::Array()); +} + +void InputStreamFile::StartRead() { + MOJO_DCHECK(!pending_read_queue_.empty()); + + // If we don't have a client, just drain all the reads. + if (!client_) { + while (!pending_read_queue_.empty()) { + // TODO(vtl): Is this what we want? + pending_read_queue_.front().callback.Run(mojo::files::Error::UNAVAILABLE, + mojo::Array()); + pending_read_queue_.pop_front(); + } + return; + } + + do { + // Find a non-zero-byte read, completing any zero-byte reads at the front of + // the queue. Note that we do this in FIFO order (thus couldn't have + // completed them earlier). + while (!pending_read_queue_.front().num_bytes) { + pending_read_queue_.front().callback.Run(mojo::files::Error::OK, + mojo::Array()); + pending_read_queue_.pop_front(); + + if (pending_read_queue_.empty()) + return; + } + + // Binding |this| is OK, since the client must not call the callback if we + // are destroyed. + mojo::files::Error error = mojo::files::Error::INTERNAL; + mojo::Array data; + // Detect if we were destroyed inside |RequestData()|. + bool was_destroyed = false; + MOJO_CHECK(!was_destroyed_); + was_destroyed_ = &was_destroyed; + bool synchronous_completion = client_->RequestData( + pending_read_queue_.front().num_bytes, &error, &data, + [this](mojo::files::Error e, mojo::Array d) { + CompleteRead(e, d.Pass()); + if (!pending_read_queue_.empty()) + StartRead(); + }); + if (was_destroyed) + return; + was_destroyed_ = nullptr; + if (synchronous_completion) + CompleteRead(error, data.Pass()); + else + return; // Asynchronous completion. + } while (!pending_read_queue_.empty()); +} + +void InputStreamFile::CompleteRead(mojo::files::Error error, + mojo::Array data) { + MOJO_CHECK(!pending_read_queue_.empty()); + + if (error != mojo::files::Error::OK) { + pending_read_queue_.front().callback.Run(error, mojo::Array()); + pending_read_queue_.pop_front(); + return; + } + + MOJO_CHECK(!data.is_null()); + MOJO_CHECK(data.size() <= pending_read_queue_.front().num_bytes); + pending_read_queue_.front().callback.Run(mojo::files::Error::OK, data.Pass()); + pending_read_queue_.pop_front(); +} + +} // namespace files_impl diff --git a/mojo/services/files/public/cpp/lib/output_stream_file.cc b/mojo/services/files/public/cpp/lib/output_stream_file.cc new file mode 100644 index 000000000..a1a068aec --- /dev/null +++ b/mojo/services/files/public/cpp/lib/output_stream_file.cc @@ -0,0 +1,232 @@ +// Copyright 2015 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 "mojo/services/files/public/cpp/output_stream_file.h" + +#include "mojo/public/cpp/environment/logging.h" + +namespace files_impl { + +// static +std::unique_ptr OutputStreamFile::Create( + Client* client, + mojo::InterfaceRequest request) { + // TODO(vtl): Use make_unique when we have C++14. + return std::unique_ptr( + new OutputStreamFile(client, request.Pass())); +} + +OutputStreamFile::~OutputStreamFile() {} + +OutputStreamFile::OutputStreamFile( + Client* client, + mojo::InterfaceRequest request) + : client_(client), is_closed_(false), binding_(this, request.Pass()) { + binding_.set_connection_error_handler([this]() { + if (client_) + client_->OnClosed(); + }); +} + +void OutputStreamFile::Close(const CloseCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + is_closed_ = true; + callback.Run(mojo::files::Error::OK); + + if (client_) + client_->OnClosed(); +} + +void OutputStreamFile::Read(uint32_t num_bytes_to_read, + int64_t offset, + mojo::files::Whence whence, + const ReadCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, mojo::Array()); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE, mojo::Array()); +} + +void OutputStreamFile::Write(mojo::Array bytes_to_write, + int64_t offset, + mojo::files::Whence whence, + const WriteCallback& callback) { + MOJO_DCHECK(!bytes_to_write.is_null()); + + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, 0); + return; + } + + if (offset != 0 || whence != mojo::files::Whence::FROM_CURRENT) { + // TODO(vtl): Is this the "right" behavior? + callback.Run(mojo::files::Error::INVALID_ARGUMENT, 0); + return; + } + + if (!bytes_to_write.size()) { + callback.Run(mojo::files::Error::OK, 0); + return; + } + + // We require the client to handle all the output, so we run the callback now. + // TODO(vtl): This means that the callback will be run (and the response + // message sent), even if the client decides to destroy us -- and thus close + // the message pipe -- in |OnDataReceived()|. This may makes throttling + // slightly less effective -- but increase parallelism -- since the writer may + // enqueue another write immediately. + callback.Run(mojo::files::Error::OK, + static_cast(bytes_to_write.size())); + + if (client_) + client_->OnDataReceived(&bytes_to_write.front(), bytes_to_write.size()); +} + +void OutputStreamFile::ReadToStream(mojo::ScopedDataPipeProducerHandle source, + int64_t offset, + mojo::files::Whence whence, + int64_t num_bytes_to_read, + const ReadToStreamCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE); +} + +void OutputStreamFile::WriteFromStream( + mojo::ScopedDataPipeConsumerHandle sink, + int64_t offset, + mojo::files::Whence whence, + const WriteFromStreamCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl) + MOJO_DLOG(ERROR) << "Not implemented"; + callback.Run(mojo::files::Error::UNIMPLEMENTED); +} + +void OutputStreamFile::Tell(const TellCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, 0); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE, 0); +} + +void OutputStreamFile::Seek(int64_t offset, + mojo::files::Whence whence, + const SeekCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, 0); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE, 0); +} + +void OutputStreamFile::Stat(const StatCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, nullptr); + return; + } + + // TODO(vtl) + MOJO_DLOG(ERROR) << "Not implemented"; + callback.Run(mojo::files::Error::UNIMPLEMENTED, nullptr); +} + +void OutputStreamFile::Truncate(int64_t size, + const TruncateCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE); +} + +void OutputStreamFile::Touch(mojo::files::TimespecOrNowPtr atime, + mojo::files::TimespecOrNowPtr mtime, + const TouchCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE); +} + +void OutputStreamFile::Dup(mojo::InterfaceRequest file, + const DupCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE); +} + +void OutputStreamFile::Reopen(mojo::InterfaceRequest file, + uint32_t open_flags, + const ReopenCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE); +} + +void OutputStreamFile::AsBuffer(const AsBufferCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, mojo::ScopedSharedBufferHandle()); + return; + } + + // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe + // unsupported/EINVAL is better.) + callback.Run(mojo::files::Error::UNAVAILABLE, + mojo::ScopedSharedBufferHandle()); +} + +void OutputStreamFile::Ioctl(uint32_t request, + mojo::Array in_values, + const IoctlCallback& callback) { + if (is_closed_) { + callback.Run(mojo::files::Error::CLOSED, mojo::Array()); + return; + } + + callback.Run(mojo::files::Error::UNIMPLEMENTED, mojo::Array()); +} + +} // namespace files_impl diff --git a/mojo/services/files/public/cpp/output_stream_file.h b/mojo/services/files/public/cpp/output_stream_file.h new file mode 100644 index 000000000..037ea9482 --- /dev/null +++ b/mojo/services/files/public/cpp/output_stream_file.h @@ -0,0 +1,114 @@ +// Copyright 2015 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. + +// This is a helper class for implementing a |mojo::files::File| that behaves +// like an "output stream" ("output" from the point of view of the client -- +// i.e., the client can write/stream output to it, but not read or seek). + +#ifndef MOJO_SERVICES_FILES_PUBLIC_CPP_OUTPUT_STREAM_FILE_H_ +#define MOJO_SERVICES_FILES_PUBLIC_CPP_OUTPUT_STREAM_FILE_H_ + +#include + +#include + +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/services/files/public/interfaces/file.mojom.h" +#include "mojo/services/files/public/interfaces/types.mojom.h" + +namespace files_impl { + +class OutputStreamFile : public mojo::files::File { + public: + // The |Client| receives data written to the stream "file" as well as other + // notifications (e.g., of the "file" being closed). From any of the methods + // below, the client may choose to destroy the |OutputStreamFile|. + class Client { + public: + // Called when we receive data from the stream "file". + // TODO(vtl): Maybe add a way to throttle (e.g., for the client to not + // accept all the data). + virtual void OnDataReceived(const void* bytes, size_t num_bytes) = 0; + + // Called when the stream "file" is closed, via |Close()| or due to the + // other end of the message pipe being closed. (This will not be called due + // the |OutputStreamFile| being destroyed.) + virtual void OnClosed() = 0; + + protected: + virtual ~Client() {} + }; + + // Static factory method. |client| may be null, but if not it should typically + // outlive us (see |set_client()|). + static std::unique_ptr Create( + Client* client, + mojo::InterfaceRequest request); + + ~OutputStreamFile() override; + + // Sets the client (which may be null, in which case all |Write()|s to the + // stream "file" will just succeed). If non-null, |client| must be valid + // whenever the run (a.k.a. message) loop is run, i.e., whenever a client + // method may be called. + void set_client(Client* client) { client_ = client; } + + private: + OutputStreamFile(Client* client, + mojo::InterfaceRequest request); + + // We should only be deleted by "ourself" (via the strong binding). + friend class mojo::Binding; + + // |mojo::files::File| implementation: + void Close(const CloseCallback& callback) override; + void Read(uint32_t num_bytes_to_read, + int64_t offset, + mojo::files::Whence whence, + const ReadCallback& callback) override; + void Write(mojo::Array bytes_to_write, + int64_t offset, + mojo::files::Whence whence, + const WriteCallback& callback) override; + void ReadToStream(mojo::ScopedDataPipeProducerHandle source, + int64_t offset, + mojo::files::Whence whence, + int64_t num_bytes_to_read, + const ReadToStreamCallback& callback) override; + void WriteFromStream(mojo::ScopedDataPipeConsumerHandle sink, + int64_t offset, + mojo::files::Whence whence, + const WriteFromStreamCallback& callback) override; + void Tell(const TellCallback& callback) override; + void Seek(int64_t offset, + mojo::files::Whence whence, + const SeekCallback& callback) override; + void Stat(const StatCallback& callback) override; + void Truncate(int64_t size, const TruncateCallback& callback) override; + void Touch(mojo::files::TimespecOrNowPtr atime, + mojo::files::TimespecOrNowPtr mtime, + const TouchCallback& callback) override; + void Dup(mojo::InterfaceRequest file, + const DupCallback& callback) override; + void Reopen(mojo::InterfaceRequest file, + uint32_t open_flags, + const ReopenCallback& callback) override; + void AsBuffer(const AsBufferCallback& callback) override; + void Ioctl(uint32_t request, + mojo::Array in_values, + const IoctlCallback& callback) override; + + Client* client_; + bool is_closed_; + + mojo::Binding binding_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(OutputStreamFile); +}; + +} // namespace files_impl + +#endif // MOJO_SERVICES_FILES_PUBLIC_CPP_OUTPUT_STREAM_FILE_H_ diff --git a/mojo/services/files/public/cpp/tests/input_stream_file_unittest.cc b/mojo/services/files/public/cpp/tests/input_stream_file_unittest.cc new file mode 100644 index 000000000..71fb5bf1b --- /dev/null +++ b/mojo/services/files/public/cpp/tests/input_stream_file_unittest.cc @@ -0,0 +1,539 @@ +// Copyright 2015 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 "files/public/cpp/input_stream_file.h" + +#include + +#include +#include + +#include "files/public/interfaces/files.mojom.h" +#include "files/public/interfaces/types.mojom.h" +#include "mojo/public/cpp/application/application_test_base.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/environment/logging.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/utility/run_loop.h" + +namespace files_impl { +namespace { + +using InputStreamFileTest = mojo::test::ApplicationTestBase; + +void QuitMessageLoop() { + mojo::RunLoop::current()->Quit(); +} + +void RunMessageLoop() { + mojo::RunLoop::current()->Run(); +} + +void RunMessageLoopUntilIdle() { + mojo::RunLoop::current()->RunUntilIdle(); +} + +void PostTaskToMessageLoop(const mojo::Closure& task) { + mojo::RunLoop::current()->PostDelayedTask(task, 0); +} + +// Converts a string to a |mojo::Array|. +mojo::Array StringToArray(const std::string& s) { + auto rv = mojo::Array::New(s.size()); + if (s.size()) + memcpy(&rv[0], &s[0], s.size()); + return rv; +} + +// Converts a |mojo::Array| to a string. If the array is null, returns +// the string "ARRAY_IS_NULL". +std::string ArrayToString(const mojo::Array& a) { + if (a.is_null()) + return std::string("ARRAY_IS_NULL"); + return a.size() ? std::string(reinterpret_cast(&a[0]), a.size()) + : std::string(); +} + +class TestClient : public InputStreamFile::Client { + public: + TestClient() { Reset(); } + ~TestClient() override {} + + // Note: This doesn't reset |callback_|. + void Reset() { + data_ = StringToArray("OOPS"); + complete_synchronously_ = true; + got_request_data_ = false; + got_on_closed_ = false; + } + + // Completes a pending callback. Note: |RequestData()| may be called again + // "inside" this (i.e., "inside" the callback). + void RunRequestDataCallback(mojo::Array data) { + MOJO_CHECK(!callback_.is_null()); + RequestDataCallback callback; + std::swap(callback, callback_); + callback.Run(mojo::files::Error::OK, data.Pass()); + } + + void set_data(mojo::Array data) { + MOJO_CHECK(!data.is_null()); + data_ = data.Pass(); + } + void set_complete_synchronously(bool complete_synchronously) { + complete_synchronously_ = complete_synchronously; + } + + bool got_request_data() const { return got_request_data_; } + bool got_on_closed() const { return got_on_closed_; } + + private: + // |InputStreamFile::Client|: + bool RequestData(size_t max_num_bytes, + mojo::files::Error* error, + mojo::Array* data, + const RequestDataCallback& callback) override { + MOJO_CHECK(max_num_bytes); + MOJO_CHECK(error); + MOJO_CHECK(data); + MOJO_CHECK(!callback.is_null()); + + // This shouldn't be called while a callback is pending. + MOJO_CHECK(callback_.is_null()); + + got_request_data_ = true; + QuitMessageLoop(); + + if (!complete_synchronously_) { + callback_ = callback; + return false; + } + + *error = mojo::files::Error::OK; + *data = data_.Clone(); + return true; + } + void OnClosed() override { + got_on_closed_ = true; + QuitMessageLoop(); + } + + mojo::Array data_; + bool complete_synchronously_; + + bool got_request_data_; + bool got_on_closed_; + + RequestDataCallback callback_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestClient); +}; + +void TestReadSync(mojo::files::File* file, + TestClient* client, + const std::string& s) { + bool read_cb_called = false; + mojo::files::Error error = mojo::files::Error::INTERNAL; + mojo::Array data; + file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT, + [&read_cb_called, &error, &data](mojo::files::Error e, + mojo::Array d) { + read_cb_called = true; + error = e; + data = d.Pass(); + QuitMessageLoop(); + }); + if (client) { + // If there's a client, since we're running everything on one thread, the + // impl (which will call the client, which will quit the message loop) will + // get called before the callback. + client->Reset(); + client->set_data(StringToArray(s)); + RunMessageLoop(); + EXPECT_TRUE(client->got_request_data()); + EXPECT_FALSE(client->got_on_closed()); + EXPECT_FALSE(read_cb_called); + // Spin the message loop again to get the callback. + client->Reset(); + RunMessageLoop(); + EXPECT_FALSE(client->got_request_data()); + EXPECT_FALSE(client->got_on_closed()); + EXPECT_TRUE(read_cb_called); + EXPECT_EQ(mojo::files::Error::OK, error); + EXPECT_EQ(s, ArrayToString(data)); + } else { + // Otherwise, only the read callback will be called and quit the message + // loop. + RunMessageLoop(); + EXPECT_TRUE(read_cb_called); + EXPECT_EQ(mojo::files::Error::UNAVAILABLE, error); + EXPECT_TRUE(data.is_null()); + } +} + +void TestReadAsync(mojo::files::File* file, + TestClient* client, + const std::string& s) { + MOJO_CHECK(client); + + bool read_cb_called = false; + mojo::files::Error error = mojo::files::Error::INTERNAL; + mojo::Array data; + file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT, + [&read_cb_called, &error, &data](mojo::files::Error e, + mojo::Array d) { + read_cb_called = true; + error = e; + data = d.Pass(); + QuitMessageLoop(); + }); + client->Reset(); + client->set_complete_synchronously(false); + RunMessageLoop(); + EXPECT_TRUE(client->got_request_data()); + EXPECT_FALSE(client->got_on_closed()); + EXPECT_FALSE(read_cb_called); + // The read callback won't get called until we tell the client to run its + // callback. + client->Reset(); + client->RunRequestDataCallback(StringToArray(s)); + EXPECT_FALSE(client->got_request_data()); + EXPECT_FALSE(client->got_on_closed()); + EXPECT_FALSE(read_cb_called); + // Spin the message loop again to get the read callback. + client->Reset(); + RunMessageLoop(); + EXPECT_FALSE(client->got_request_data()); + EXPECT_FALSE(client->got_on_closed()); + EXPECT_TRUE(read_cb_called); + EXPECT_EQ(mojo::files::Error::OK, error); + EXPECT_EQ(s, ArrayToString(data)); +} + +void TestClose(mojo::files::File* file, TestClient* client) { + bool close_cb_called = false; + mojo::files::Error error = mojo::files::Error::INTERNAL; + file->Close([&close_cb_called, &error](mojo::files::Error e) { + close_cb_called = true; + error = e; + QuitMessageLoop(); + }); + if (client) { + // (This is analogous to |TestReadSync()|.) + client->Reset(); + RunMessageLoop(); + EXPECT_FALSE(client->got_request_data()); + EXPECT_TRUE(client->got_on_closed()); + EXPECT_FALSE(close_cb_called); + client->Reset(); + RunMessageLoop(); + EXPECT_FALSE(client->got_request_data()); + EXPECT_FALSE(client->got_on_closed()); + } else { + RunMessageLoop(); + } + EXPECT_TRUE(close_cb_called); + EXPECT_EQ(mojo::files::Error::OK, error); +} + +TEST_F(InputStreamFileTest, BasicSync) { + mojo::files::FilePtr file; + TestClient client; + std::unique_ptr file_impl = + InputStreamFile::Create(&client, GetProxy(&file)); + + TestReadSync(file.get(), &client, "hello"); + TestReadSync(file.get(), &client, "world"); + TestClose(file.get(), &client); +} + +TEST_F(InputStreamFileTest, BasicAsync) { + mojo::files::FilePtr file; + TestClient client; + std::unique_ptr file_impl = + InputStreamFile::Create(&client, GetProxy(&file)); + + TestReadAsync(file.get(), &client, "hello"); + TestReadAsync(file.get(), &client, "world"); + TestClose(file.get(), &client); +} + +TEST_F(InputStreamFileTest, SetClient) { + mojo::files::FilePtr file; + TestClient client1; + std::unique_ptr file_impl = + InputStreamFile::Create(&client1, GetProxy(&file)); + + TestReadSync(file.get(), &client1, "hello"); + + TestClient client2; + file_impl->set_client(&client2); + TestReadSync(file.get(), &client2, "world"); + + file_impl->set_client(&client1); + TestReadAsync(file.get(), &client1, "!"); + TestClose(file.get(), &client1); +} + +TEST_F(InputStreamFileTest, NullClient) { + mojo::files::FilePtr file; + std::unique_ptr file_impl = + InputStreamFile::Create(nullptr, GetProxy(&file)); + + TestReadSync(file.get(), nullptr, "hello"); + TestClose(file.get(), nullptr); +} + +TEST_F(InputStreamFileTest, SetNullClient) { + mojo::files::FilePtr file; + TestClient client; + std::unique_ptr file_impl = + InputStreamFile::Create(&client, GetProxy(&file)); + + TestReadSync(file.get(), &client, "hello"); + + file_impl->set_client(nullptr); + client.Reset(); + TestReadSync(file.get(), nullptr, "hello"); + TestClose(file.get(), nullptr); + EXPECT_FALSE(client.got_request_data()); + EXPECT_FALSE(client.got_on_closed()); +} + +TEST_F(InputStreamFileTest, ImplOnlyClosesMessagePipeOnDestruction) { + mojo::files::FilePtr file; + std::unique_ptr file_impl = + InputStreamFile::Create(nullptr, GetProxy(&file)); + bool got_connection_error = false; + file.set_connection_error_handler([&got_connection_error]() { + got_connection_error = true; + QuitMessageLoop(); + }); + + TestClose(file.get(), nullptr); + // The impl should only close its end when it's destroyed (even if |Close()| + // has been called). + RunMessageLoopUntilIdle(); + EXPECT_FALSE(got_connection_error); + file_impl.reset(); + RunMessageLoop(); + EXPECT_TRUE(got_connection_error); +} + +TEST_F(InputStreamFileTest, ClosingMessagePipeCausesOnClosed) { + mojo::files::FilePtr file; + TestClient client; + std::unique_ptr file_impl = + InputStreamFile::Create(&client, GetProxy(&file)); + + file.reset(); + RunMessageLoop(); + EXPECT_FALSE(client.got_request_data()); + EXPECT_TRUE(client.got_on_closed()); +} + +// Clients may own the impl (and this is a typical pattern). This client will +// own/destroy its impl on any |Client| call (and we'll test that this doesn't +// result in any additional calls to the client). +class TestClientDestroysImplClient : public InputStreamFile::Client { + public: + explicit TestClientDestroysImplClient( + mojo::InterfaceRequest request) + : file_impl_(InputStreamFile::Create(this, request.Pass())) {} + ~TestClientDestroysImplClient() override {} + + private: + // InputStreamFile::Client|: + bool RequestData(size_t /*max_num_bytes*/, + mojo::files::Error* /*error*/, + mojo::Array* /*data*/, + const RequestDataCallback& /*callback*/) override { + // We reset the impl on any call, and afterwards it shouldn't call us. + EXPECT_TRUE(file_impl_); + file_impl_.reset(); + return true; + } + void OnClosed() override { + // We reset the impl on any call, and afterwards it shouldn't call us. + EXPECT_TRUE(file_impl_); + file_impl_.reset(); + } + + std::unique_ptr file_impl_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestClientDestroysImplClient); +}; + +TEST_F(InputStreamFileTest, ClientDestroysImpl) { + // Test destruction due to reading. + { + mojo::files::FilePtr file; + TestClientDestroysImplClient client(GetProxy(&file)); + bool got_connection_error = false; + file.set_connection_error_handler([&got_connection_error]() { + got_connection_error = true; + QuitMessageLoop(); + }); + // If the impl is destroyed while trying to answer a read, it doesn't + // respond. + // TODO(vtl): I'm not sure if this is the best behavior. Maybe it should + // respond with an error? + file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT, + [](mojo::files::Error, mojo::Array) { + MOJO_CHECK(false) << "Not reached"; + }); + RunMessageLoop(); + EXPECT_TRUE(got_connection_error); + } + + // Test destruction due to closing. + { + mojo::files::FilePtr file; + TestClientDestroysImplClient client(GetProxy(&file)); + bool got_connection_error = false; + file.set_connection_error_handler([&got_connection_error]() { + got_connection_error = true; + QuitMessageLoop(); + }); + TestClose(file.get(), nullptr); + if (!got_connection_error) + RunMessageLoop(); + EXPECT_TRUE(got_connection_error); + } +} + +// This responds synchronously to any (non-zero-byte) read with a single byte, +// starting with 0 and incrementing each time. +class TestClientFifoSync : public InputStreamFile::Client { + public: + explicit TestClientFifoSync(mojo::InterfaceRequest request) + : file_impl_(InputStreamFile::Create(this, request.Pass())), + next_byte_(0u) {} + ~TestClientFifoSync() override {} + + private: + // InputStreamFile::Client|: + bool RequestData(size_t max_num_bytes, + mojo::files::Error* error, + mojo::Array* data, + const RequestDataCallback& callback) override { + *error = mojo::files::Error::OK; + data->resize(1); + (*data)[0] = next_byte_++; + return true; + } + void OnClosed() override {} + + std::unique_ptr file_impl_; + uint8_t next_byte_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestClientFifoSync); +}; + +// Like |TestClientFifoSync|, but asynchronous. +class TestClientFifoAsync : public InputStreamFile::Client { + public: + explicit TestClientFifoAsync( + mojo::InterfaceRequest request) + : file_impl_(InputStreamFile::Create(this, request.Pass())), + next_byte_(0u) {} + ~TestClientFifoAsync() override {} + + private: + // InputStreamFile::Client|: + bool RequestData(size_t max_num_bytes, + mojo::files::Error* error, + mojo::Array* data, + const RequestDataCallback& callback) override { + PostTaskToMessageLoop([this, callback]() { + mojo::Array data; + data.push_back(next_byte_++); + callback.Run(mojo::files::Error::OK, data.Pass()); + }); + return false; + } + void OnClosed() override {} + + std::unique_ptr file_impl_; + uint8_t next_byte_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestClientFifoAsync); +}; + +void TestFifo(mojo::files::File* file) { + int expect_callback = 0; + uint8_t expect_byte = 0; + + // Test a couple of zero-byte reads. + file->Read(0u, 0, mojo::files::Whence::FROM_CURRENT, + [&expect_callback](mojo::files::Error e, mojo::Array a) { + EXPECT_EQ(0, expect_callback++); + EXPECT_EQ(mojo::files::Error::OK, e); + EXPECT_EQ(0u, a.size()); + }); + file->Read(0u, 0, mojo::files::Whence::FROM_CURRENT, + [&expect_callback](mojo::files::Error e, mojo::Array a) { + EXPECT_EQ(1, expect_callback++); + EXPECT_EQ(mojo::files::Error::OK, e); + EXPECT_EQ(0u, a.size()); + }); + + // Test a couple of non-zero-byte reads. + file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT, + [&expect_callback, &expect_byte](mojo::files::Error e, + mojo::Array a) { + EXPECT_EQ(2, expect_callback++); + EXPECT_EQ(mojo::files::Error::OK, e); + EXPECT_EQ(1u, a.size()); + EXPECT_EQ(expect_byte++, a[0]); + }); + file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT, + [&expect_callback, &expect_byte](mojo::files::Error e, + mojo::Array a) { + EXPECT_EQ(3, expect_callback++); + EXPECT_EQ(mojo::files::Error::OK, e); + EXPECT_EQ(1u, a.size()); + EXPECT_EQ(expect_byte++, a[0]); + }); + // Throw in a zero-byte read. + file->Read(0u, 0, mojo::files::Whence::FROM_CURRENT, + [&expect_callback](mojo::files::Error e, mojo::Array a) { + EXPECT_EQ(4, expect_callback++); + EXPECT_EQ(mojo::files::Error::OK, e); + EXPECT_EQ(0u, a.size()); + }); + // And a final non-zero-byte read (we'll quit the message loop). + file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT, + [&expect_callback, &expect_byte](mojo::files::Error e, + mojo::Array a) { + EXPECT_EQ(5, expect_callback++); + EXPECT_EQ(mojo::files::Error::OK, e); + EXPECT_EQ(1u, a.size()); + EXPECT_EQ(expect_byte++, a[0]); + QuitMessageLoop(); + }); + RunMessageLoop(); + + // Check that all the callbacks ran. + EXPECT_EQ(6, expect_callback); + EXPECT_EQ(3u, expect_byte); +} + +// Tests that if multiple reads are sent, they are completed in FIFO order. This +// tests the synchronous completion path. +TEST_F(InputStreamFileTest, FifoSync) { + mojo::files::FilePtr file; + TestClientFifoSync client(GetProxy(&file)); + TestFifo(file.get()); +} + +// Like |InputStreamFileTest.FifoSync|, but asynchronous. +TEST_F(InputStreamFileTest, FifoAsync) { + mojo::files::FilePtr file; + TestClientFifoAsync client(GetProxy(&file)); + TestFifo(file.get()); +} + +} // namespace +} // namespace files_impl diff --git a/mojo/services/files/public/cpp/tests/output_stream_file_unittest.cc b/mojo/services/files/public/cpp/tests/output_stream_file_unittest.cc new file mode 100644 index 000000000..678131b71 --- /dev/null +++ b/mojo/services/files/public/cpp/tests/output_stream_file_unittest.cc @@ -0,0 +1,287 @@ +// Copyright 2015 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 "files/public/cpp/output_stream_file.h" + +#include + +#include +#include + +#include "files/public/interfaces/files.mojom.h" +#include "files/public/interfaces/types.mojom.h" +#include "mojo/public/cpp/application/application_test_base.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/utility/run_loop.h" + +namespace files_impl { +namespace { + +using OutputStreamFileTest = mojo::test::ApplicationTestBase; + +void QuitMessageLoop() { + mojo::RunLoop::current()->Quit(); +} + +void RunMessageLoop() { + mojo::RunLoop::current()->Run(); +} + +void RunMessageLoopUntilIdle() { + mojo::RunLoop::current()->RunUntilIdle(); +} + +// Converts a string to a |mojo::Array|. +mojo::Array StringToArray(const std::string& s) { + auto rv = mojo::Array::New(s.size()); + if (s.size()) + memcpy(&rv[0], &s[0], s.size()); + return rv; +} + +class TestClient : public OutputStreamFile::Client { + public: + TestClient() { Reset(); } + ~TestClient() override {} + + void Reset() { + got_on_data_received_ = false; + data_ = std::string(); + got_on_closed_ = false; + } + + bool got_on_data_received() const { return got_on_data_received_; } + const std::string& data() const { return data_; } + bool got_on_closed() const { return got_on_closed_; } + + private: + // |OutputStreamFile::Client|: + void OnDataReceived(const void* bytes, size_t num_bytes) override { + got_on_data_received_ = true; + data_ = std::string(static_cast(bytes), num_bytes); + QuitMessageLoop(); + } + void OnClosed() override { + got_on_closed_ = true; + QuitMessageLoop(); + } + + bool got_on_data_received_; + std::string data_; + bool got_on_closed_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestClient); +}; + +void TestWrite(mojo::files::File* file, + TestClient* client, + const std::string& s) { + bool write_cb_called = false; + mojo::files::Error error = mojo::files::Error::INTERNAL; + uint32_t num_bytes_written = 0; + file->Write(StringToArray(s), 0, mojo::files::Whence::FROM_CURRENT, + [&write_cb_called, &error, &num_bytes_written]( + mojo::files::Error e, uint32_t n) { + write_cb_called = true; + error = e; + num_bytes_written = n; + QuitMessageLoop(); + }); + if (client) { + // If there's a client, since we're running everything on one thread, the + // impl (which will call the client, which will quit the message loop) will + // get called before the callback. + client->Reset(); + RunMessageLoop(); + EXPECT_TRUE(client->got_on_data_received()); + EXPECT_EQ(s, client->data()); + EXPECT_FALSE(client->got_on_closed()); + EXPECT_FALSE(write_cb_called); + // Spin the message loop again to get the callback. + client->Reset(); + RunMessageLoop(); + EXPECT_FALSE(client->got_on_data_received()); + EXPECT_FALSE(client->got_on_closed()); + } else { + // Otherwise, only the write callback will be called and quit the message + // loop. + RunMessageLoop(); + } + EXPECT_TRUE(write_cb_called); + EXPECT_EQ(mojo::files::Error::OK, error); + EXPECT_EQ(s.size(), num_bytes_written); +} + +void TestClose(mojo::files::File* file, TestClient* client) { + bool close_cb_called = false; + mojo::files::Error error = mojo::files::Error::INTERNAL; + file->Close([&close_cb_called, &error](mojo::files::Error e) { + close_cb_called = true; + error = e; + QuitMessageLoop(); + }); + // (This is analogous to |TestWrite()|.) + if (client) { + client->Reset(); + RunMessageLoop(); + EXPECT_FALSE(client->got_on_data_received()); + EXPECT_TRUE(client->got_on_closed()); + EXPECT_FALSE(close_cb_called); + client->Reset(); + RunMessageLoop(); + EXPECT_FALSE(client->got_on_data_received()); + EXPECT_FALSE(client->got_on_closed()); + } else { + RunMessageLoop(); + } + EXPECT_TRUE(close_cb_called); + EXPECT_EQ(mojo::files::Error::OK, error); +} + +TEST_F(OutputStreamFileTest, Basic) { + mojo::files::FilePtr file; + TestClient client; + std::unique_ptr file_impl = + OutputStreamFile::Create(&client, GetProxy(&file)); + + TestWrite(file.get(), &client, "hello"); + TestWrite(file.get(), &client, "world"); + TestClose(file.get(), &client); +} + +TEST_F(OutputStreamFileTest, SetClient) { + mojo::files::FilePtr file; + TestClient client1; + std::unique_ptr file_impl = + OutputStreamFile::Create(&client1, GetProxy(&file)); + + TestWrite(file.get(), &client1, "hello"); + + TestClient client2; + file_impl->set_client(&client2); + TestWrite(file.get(), &client2, "world"); + + file_impl->set_client(&client1); + TestWrite(file.get(), &client1, "!"); + TestClose(file.get(), &client1); +} + +TEST_F(OutputStreamFileTest, NullClient) { + mojo::files::FilePtr file; + std::unique_ptr file_impl = + OutputStreamFile::Create(nullptr, GetProxy(&file)); + + TestWrite(file.get(), nullptr, "hello"); + + TestClient client; + file_impl->set_client(&client); + TestWrite(file.get(), &client, "world"); + + file_impl->set_client(nullptr); + client.Reset(); + TestWrite(file.get(), nullptr, "!"); + TestClose(file.get(), nullptr); + EXPECT_FALSE(client.got_on_data_received()); + EXPECT_FALSE(client.got_on_closed()); +} + +TEST_F(OutputStreamFileTest, ImplOnlyClosesMessagePipeOnDestruction) { + mojo::files::FilePtr file; + std::unique_ptr file_impl = + OutputStreamFile::Create(nullptr, GetProxy(&file)); + bool got_connection_error = false; + file.set_connection_error_handler([&got_connection_error]() { + got_connection_error = true; + QuitMessageLoop(); + }); + + TestClose(file.get(), nullptr); + // The impl should only close its end when it's destroyed (even if |Close()| + // has been called). + RunMessageLoopUntilIdle(); + EXPECT_FALSE(got_connection_error); + file_impl.reset(); + RunMessageLoop(); + EXPECT_TRUE(got_connection_error); +} + +TEST_F(OutputStreamFileTest, ClosingMessagePipeCausesOnClosed) { + mojo::files::FilePtr file; + TestClient client; + std::unique_ptr file_impl = + OutputStreamFile::Create(&client, GetProxy(&file)); + + file.reset(); + RunMessageLoop(); + EXPECT_FALSE(client.got_on_data_received()); + EXPECT_TRUE(client.got_on_closed()); +} + +// Clients may own the impl (and this is a typical pattern). This client will +// own/destroy its impl on any |Client| call (and we'll test that this doesn't +// result in any additional calls to the client). +class TestClientDestroysImplClient : public OutputStreamFile::Client { + public: + explicit TestClientDestroysImplClient( + mojo::InterfaceRequest request) + : file_impl_(OutputStreamFile::Create(this, request.Pass())) {} + ~TestClientDestroysImplClient() override {} + + private: + // OutputStreamFile::Client|: + void OnDataReceived(const void* /*bytes*/, size_t /*num_bytes*/) override { + // We reset the impl on any call, and afterwards it shouldn't call us. + EXPECT_TRUE(file_impl_); + file_impl_.reset(); + } + void OnClosed() override { + // We reset the impl on any call, and afterwards it shouldn't call us. + EXPECT_TRUE(file_impl_); + file_impl_.reset(); + } + + std::unique_ptr file_impl_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(TestClientDestroysImplClient); +}; + +TEST_F(OutputStreamFileTest, ClientDestroysImpl) { + // Test destruction due to writing. + { + mojo::files::FilePtr file; + TestClientDestroysImplClient client(GetProxy(&file)); + bool got_connection_error = false; + file.set_connection_error_handler([&got_connection_error]() { + got_connection_error = true; + QuitMessageLoop(); + }); + // |TestClientDestroysImplClient| doesn't quit the message loop, so it + // behaves like a null client. + TestWrite(file.get(), nullptr, "hello"); + // The connection error may be called immediately after the write callback, + // in which case we have to spin the message loop again. + if (!got_connection_error) + RunMessageLoop(); + EXPECT_TRUE(got_connection_error); + } + + // Test destruction due to closing. + { + mojo::files::FilePtr file; + TestClientDestroysImplClient client(GetProxy(&file)); + bool got_connection_error = false; + file.set_connection_error_handler([&got_connection_error]() { + got_connection_error = true; + QuitMessageLoop(); + }); + TestClose(file.get(), nullptr); + if (!got_connection_error) + RunMessageLoop(); + EXPECT_TRUE(got_connection_error); + } +} + +} // namespace +} // namespace files_impl diff --git a/mojo/services/mojo_services.gni b/mojo/services/mojo_services.gni index eb6d5d74d..83eede136 100644 --- a/mojo/services/mojo_services.gni +++ b/mojo/services/mojo_services.gni @@ -8,12 +8,12 @@ # //mojo/services/X/public/interfaces to this list. mojo_services = [ - "//mojo/services/accessibility/public/interfaces", "//mojo/services/asset_bundle/public/interfaces", "//mojo/services/authenticating_url_loader_interceptor/public/interfaces", "//mojo/services/authentication/public/interfaces", - "//mojo/services/camera_roll/public/interfaces", + "//mojo/services/camera/public/interfaces", "//mojo/services/clipboard/public/interfaces", + "//mojo/services/contacts/public/interfaces", "//mojo/services/content_handler/public/interfaces", "//mojo/services/device_info/public/interfaces", "//mojo/services/files/public/interfaces", @@ -24,6 +24,7 @@ mojo_services = [ "//mojo/services/input_events/public/interfaces", "//mojo/services/keyboard/public/interfaces", "//mojo/services/location/public/interfaces", + "//mojo/services/native_support/public/interfaces", "//mojo/services/native_viewport/public/interfaces", "//mojo/services/navigation/public/interfaces", "//mojo/services/network/public/interfaces", diff --git a/mojo/services/accessibility/public/interfaces/BUILD.gn b/mojo/services/native_support/public/interfaces/BUILD.gn similarity index 68% rename from mojo/services/accessibility/public/interfaces/BUILD.gn rename to mojo/services/native_support/public/interfaces/BUILD.gn index 2b29c1a39..3dee937a4 100644 --- a/mojo/services/accessibility/public/interfaces/BUILD.gn +++ b/mojo/services/native_support/public/interfaces/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. +# Copyright 2015 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. @@ -7,12 +7,12 @@ import("$mojo_sdk_root/mojo/public/tools/bindings/mojom.gni") mojom("interfaces") { sources = [ - "accessibility.mojom", + "process.mojom", ] import_dirs = [ get_path_info("../../../", "abspath") ] - deps = [ - "../../../geometry/public/interfaces", + public_deps = [ + "../../../files/public/interfaces", ] } diff --git a/mojo/services/native_support/public/interfaces/process.mojom b/mojo/services/native_support/public/interfaces/process.mojom new file mode 100644 index 000000000..ca4010c86 --- /dev/null +++ b/mojo/services/native_support/public/interfaces/process.mojom @@ -0,0 +1,65 @@ +// Copyright 2015 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. + +[DartPackage="mojo_services"] +module native_support; + +import "files/public/interfaces/file.mojom"; +import "files/public/interfaces/types.mojom"; + +// Interface for dealing with (e.g., starting) "native" processes. +interface Process { + // Spawns a process, optionally redirecting stdin/stdout/stderr from/to the + // corresponding |mojo.files.File| (if null, redirects from/to /dev/null). + // |path| is the path to the binary to execute; |argv| is the argv to give to + // the process (if null, it just takes |argv[0]| to be |path| with no other + // arguments); |envp| is the environment to give to the process, consisting of + // an array of strings of the form "NAME=value" (if null, simply inherits the + // environment from the parent, whatever that is). + // TODO(vtl): This should really take an array of |mojo.files.File|s (or maybe + // two, one for input and the other for output), corresponding to FDs, but the + // C++ bindings generator doesn't support arrays of interfaces yet + // (https://github.com/domokit/mojo/issues/412). + // TODO(vtl): The implementation currently ignores |argv[0]| and always fills + // it in with |path|. + // TODO(vtl): Inheriting |envp| from the parent is somewhat dubious, and + // there's also no way to just specify modifications or limit inheritance. + Spawn(string path, + array? argv, + array? envp, + mojo.files.File? stdin_file, + mojo.files.File? stdout_file, + mojo.files.File? stderr_file, + ProcessController& process_controller) => (mojo.files.Error error); + // Like |Spawn()|, except that the child's stdin/stdout/stderr are redirected + // from/to |terminal_file|, which should be a |mojo.files.File| for a terminal + // (i.e., one that behaves like one, including responding to the required + // ioctls). + SpawnWithTerminal( + string path, + array? argv, + array? envp, + mojo.files.File terminal_file, + ProcessController& process_controller) => (mojo.files.Error error); +}; + +// Interface for controlling a process started by one of |Process|'s facilities +// (in particular, |Spawn()| or |SpawnWithTerminal()|). +// TODO(vtl): What does it do if this is closed (without being detached)? Kill +// with SIGHUP? +interface ProcessController { + // Wait for process completion. + // TODO(vtl): Add options (e.g., timeout)? + Wait() => (mojo.files.Error error, int32 exit_status); + + // Kill the process with the given signal (note: does not wait). |signal| + // should be nonnegative. This is not valid after a successful call to + // |Wait()|. + // TODO(vtl): Add constants for signals. (For standard POSIX signals, the + // values should be the same as the POSIX-specified values, so using POSIX + // macros for the values should always be OK.) + Kill(int32 signal) => (mojo.files.Error error); + + // TODO(vtl): Add a "Detach()"? +}; diff --git a/mojo/services/surfaces/public/cpp/surfaces_utils.cc b/mojo/services/surfaces/public/cpp/surfaces_utils.cc index 2e52fd265..ce6dd4731 100644 --- a/mojo/services/surfaces/public/cpp/surfaces_utils.cc +++ b/mojo/services/surfaces/public/cpp/surfaces_utils.cc @@ -31,7 +31,7 @@ SharedQuadStatePtr CreateDefaultSQS(const Size& size) { sqs->clip_rect = rect.Clone(); sqs->is_clipped = false; sqs->opacity = 1.f; - sqs->blend_mode = mojo::SK_XFERMODE_kSrc_Mode; + sqs->blend_mode = mojo::SkXfermode::kSrc_Mode; sqs->sorting_context_id = 0; return sqs.Pass(); } diff --git a/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom b/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom index a6c7a2227..abeb62f7d 100644 --- a/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom +++ b/mojo/services/url_response_disk_cache/public/interfaces/url_response_disk_cache.mojom @@ -24,11 +24,18 @@ interface URLResponseDiskCache { // to store content. This service guarantee that |cache_dir_path| will be // emptied when |file_path| content changes. For example, a content handler // that is backed by a VM that compiles files could have the VM use this - // directory to cache the compiled files. + // directory to cache the compiled files. After |Get| has been called and a + // response is returned, the caller must call |Validate| or |Update| to + // revalidate the entry. If this is not done, a future call to |Get| may + // return null. Get(string url) => (mojo.URLResponse? response, array? file_path, array? cache_dir_path); + // Validate the cache for the given |url|. This will enforce that |Get| will + // return an entry if it exists. + Validate(string url); + // Update the cache with the given response. Update(mojo.URLResponse response); diff --git a/mojo/services/view_manager/public/cpp/lib/view.cc b/mojo/services/view_manager/public/cpp/lib/view.cc index ff9ce1d32..5b11fe40d 100644 --- a/mojo/services/view_manager/public/cpp/lib/view.cc +++ b/mojo/services/view_manager/public/cpp/lib/view.cc @@ -129,14 +129,14 @@ bool ReorderImpl(View::Children* children, const size_t target_i = std::find(children->begin(), children->end(), relative) - children->begin(); - if ((direction == ORDER_DIRECTION_ABOVE && child_i == target_i + 1) || - (direction == ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) { + if ((direction == OrderDirection::ABOVE && child_i == target_i + 1) || + (direction == OrderDirection::BELOW && child_i + 1 == target_i)) { return false; } ScopedOrderChangedNotifier notifier(view, relative, direction); - const size_t dest_i = direction == ORDER_DIRECTION_ABOVE + const size_t dest_i = direction == OrderDirection::ABOVE ? (child_i < target_i ? target_i : target_i + 1) : (child_i < target_i ? target_i - 1 : target_i); children->erase(children->begin() + child_i); @@ -309,13 +309,13 @@ void View::RemoveChild(View* child) { void View::MoveToFront() { if (!parent_ || parent_->children_.back() == this) return; - Reorder(parent_->children_.back(), ORDER_DIRECTION_ABOVE); + Reorder(parent_->children_.back(), OrderDirection::ABOVE); } void View::MoveToBack() { if (!parent_ || parent_->children_.front() == this) return; - Reorder(parent_->children_.front(), ORDER_DIRECTION_BELOW); + Reorder(parent_->children_.front(), OrderDirection::BELOW); } void View::Reorder(View* relative, OrderDirection direction) { diff --git a/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc b/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc index 52d1b48c5..b8262d1a9 100644 --- a/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc +++ b/mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.cc @@ -242,7 +242,7 @@ Id ViewManagerClientImpl::CreateViewOnServer() { DCHECK(service_); const Id view_id = MakeTransportId(connection_id_, ++next_id_); service_->CreateView(view_id, [this](ErrorCode code) { - OnActionCompleted(code == ERROR_CODE_NONE); + OnActionCompleted(code == ErrorCode::NONE); }); return view_id; } diff --git a/mojo/services/view_manager/public/cpp/tests/view_unittest.cc b/mojo/services/view_manager/public/cpp/tests/view_unittest.cc index 09d1bde60..a49733c8c 100644 --- a/mojo/services/view_manager/public/cpp/tests/view_unittest.cc +++ b/mojo/services/view_manager/public/cpp/tests/view_unittest.cc @@ -492,11 +492,11 @@ TEST_F(ViewObserverTest, Order) { ASSERT_EQ(2U, changes.size()); EXPECT_EQ(&v11, changes[0].view); EXPECT_EQ(&v13, changes[0].relative_view); - EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[0].direction); + EXPECT_EQ(OrderDirection::ABOVE, changes[0].direction); EXPECT_EQ(&v11, changes[1].view); EXPECT_EQ(&v13, changes[1].relative_view); - EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[1].direction); + EXPECT_EQ(OrderDirection::ABOVE, changes[1].direction); } { @@ -512,11 +512,11 @@ TEST_F(ViewObserverTest, Order) { ASSERT_EQ(2U, changes.size()); EXPECT_EQ(&v11, changes[0].view); EXPECT_EQ(&v12, changes[0].relative_view); - EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[0].direction); + EXPECT_EQ(OrderDirection::BELOW, changes[0].direction); EXPECT_EQ(&v11, changes[1].view); EXPECT_EQ(&v12, changes[1].relative_view); - EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[1].direction); + EXPECT_EQ(OrderDirection::BELOW, changes[1].direction); } { @@ -524,7 +524,7 @@ TEST_F(ViewObserverTest, Order) { // Move v11 above v12. // Resulting order: v12. v11, v13 - v11.Reorder(&v12, ORDER_DIRECTION_ABOVE); + v11.Reorder(&v12, OrderDirection::ABOVE); EXPECT_EQ(&v12, v1.children().front()); EXPECT_EQ(&v13, v1.children().back()); @@ -532,11 +532,11 @@ TEST_F(ViewObserverTest, Order) { ASSERT_EQ(2U, changes.size()); EXPECT_EQ(&v11, changes[0].view); EXPECT_EQ(&v12, changes[0].relative_view); - EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[0].direction); + EXPECT_EQ(OrderDirection::ABOVE, changes[0].direction); EXPECT_EQ(&v11, changes[1].view); EXPECT_EQ(&v12, changes[1].relative_view); - EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[1].direction); + EXPECT_EQ(OrderDirection::ABOVE, changes[1].direction); } { @@ -544,7 +544,7 @@ TEST_F(ViewObserverTest, Order) { // Move v11 below v12. // Resulting order: v11, v12, v13 - v11.Reorder(&v12, ORDER_DIRECTION_BELOW); + v11.Reorder(&v12, OrderDirection::BELOW); EXPECT_EQ(&v11, v1.children().front()); EXPECT_EQ(&v13, v1.children().back()); @@ -552,11 +552,11 @@ TEST_F(ViewObserverTest, Order) { ASSERT_EQ(2U, changes.size()); EXPECT_EQ(&v11, changes[0].view); EXPECT_EQ(&v12, changes[0].relative_view); - EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[0].direction); + EXPECT_EQ(OrderDirection::BELOW, changes[0].direction); EXPECT_EQ(&v11, changes[1].view); EXPECT_EQ(&v12, changes[1].relative_view); - EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[1].direction); + EXPECT_EQ(OrderDirection::BELOW, changes[1].direction); } } diff --git a/services/sky/compositor/layer_host.cc b/services/sky/compositor/layer_host.cc index a12f165e4..ac58e5b1f 100644 --- a/services/sky/compositor/layer_host.cc +++ b/services/sky/compositor/layer_host.cc @@ -119,7 +119,7 @@ void LayerHost::Upload(TextureLayer* layer) { resource_manager_.CreateTransferableResource(layer); mojo::QuadPtr quad = mojo::Quad::New(); - quad->material = mojo::MATERIAL_TEXTURE_CONTENT; + quad->material = mojo::Material::TEXTURE_CONTENT; mojo::RectPtr rect = mojo::Rect::New(); rect->width = size.width(); diff --git a/services/sky/compositor/resource_manager.cc b/services/sky/compositor/resource_manager.cc index 4223f528d..4be8f620e 100644 --- a/services/sky/compositor/resource_manager.cc +++ b/services/sky/compositor/resource_manager.cc @@ -54,7 +54,7 @@ mojo::TransferableResourcePtr ResourceManager::CreateTransferableResource( mojo::TransferableResourcePtr resource = mojo::TransferableResource::New(); resource->id = next_resource_id_++; resource_to_texture_map_[resource->id] = texture.release(); - resource->format = mojo::RESOURCE_FORMAT_RGBA_8888; + resource->format = mojo::ResourceFormat::RGBA_8888; resource->filter = GL_LINEAR; resource->size = size.Clone(); resource->is_repeated = false; diff --git a/services/sky/converters/input_event_types.cc b/services/sky/converters/input_event_types.cc index 88b85cd4a..29685ce99 100644 --- a/services/sky/converters/input_event_types.cc +++ b/services/sky/converters/input_event_types.cc @@ -12,21 +12,22 @@ namespace sky { namespace { -int EventFlagsToWebInputEventModifiers(int flags) { +int EventFlagsToWebInputEventModifiers(mojo::EventFlags event_flags) { + int flags = static_cast(event_flags); return - (flags & mojo::EVENT_FLAGS_SHIFT_DOWN ? + (flags & static_cast(mojo::EventFlags::SHIFT_DOWN) ? blink::WebInputEvent::ShiftKey : 0) | - (flags & mojo::EVENT_FLAGS_CONTROL_DOWN ? + (flags & static_cast(mojo::EventFlags::CONTROL_DOWN) ? blink::WebInputEvent::ControlKey : 0) | - (flags & mojo::EVENT_FLAGS_CAPS_LOCK_DOWN ? + (flags & static_cast(mojo::EventFlags::CAPS_LOCK_DOWN) ? blink::WebInputEvent::CapsLockOn : 0) | - (flags & mojo::EVENT_FLAGS_ALT_DOWN ? + (flags & static_cast(mojo::EventFlags::ALT_DOWN) ? blink::WebInputEvent::AltKey : 0) | - (flags & mojo::EVENT_FLAGS_LEFT_MOUSE_BUTTON ? + (flags & static_cast(mojo::EventFlags::LEFT_MOUSE_BUTTON) ? blink::WebInputEvent::LeftButtonDown : 0) | - (flags & mojo::EVENT_FLAGS_MIDDLE_MOUSE_BUTTON ? + (flags & static_cast(mojo::EventFlags::MIDDLE_MOUSE_BUTTON) ? blink::WebInputEvent::MiddleButtonDown : 0) | - (flags & mojo::EVENT_FLAGS_RIGHT_MOUSE_BUTTON ? + (flags & static_cast(mojo::EventFlags::RIGHT_MOUSE_BUTTON) ? blink::WebInputEvent::RightButtonDown : 0); } @@ -39,16 +40,16 @@ scoped_ptr BuildWebPointerEvent( base::TimeDelta::FromInternalValue(event->time_stamp).InMillisecondsF(); switch (event->action) { - case mojo::EVENT_TYPE_POINTER_DOWN: + case mojo::EventType::POINTER_DOWN: web_event->type = blink::WebInputEvent::PointerDown; break; - case mojo::EVENT_TYPE_POINTER_MOVE: + case mojo::EventType::POINTER_MOVE: web_event->type = blink::WebInputEvent::PointerMove; break; - case mojo::EVENT_TYPE_POINTER_UP: + case mojo::EventType::POINTER_UP: web_event->type = blink::WebInputEvent::PointerUp; break; - case mojo::EVENT_TYPE_POINTER_CANCEL: + case mojo::EventType::POINTER_CANCEL: // FIXME: What mouse event should we listen to in order to learn when the // mouse moves out of the mojo::View? web_event->type = blink::WebInputEvent::PointerCancel; @@ -58,7 +59,7 @@ scoped_ptr BuildWebPointerEvent( break; } - if (event->pointer_data->kind == mojo::POINTER_KIND_TOUCH) { + if (event->pointer_data->kind == mojo::PointerKind::TOUCH) { web_event->kind = blink::WebPointerEvent::Touch; web_event->pointer = event->pointer_data->pointer_id; } else { @@ -92,18 +93,18 @@ scoped_ptr BuildWebKeyboardEvent( base::TimeDelta::FromInternalValue(event->time_stamp).InMillisecondsF(); switch (event->action) { - case mojo::EVENT_TYPE_KEY_PRESSED: + case mojo::EventType::KEY_PRESSED: web_event->type = event->key_data->is_char ? blink::WebInputEvent::Char : blink::WebInputEvent::KeyDown; break; - case mojo::EVENT_TYPE_KEY_RELEASED: + case mojo::EventType::KEY_RELEASED: web_event->type = blink::WebInputEvent::KeyUp; break; default: NOTREACHED(); } - web_event->key = event->key_data->windows_key_code; + web_event->key = static_cast(event->key_data->windows_key_code); web_event->charCode = event->key_data->text; web_event->unmodifiedCharCode = event->key_data->unmodified_text; @@ -136,17 +137,17 @@ scoped_ptr BuildWebWheelEvent( scoped_ptr ConvertEvent(const mojo::EventPtr& event, float device_pixel_ratio) { - if (event->action == mojo::EVENT_TYPE_POINTER_DOWN || - event->action == mojo::EVENT_TYPE_POINTER_UP || - event->action == mojo::EVENT_TYPE_POINTER_CANCEL || - event->action == mojo::EVENT_TYPE_POINTER_MOVE) { + if (event->action == mojo::EventType::POINTER_DOWN || + event->action == mojo::EventType::POINTER_UP || + event->action == mojo::EventType::POINTER_CANCEL || + event->action == mojo::EventType::POINTER_MOVE) { if (event->pointer_data->horizontal_wheel != 0 || event->pointer_data->vertical_wheel != 0) { return BuildWebWheelEvent(event, device_pixel_ratio); } return BuildWebPointerEvent(event, device_pixel_ratio); - } else if ((event->action == mojo::EVENT_TYPE_KEY_PRESSED || - event->action == mojo::EVENT_TYPE_KEY_RELEASED) && + } else if ((event->action == mojo::EventType::KEY_PRESSED || + event->action == mojo::EventType::KEY_RELEASED) && event->key_data) { return BuildWebKeyboardEvent(event, device_pixel_ratio); } diff --git a/services/sky/document_view.cc b/services/sky/document_view.cc index 2fe36b9d4..44e835ab4 100644 --- a/services/sky/document_view.cc +++ b/services/sky/document_view.cc @@ -214,7 +214,7 @@ void DocumentView::OnSurfaceIdAvailable(mojo::SurfaceIdPtr surface_id) { *viewport_metrics_->size)); mojo::QuadPtr quad = mojo::Quad::New(); - quad->material = mojo::MATERIAL_SURFACE_CONTENT; + quad->material = mojo::Material::SURFACE_CONTENT; quad->rect = bounds.Clone(); quad->opaque_rect = bounds.Clone(); quad->visible_rect = bounds.Clone(); diff --git a/sky/engine/bindings/mojo_natives.cc b/sky/engine/bindings/mojo_natives.cc index 5e81f88d6..ca25406b3 100644 --- a/sky/engine/bindings/mojo_natives.cc +++ b/sky/engine/bindings/mojo_natives.cc @@ -44,6 +44,8 @@ namespace blink { V(MojoHandle_Wait, 3) \ V(MojoHandle_Register, 2) \ V(MojoHandle_WaitMany, 3) \ + V(MojoHandleWatcher_GrowStateArrays, 1) \ + V(MojoHandleWatcher_WaitMany, 2) \ V(MojoHandleWatcher_SendControlData, 4) \ V(MojoHandleWatcher_RecvControlData, 1) \ V(MojoHandleWatcher_SetControlHandle, 1) \ @@ -702,6 +704,115 @@ void MojoMessagePipe_Read(Dart_NativeArguments arguments) { Dart_SetReturnValue(arguments, list); } +struct MojoWaitManyState { + MojoWaitManyState() {} + + std::vector handles; + std::vector signals; + std::vector out_index; + std::vector out_signals; + + static MojoWaitManyState* GetInstance(); + + private: + DISALLOW_COPY_AND_ASSIGN(MojoWaitManyState); +}; + +// This global is safe because it is only accessed by the single handle watcher +// isolate. If multiple handle watcher isolates are ever needed, it will need +// to be replicated. +MojoWaitManyState* MojoWaitManyState::GetInstance() { + static MojoWaitManyState* state = new MojoWaitManyState; + return state; +} + +void MojoHandleWatcher_GrowStateArrays(Dart_NativeArguments arguments) { + int64_t new_length; + CHECK_INTEGER_ARGUMENT(arguments, 0, &new_length, InvalidArgument); + + MojoWaitManyState& handle_watcher_wait_state = + *MojoWaitManyState::GetInstance(); + + handle_watcher_wait_state.handles.resize(new_length); + handle_watcher_wait_state.signals.resize(new_length); + handle_watcher_wait_state.out_index.resize(1); + handle_watcher_wait_state.out_signals.resize(new_length); + + Dart_Handle dart_handles = Dart_NewExternalTypedData( + Dart_TypedData_kUint32, handle_watcher_wait_state.handles.data(), + handle_watcher_wait_state.handles.size()); + if (Dart_IsError(dart_handles)) { + Dart_PropagateError(dart_handles); + } + if (Dart_IsNull(dart_handles)) { + SetNullReturn(arguments); + return; + } + + Dart_Handle dart_signals = Dart_NewExternalTypedData( + Dart_TypedData_kUint32, handle_watcher_wait_state.signals.data(), + handle_watcher_wait_state.signals.size()); + if (Dart_IsError(dart_signals)) { + Dart_PropagateError(dart_signals); + } + if (Dart_IsNull(dart_signals)) { + SetNullReturn(arguments); + return; + } + + Dart_Handle dart_out_index = Dart_NewExternalTypedData( + Dart_TypedData_kUint32, handle_watcher_wait_state.out_index.data(), + handle_watcher_wait_state.out_index.size()); + if (Dart_IsError(dart_out_index)) { + Dart_PropagateError(dart_out_index); + } + if (Dart_IsNull(dart_out_index)) { + SetNullReturn(arguments); + return; + } + + Dart_Handle dart_out_signals = Dart_NewExternalTypedData( + Dart_TypedData_kUint64, handle_watcher_wait_state.out_signals.data(), + handle_watcher_wait_state.out_signals.size()); + if (Dart_IsError(dart_out_signals)) { + Dart_PropagateError(dart_out_signals); + } + if (Dart_IsNull(dart_out_signals)) { + SetNullReturn(arguments); + return; + } + + Dart_Handle list = Dart_NewList(4); + Dart_ListSetAt(list, 0, dart_handles); + Dart_ListSetAt(list, 1, dart_signals); + Dart_ListSetAt(list, 2, dart_out_index); + Dart_ListSetAt(list, 3, dart_out_signals); + Dart_SetReturnValue(arguments, list); +} + +void MojoHandleWatcher_WaitMany(Dart_NativeArguments arguments) { + int64_t handles_len = 0; + int64_t deadline = 0; + CHECK_INTEGER_ARGUMENT(arguments, 0, &handles_len, InvalidArgument); + CHECK_INTEGER_ARGUMENT(arguments, 1, &deadline, InvalidArgument); + + MojoWaitManyState& handle_watcher_wait_state = + *MojoWaitManyState::GetInstance(); + + uint32_t* handles = handle_watcher_wait_state.handles.data(); + uint32_t* signals = handle_watcher_wait_state.signals.data(); + uint32_t* out_index = handle_watcher_wait_state.out_index.data(); + MojoHandleSignalsState* out_signals = + handle_watcher_wait_state.out_signals.data(); + + Dart_IsolateBlocked(); + MojoResult mojo_result = MojoWaitMany(handles, signals, handles_len, deadline, + out_index, out_signals); + Dart_IsolateUnblocked(); + + Dart_SetIntegerReturnValue(arguments, static_cast(mojo_result)); +} + struct ControlData { int64_t handle; Dart_Port port; diff --git a/sky/shell/ui/input_event_converter.cc b/sky/shell/ui/input_event_converter.cc index b8ed47597..ef90e6250 100644 --- a/sky/shell/ui/input_event_converter.cc +++ b/sky/shell/ui/input_event_converter.cc @@ -18,16 +18,16 @@ scoped_ptr BuildWebPointerEvent( web_event->timeStampMS = event->time_stamp; switch (event->type) { - case EVENT_TYPE_POINTER_DOWN: + case EventType::POINTER_DOWN: web_event->type = blink::WebInputEvent::PointerDown; break; - case EVENT_TYPE_POINTER_UP: + case EventType::POINTER_UP: web_event->type = blink::WebInputEvent::PointerUp; break; - case EVENT_TYPE_POINTER_MOVE: + case EventType::POINTER_MOVE: web_event->type = blink::WebInputEvent::PointerMove; break; - case EVENT_TYPE_POINTER_CANCEL: + case EventType::POINTER_CANCEL: web_event->type = blink::WebInputEvent::PointerCancel; break; default: @@ -36,7 +36,7 @@ scoped_ptr BuildWebPointerEvent( } if (event->pointer_data) { - if (event->pointer_data->kind == POINTER_KIND_TOUCH) + if (event->pointer_data->kind == PointerKind::TOUCH) web_event->kind = blink::WebPointerEvent::Touch; web_event->pointer = event->pointer_data->pointer; web_event->x = event->pointer_data->x / device_pixel_ratio; @@ -60,15 +60,15 @@ scoped_ptr BuildWebBackEvent(const InputEventPtr& event) { scoped_ptr ConvertEvent(const InputEventPtr& event, float device_pixel_ratio) { switch (event->type) { - case EVENT_TYPE_POINTER_DOWN: - case EVENT_TYPE_POINTER_UP: - case EVENT_TYPE_POINTER_MOVE: - case EVENT_TYPE_POINTER_CANCEL: + case EventType::POINTER_DOWN: + case EventType::POINTER_UP: + case EventType::POINTER_MOVE: + case EventType::POINTER_CANCEL: return BuildWebPointerEvent(event, device_pixel_ratio); - case EVENT_TYPE_BACK: + case EventType::BACK: return BuildWebBackEvent(event); - case EVENT_TYPE_UNKNOWN: - NOTIMPLEMENTED() << "ConvertEvent received unexpected EVENT_TYPE_UNKNOWN"; + case EventType::UNKNOWN: + NOTIMPLEMENTED() << "ConvertEvent received unexpected EventType::UNKNOWN"; } return scoped_ptr();