summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-10 20:52:11 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-10 20:52:11 +0000
commit6cf6ca58e053f69d88c90b55f3a9011a6caba801 (patch)
treea10d40846807d905cd014a933135f7856d430067
parent6a91ffb0e9bafcde73e134aef4b635e2efeb71db (diff)
downloadchromium_src-6cf6ca58e053f69d88c90b55f3a9011a6caba801.zip
chromium_src-6cf6ca58e053f69d88c90b55f3a9011a6caba801.tar.gz
chromium_src-6cf6ca58e053f69d88c90b55f3a9011a6caba801.tar.bz2
Simple shell that loads a dll and calls an entrypoint function passing in a handle to a pipe created by the shell app.
To achieve this I had to make mojo_system a <(component) so sample_app.dll could link against it. Trung, Darin tells me you had a different idea about how to achieve this. Consider this CL a starting point for the discussion :-) R=darin@chromium.org BUG= Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=227604 Review URL: https://codereview.chromium.org/25895002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@227983 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--build/all.gyp2
-rw-r--r--mojo/mojo.gyp31
-rw-r--r--mojo/public/system/core.h44
-rw-r--r--mojo/public/system/system_export.h29
-rw-r--r--mojo/shell/app_container.cc92
-rw-r--r--mojo/shell/app_container.h43
-rw-r--r--mojo/shell/sample_app.cc73
-rw-r--r--mojo/shell/shell.cc33
-rw-r--r--mojo/shell/switches.cc11
-rw-r--r--mojo/shell/switches.h14
-rw-r--r--mojo/system/core_impl.h2
-rw-r--r--mojo/system/dispatcher.h4
-rw-r--r--mojo/system/memory.cc6
-rw-r--r--mojo/system/memory.h5
-rw-r--r--mojo/system/message_pipe.h4
-rw-r--r--mojo/system/message_pipe_dispatcher.h3
-rw-r--r--mojo/system/simple_dispatcher.h3
-rw-r--r--mojo/system/waiter.h3
-rw-r--r--mojo/system/waiter_list.h3
19 files changed, 374 insertions, 31 deletions
diff --git a/build/all.gyp b/build/all.gyp
index ea10e64..d6aa909 100644
--- a/build/all.gyp
+++ b/build/all.gyp
@@ -14,7 +14,6 @@
'../chrome/chrome.gyp:*',
'../content/content.gyp:*',
'../crypto/crypto.gyp:*',
- '../mojo/mojo.gyp:*',
'../net/net.gyp:*',
'../sdch/sdch.gyp:*',
'../sql/sql.gyp:*',
@@ -43,6 +42,7 @@
'../jingle/jingle.gyp:*',
'../media/cast/cast.gyp:*',
'../media/media.gyp:*',
+ '../mojo/mojo.gyp:*',
'../ppapi/ppapi.gyp:*',
'../ppapi/ppapi_internal.gyp:*',
'../printing/printing.gyp:*',
diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp
index d8f3121..5a86936 100644
--- a/mojo/mojo.gyp
+++ b/mojo/mojo.gyp
@@ -64,10 +64,13 @@
{
'target_name': 'mojo_system',
# TODO(vtl): This should probably be '<(component)'; make it work.
- 'type': 'static_library',
+ 'type': '<(component)',
'dependencies': [
'../base/base.gyp:base',
],
+ 'defines': [
+ 'MOJO_SYSTEM_IMPLEMENTATION',
+ ],
'sources': [
'public/system/core.h',
'system/core.cc',
@@ -120,5 +123,31 @@
'system/waiter_unittest.cc',
],
},
+ {
+ 'target_name': 'mojo_shell',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'mojo_system',
+ ],
+ 'sources': [
+ 'shell/app_container.cc',
+ 'shell/app_container.h',
+ 'shell/shell.cc',
+ 'shell/switches.cc',
+ 'shell/switches.h',
+ ],
+ },
+ {
+ 'target_name': 'sample_app',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'mojo_system',
+ ],
+ 'sources': [
+ 'shell/sample_app.cc',
+ ],
+ },
],
}
diff --git a/mojo/public/system/core.h b/mojo/public/system/core.h
index cb405ae..9a53a78 100644
--- a/mojo/public/system/core.h
+++ b/mojo/public/system/core.h
@@ -9,6 +9,8 @@
#include <stdint.h>
+#include "mojo/public/system/system_export.h"
+
// Types -----------------------------------------------------------------------
// TODO(vtl): Notes: Use of undefined flags will lead to undefined behavior
@@ -190,7 +192,7 @@ extern "C" {
// happen before the close, be cancelled with result |MOJO_RESULT_CANCELLED| if
// they properly overlap (this is likely the case with |MojoWait()|, etc.), or
// fail with |MOJO_RESULT_INVALID_ARGUMENT| if they happen after.
-MojoResult MojoClose(MojoHandle handle);
+MOJO_SYSTEM_EXPORT MojoResult MojoClose(MojoHandle handle);
// Waits on the given handle until the state indicated by |flags| is satisfied
// or until |deadline| has passed.
@@ -208,9 +210,9 @@ MojoResult MojoClose(MojoHandle handle);
// If there are multiple waiters (on different threads, obviously) waiting on
// the same handle and flag and that flag becomes set, all waiters will be
// awoken.
-MojoResult MojoWait(MojoHandle handle,
- MojoWaitFlags flags,
- MojoDeadline deadline);
+MOJO_SYSTEM_EXPORT MojoResult MojoWait(MojoHandle handle,
+ MojoWaitFlags flags,
+ MojoDeadline deadline);
// Waits on |handles[0]|, ..., |handles[num_handles-1]| for at least one of them
// to satisfy the state indicated by |flags[0]|, ..., |flags[num_handles-1]|,
@@ -225,23 +227,27 @@ MojoResult MojoWait(MojoHandle handle,
// handles satisfying any of its flags.
// |MOJO_RESULT_FAILED_PRECONDITION| if it is or becomes impossible that SOME
// |handle[i]| will ever satisfy any of its flags |flags[i]|.
-MojoResult MojoWaitMany(const MojoHandle* handles,
- const MojoWaitFlags* flags,
- uint32_t num_handles,
- MojoDeadline deadline);
+MOJO_SYSTEM_EXPORT MojoResult MojoWaitMany(const MojoHandle* handles,
+ const MojoWaitFlags* flags,
+ uint32_t num_handles,
+ MojoDeadline deadline);
// TODO(vtl): flags? other params (e.g., queue sizes, max message sizes?)
-MojoResult MojoCreateMessagePipe(MojoHandle* handle_0, MojoHandle* handle_1);
-
-MojoResult MojoWriteMessage(MojoHandle handle,
- const void* bytes, uint32_t num_bytes,
- const MojoHandle* handles, uint32_t num_handles,
- MojoWriteMessageFlags flags);
-
-MojoResult MojoReadMessage(MojoHandle handle,
- void* bytes, uint32_t* num_bytes,
- MojoHandle* handles, uint32_t* num_handles,
- MojoReadMessageFlags flags);
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateMessagePipe(MojoHandle* handle_0,
+ MojoHandle* handle_1);
+
+MOJO_SYSTEM_EXPORT MojoResult MojoWriteMessage(
+ MojoHandle handle,
+ const void* bytes, uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags);
+
+MOJO_SYSTEM_EXPORT MojoResult MojoReadMessage(MojoHandle handle,
+ void* bytes, uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags);
#ifdef __cplusplus
} // extern "C"
diff --git a/mojo/public/system/system_export.h b/mojo/public/system/system_export.h
new file mode 100644
index 0000000..8a1b1e7
--- /dev/null
+++ b/mojo/public/system/system_export.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_SYSTEM_SYSTEM_EXPORT_H_
+#define MOJO_PUBLIC_SYSTEM_SYSTEM_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_SYSTEM_IMPLEMENTATION)
+#define MOJO_SYSTEM_EXPORT __declspec(dllexport)
+#else
+#define MOJO_SYSTEM_EXPORT __declspec(dllimport)
+#endif // defined(GFX_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(MOJO_SYSTEM_IMPLEMENTATION)
+#define MOJO_SYSTEM_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_SYSTEM_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define MOJO_SYSTEM_EXPORT
+#endif
+
+#endif // MOJO_PUBLIC_SYSTEM_SYSTEM_EXPORT_H_
diff --git a/mojo/shell/app_container.cc b/mojo/shell/app_container.cc
new file mode 100644
index 0000000..b679973
--- /dev/null
+++ b/mojo/shell/app_container.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/native_library.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/thread.h"
+#include "mojo/public/system/core.h"
+#include "mojo/shell/app_container.h"
+
+typedef MojoResult (*MojoMainFunction)(mojo::Handle pipe);
+
+namespace mojo {
+namespace shell {
+
+void LaunchAppOnThread(
+ const base::FilePath& app_path,
+ Handle app_handle) {
+ MojoResult result = MOJO_RESULT_OK;
+ MojoMainFunction main_function = NULL;
+
+ base::NativeLibrary app_library = base::LoadNativeLibrary(app_path, NULL);
+ if (!app_library) {
+ LOG(ERROR) << "Failed to load library: " << app_path.value().c_str();
+ goto completed;
+ }
+
+ main_function = reinterpret_cast<MojoMainFunction>(
+ base::GetFunctionPointerFromNativeLibrary(app_library, "MojoMain"));
+ if (!main_function) {
+ LOG(ERROR) << "Entrypoint MojoMain not found.";
+ goto completed;
+ }
+
+ result = main_function(app_handle);
+ if (result < MOJO_RESULT_OK) {
+ LOG(ERROR) << "MojoMain returned an error: " << result;
+ // TODO(*): error handling?
+ goto completed;
+ }
+
+completed:
+ base::UnloadNativeLibrary(app_library);
+ Close(app_handle);
+}
+
+AppContainer::AppContainer()
+ : weak_factory_(this) {
+}
+
+AppContainer::~AppContainer() {
+}
+
+void AppContainer::LaunchApp(const base::FilePath& app_path) {
+ Handle app_handle;
+ MojoResult result = CreateMessagePipe(&shell_handle_, &app_handle);
+ if (result < MOJO_RESULT_OK) {
+ // Failure..
+ }
+
+ // Launch the app on its own thread.
+ // TODO(beng): Create a unique thread name.
+ thread_.reset(new base::Thread("app_thread"));
+ thread_->Start();
+ thread_->message_loop_proxy()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&LaunchAppOnThread, app_path, app_handle),
+ base::Bind(&AppContainer::AppCompleted, weak_factory_.GetWeakPtr()));
+
+ const char* hello_msg = "Hello";
+ result = WriteMessage(shell_handle_, hello_msg, strlen(hello_msg)+1,
+ NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+ if (result < MOJO_RESULT_OK) {
+ // Failure..
+ }
+}
+
+
+void AppContainer::AppCompleted() {
+ thread_.reset();
+ Close(shell_handle_);
+
+ // Probably want to do something more sophisticated here, like notify someone
+ // else to do this.
+ base::MessageLoop::current()->Quit();
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/mojo/shell/app_container.h b/mojo/shell/app_container.h
new file mode 100644
index 0000000..b02a361
--- /dev/null
+++ b/mojo/shell/app_container.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SHELL_APP_CONTAINER_H_
+#define MOJO_SHELL_APP_CONTAINER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/system/core.h"
+
+namespace base {
+class Thread;
+}
+
+namespace mojo {
+namespace shell {
+
+// A container class that runs an app on its own thread.
+class AppContainer {
+ public:
+ AppContainer();
+ ~AppContainer();
+
+ void LaunchApp(const base::FilePath& app_path);
+
+ private:
+ void AppCompleted();
+
+ base::WeakPtrFactory<AppContainer> weak_factory_;
+
+ scoped_ptr<base::Thread> thread_;
+
+ // Following members are valid only on app thread.
+ Handle shell_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppContainer);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_APP_CONTAINER_H_ \ No newline at end of file
diff --git a/mojo/shell/sample_app.cc b/mojo/shell/sample_app.cc
new file mode 100644
index 0000000..a3acbe3
--- /dev/null
+++ b/mojo/shell/sample_app.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+
+#include "base/basictypes.h"
+#include "mojo/public/system/core.h"
+#include "mojo/system/core_impl.h"
+
+#if defined(OS_WIN)
+#if !defined(CDECL)
+#define CDECL __cdecl
+#endif
+#define SAMPLE_APP_EXPORT __declspec(dllexport)
+#else
+#define CDECL
+#define SAMPLE_APP_EXPORT __attribute__((visibility("default")))
+#endif
+
+char* ReadStringFromPipe(mojo::Handle pipe) {
+ uint32_t len = 0;
+ char* buf = NULL;
+ MojoResult result = mojo::ReadMessage(pipe, buf, &len, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ if (result == MOJO_RESULT_RESOURCE_EXHAUSTED) {
+ buf = new char[len];
+ result = mojo::ReadMessage(pipe, buf, &len, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ }
+ if (result < MOJO_RESULT_OK) {
+ // Failure..
+ if (buf)
+ delete[] buf;
+ return NULL;
+ }
+ return buf;
+}
+
+class SampleMessageWaiter {
+ public:
+ explicit SampleMessageWaiter(mojo::Handle pipe) : pipe_(pipe) {}
+ ~SampleMessageWaiter() {}
+
+ void Read() {
+ char* string = ReadStringFromPipe(pipe_);
+ if (string) {
+ printf("Read string from pipe: %s\n", string);
+ delete[] string;
+ string = NULL;
+ }
+ }
+
+ void WaitAndRead() {
+ MojoResult result = mojo::Wait(pipe_, MOJO_WAIT_FLAG_READABLE, 100);
+ if (result < MOJO_RESULT_OK) {
+ // Failure...
+ }
+
+ Read();
+ }
+
+ private:
+
+ mojo::Handle pipe_;
+ DISALLOW_COPY_AND_ASSIGN(SampleMessageWaiter);
+};
+
+extern "C" SAMPLE_APP_EXPORT MojoResult CDECL MojoMain(
+ mojo::Handle pipe) {
+ SampleMessageWaiter(pipe).WaitAndRead();
+ return MOJO_RESULT_OK;
+}
diff --git a/mojo/shell/shell.cc b/mojo/shell/shell.cc
new file mode 100644
index 0000000..72f697f
--- /dev/null
+++ b/mojo/shell/shell.cc
@@ -0,0 +1,33 @@
+// 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 "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/shell/app_container.h"
+#include "mojo/shell/switches.h"
+#include "mojo/system/core_impl.h"
+
+int main(int argc, char** argv) {
+ base::AtExitManager at_exit;
+ CommandLine::Init(argc, argv);
+
+ mojo::system::CoreImpl::Init();
+
+ base::MessageLoop message_loop(base::MessageLoop::TYPE_UI);
+
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ if (!command_line.HasSwitch(switches::kApp)) {
+ LOG(ERROR) << "No app path specified.";
+ return 0;
+ }
+
+ scoped_ptr<mojo::shell::AppContainer> container(
+ new mojo::shell::AppContainer);
+ container->LaunchApp(command_line.GetSwitchValuePath(switches::kApp));
+ message_loop.Run();
+ return 0;
+}
diff --git a/mojo/shell/switches.cc b/mojo/shell/switches.cc
new file mode 100644
index 0000000..0d3883d
--- /dev/null
+++ b/mojo/shell/switches.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/shell/switches.h"
+
+namespace switches {
+
+const char kApp[] = "app";
+
+} // namespace switches
diff --git a/mojo/shell/switches.h b/mojo/shell/switches.h
new file mode 100644
index 0000000..3ccac80
--- /dev/null
+++ b/mojo/shell/switches.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SHELL_SWITCHES_H_
+#define MOJO_SHELL_SWITCHES_H_
+
+namespace switches {
+
+extern const char kApp[];
+
+} // namespace switches
+
+#endif // MOJO_SHELL_SWITCHES_H_ \ No newline at end of file
diff --git a/mojo/system/core_impl.h b/mojo/system/core_impl.h
index 1417a06..aede29d 100644
--- a/mojo/system/core_impl.h
+++ b/mojo/system/core_impl.h
@@ -25,7 +25,7 @@ class CoreTestBase;
// the (obvious) exception of |Init()|, which must be called first (and the call
// completed) before making any other calls, all the public methods are
// thread-safe.
-class CoreImpl {
+class MOJO_SYSTEM_EXPORT CoreImpl {
public:
static void Init();
diff --git a/mojo/system/dispatcher.h b/mojo/system/dispatcher.h
index 6b44f6a..75f6a97 100644
--- a/mojo/system/dispatcher.h
+++ b/mojo/system/dispatcher.h
@@ -9,6 +9,7 @@
#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "mojo/public/system/core.h"
+#include "mojo/public/system/system_export.h"
namespace mojo {
namespace system {
@@ -20,7 +21,8 @@ class Waiter;
// object is thread-safe, with its state being protected by a single lock
// |lock_|, which is also made available to implementation subclasses (via the
// |lock()| method).
-class Dispatcher : public base::RefCountedThreadSafe<Dispatcher> {
+class MOJO_SYSTEM_EXPORT Dispatcher :
+ public base::RefCountedThreadSafe<Dispatcher> {
public:
// These methods implement the various primitives named |Mojo...()|. These
// take |lock_| and handle races with |Close()|. Then they call out to
diff --git a/mojo/system/memory.cc b/mojo/system/memory.cc
index fe7edef..f20b61d 100644
--- a/mojo/system/memory.cc
+++ b/mojo/system/memory.cc
@@ -23,8 +23,10 @@ bool VerifyUserPointerForSize(const void* pointer, size_t count) {
}
// Explicitly instantiate the sizes we need. Add instantiations as needed.
-template bool VerifyUserPointerForSize<1>(const void*, size_t);
-template bool VerifyUserPointerForSize<4>(const void*, size_t);
+template MOJO_SYSTEM_EXPORT bool VerifyUserPointerForSize<1>(
+ const void*, size_t);
+template MOJO_SYSTEM_EXPORT bool VerifyUserPointerForSize<4>(
+ const void*, size_t);
} // namespace system
} // namespace mojo
diff --git a/mojo/system/memory.h b/mojo/system/memory.h
index cca3aca..963974a 100644
--- a/mojo/system/memory.h
+++ b/mojo/system/memory.h
@@ -7,6 +7,8 @@
#include <stddef.h>
+#include "mojo/public/system/system_export.h"
+
namespace mojo {
namespace system {
@@ -14,7 +16,8 @@ namespace system {
// instantiations in the .cc file. This is used by |VerifyUserPointer<T>()|
// below, and you should use that instead.
template <size_t size>
-bool VerifyUserPointerForSize(const void* pointer, size_t count);
+bool MOJO_SYSTEM_EXPORT VerifyUserPointerForSize(const void* pointer,
+ size_t count);
// Verify that |count * sizeof(T)| bytes can be read from the user |pointer|
// insofar as possible/necessary (note: this is done carefully since |count *
diff --git a/mojo/system/message_pipe.h b/mojo/system/message_pipe.h
index d8266f7..2be2acd 100644
--- a/mojo/system/message_pipe.h
+++ b/mojo/system/message_pipe.h
@@ -12,6 +12,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "mojo/public/system/core.h"
+#include "mojo/public/system/system_export.h"
#include "mojo/system/waiter_list.h"
namespace mojo {
@@ -23,7 +24,8 @@ class Waiter;
// |MessagePipe| is the secondary object implementing a message pipe (see the
// explanatory comment in core_impl.cc), and is jointly owned by the two
// dispatchers passed in to the constructor. This class is thread-safe.
-class MessagePipe : public base::RefCountedThreadSafe<MessagePipe> {
+class MOJO_SYSTEM_EXPORT MessagePipe :
+ public base::RefCountedThreadSafe<MessagePipe> {
public:
MessagePipe();
diff --git a/mojo/system/message_pipe_dispatcher.h b/mojo/system/message_pipe_dispatcher.h
index b52ebb8..a0e2f09 100644
--- a/mojo/system/message_pipe_dispatcher.h
+++ b/mojo/system/message_pipe_dispatcher.h
@@ -8,6 +8,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
+#include "mojo/public/system/system_export.h"
#include "mojo/system/dispatcher.h"
namespace mojo {
@@ -17,7 +18,7 @@ class MessagePipe;
// This is the |Dispatcher| implementation for message pipes (created by the
// Mojo primitive |MojoCreateMessagePipe()|). This class is thread-safe.
-class MessagePipeDispatcher : public Dispatcher {
+class MOJO_SYSTEM_EXPORT MessagePipeDispatcher : public Dispatcher {
public:
MessagePipeDispatcher();
diff --git a/mojo/system/simple_dispatcher.h b/mojo/system/simple_dispatcher.h
index f306aba..4ef37d1 100644
--- a/mojo/system/simple_dispatcher.h
+++ b/mojo/system/simple_dispatcher.h
@@ -8,6 +8,7 @@
#include <list>
#include "base/basictypes.h"
+#include "mojo/public/system/system_export.h"
#include "mojo/system/dispatcher.h"
#include "mojo/system/waiter_list.h"
@@ -18,7 +19,7 @@ namespace system {
// correspondence between handles and dispatchers (see the explanatory comment
// in core_impl.cc). This class implements the standard waiter-signalling
// mechanism in that case.
-class SimpleDispatcher : public Dispatcher {
+class MOJO_SYSTEM_EXPORT SimpleDispatcher : public Dispatcher {
protected:
SimpleDispatcher();
diff --git a/mojo/system/waiter.h b/mojo/system/waiter.h
index bdd33fc..a2e8076 100644
--- a/mojo/system/waiter.h
+++ b/mojo/system/waiter.h
@@ -9,6 +9,7 @@
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "mojo/public/system/core.h"
+#include "mojo/public/system/system_export.h"
namespace mojo {
namespace system {
@@ -17,7 +18,7 @@ namespace system {
// under other locks, in particular, |Dispatcher::lock_|s, so |Waiter| methods
// must never call out to other objects (in particular, |Dispatcher|s). This
// class is thread-safe.
-class Waiter {
+class MOJO_SYSTEM_EXPORT Waiter {
public:
Waiter();
~Waiter();
diff --git a/mojo/system/waiter_list.h b/mojo/system/waiter_list.h
index 23933e3..e23c138 100644
--- a/mojo/system/waiter_list.h
+++ b/mojo/system/waiter_list.h
@@ -9,6 +9,7 @@
#include "base/basictypes.h"
#include "mojo/public/system/core.h"
+#include "mojo/public/system/system_export.h"
namespace mojo {
namespace system {
@@ -22,7 +23,7 @@ class Waiter;
// object (see simple_dispatcher.* and the explanatory comment in core_impl.cc).
// This class is thread-unsafe (all concurrent access must be protected by some
// lock).
-class WaiterList {
+class MOJO_SYSTEM_EXPORT WaiterList {
public:
WaiterList();
~WaiterList();