summaryrefslogtreecommitdiffstats
path: root/ipc/glue/MessageLink.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/glue/MessageLink.cpp')
-rw-r--r--ipc/glue/MessageLink.cpp195
1 files changed, 195 insertions, 0 deletions
diff --git a/ipc/glue/MessageLink.cpp b/ipc/glue/MessageLink.cpp
new file mode 100644
index 0000000000..872ff48b27
--- /dev/null
+++ b/ipc/glue/MessageLink.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=4 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ipc/MessageLink.h"
+#include "mojo/core/ports/event.h"
+#include "mojo/core/ports/node.h"
+#include "mozilla/ipc/MessageChannel.h"
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/NodeController.h"
+#include "chrome/common/ipc_channel.h"
+#include "base/task.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "nsDebug.h"
+#include "nsExceptionHandler.h"
+#include "nsISupportsImpl.h"
+#include "nsPrintfCString.h"
+#include "nsXULAppAPI.h"
+
+using namespace mozilla;
+
+namespace mozilla {
+namespace ipc {
+
+MessageLink::MessageLink(MessageChannel* aChan) : mChan(aChan) {}
+
+MessageLink::~MessageLink() {
+#ifdef DEBUG
+ mChan = nullptr;
+#endif
+}
+
+class PortLink::PortObserverThunk : public NodeController::PortObserver {
+ public:
+ PortObserverThunk(RefCountedMonitor* aMonitor, PortLink* aLink)
+ : mMonitor(aMonitor), mLink(aLink) {}
+
+ void OnPortStatusChanged() override {
+ MonitorAutoLock lock(*mMonitor);
+ if (mLink) {
+ mLink->OnPortStatusChanged();
+ }
+ }
+
+ private:
+ friend class PortLink;
+
+ // The monitor from our PortLink's MessageChannel. Guards access to `mLink`.
+ RefPtr<RefCountedMonitor> mMonitor;
+
+ // Cleared by `PortLink` in `PortLink::Clear()`.
+ PortLink* MOZ_NON_OWNING_REF mLink;
+};
+
+PortLink::PortLink(MessageChannel* aChan, ScopedPort aPort)
+ : MessageLink(aChan), mNode(aPort.Controller()), mPort(aPort.Release()) {
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ mObserver = new PortObserverThunk(mChan->mMonitor, this);
+ mNode->SetPortObserver(mPort, mObserver);
+
+ // Dispatch an event to the IO loop to trigger an initial
+ // `OnPortStatusChanged` to deliver any pending messages. This needs to be run
+ // asynchronously from a different thread (or in the case of a same-thread
+ // channel, from the current thread), for now due to assertions in
+ // `MessageChannel`.
+ nsCOMPtr<nsIRunnable> openRunnable = NewRunnableMethod(
+ "PortLink::Open", mObserver, &PortObserverThunk::OnPortStatusChanged);
+ if (aChan->mIsSameThreadChannel) {
+ aChan->mWorkerThread->Dispatch(openRunnable.forget());
+ } else {
+ XRE_GetIOMessageLoop()->PostTask(openRunnable.forget());
+ }
+}
+
+PortLink::~PortLink() {
+ MOZ_RELEASE_ASSERT(!mObserver, "PortLink destroyed without being closed!");
+}
+
+void PortLink::SendMessage(UniquePtr<Message> aMessage) {
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ if (aMessage->size() > IPC::Channel::kMaximumMessageSize) {
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::IPCMessageName,
+ nsDependentCString(aMessage->name()));
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::IPCMessageSize,
+ static_cast<unsigned int>(aMessage->size()));
+ MOZ_CRASH("IPC message size is too large");
+ }
+ aMessage->AssertAsLargeAsHeader();
+
+ RefPtr<PortObserverThunk> observer = mObserver;
+ if (!observer) {
+ NS_WARNING("Ignoring message to closed PortLink");
+ return;
+ }
+
+ // Make local copies of relevant member variables, so we can unlock the
+ // monitor for the rest of this function. This protects us in case `this` is
+ // deleted during the call (although that shouldn't happen in practice).
+ //
+ // We don't want the monitor to be held when calling into ports, as we may be
+ // re-entrantly called by our `PortObserverThunk` which will attempt to
+ // acquire the monitor.
+ RefPtr<RefCountedMonitor> monitor = mChan->mMonitor;
+ RefPtr<NodeController> node = mNode;
+ PortRef port = mPort;
+
+ bool ok = false;
+ monitor->AssertCurrentThreadOwns();
+ {
+ MonitorAutoUnlock guard(*monitor);
+ ok = node->SendUserMessage(port, std::move(aMessage));
+ }
+ if (!ok) {
+ // The send failed, but double-check that we weren't closed racily while
+ // sending, which could lead to an invalid state error.
+ if (observer->mLink) {
+ MOZ_CRASH("Invalid argument to SendUserMessage");
+ }
+ NS_WARNING("Message dropped as PortLink was closed");
+ }
+}
+
+void PortLink::Close() {
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ if (!mObserver) {
+ // We're already being closed.
+ return;
+ }
+
+ Clear();
+}
+
+void PortLink::Clear() {
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ // NOTE: We're calling into `ports` with our monitor held! Usually, this could
+ // lead to deadlocks due to the PortObserverThunk acquiring the lock
+ // re-entrantly, but is OK here as we're immediately clearing the port's
+ // observer. We shouldn't have issues with any re-entrant calls on this thread
+ // acquiring this MessageChannel's monitor.
+ //
+ // We also clear out the reference in `mObserver` back to this type so that
+ // notifications from other threads won't try to call us again once we release
+ // the monitor.
+ mNode->SetPortObserver(mPort, nullptr);
+ mObserver->mLink = nullptr;
+ mObserver = nullptr;
+ mNode->ClosePort(mPort);
+}
+
+void PortLink::OnPortStatusChanged() {
+ mChan->mMonitor->AssertCurrentThreadOwns();
+
+ // Check if the port's remoteness status has updated, and tell our channel if
+ // it has.
+ if (Maybe<PortStatus> status = mNode->GetStatus(mPort);
+ status && status->peer_remote != mChan->IsCrossProcess()) {
+ mChan->SetIsCrossProcess(status->peer_remote);
+ }
+
+ while (mObserver) {
+ UniquePtr<IPC::Message> message;
+ if (!mNode->GetMessage(mPort, &message)) {
+ Clear();
+ mChan->OnChannelErrorFromLink();
+ return;
+ }
+ if (!message) {
+ return;
+ }
+
+ mChan->OnMessageReceivedFromLink(std::move(message));
+ }
+}
+
+bool PortLink::IsClosed() const {
+ if (Maybe<PortStatus> status = mNode->GetStatus(mPort)) {
+ return !(status->has_messages || status->receiving_messages);
+ }
+ return true;
+}
+
+} // namespace ipc
+} // namespace mozilla