LCOV - code coverage report
Current view: directory - ipc/glue - AsyncChannel.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 347 4 1.2 %
Date: 2012-06-02 Functions: 61 1 1.6 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: sw=4 ts=4 et :
       3                 :  */
       4                 : /* ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is Mozilla Plugin App.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  *   Chris Jones <jones.chris.g@gmail.com>
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "mozilla/ipc/AsyncChannel.h"
      41                 : #include "mozilla/ipc/BrowserProcessSubThread.h"
      42                 : #include "mozilla/ipc/ProtocolUtils.h"
      43                 : 
      44                 : #include "nsDebug.h"
      45                 : #include "nsTraceRefcnt.h"
      46                 : #include "nsXULAppAPI.h"
      47                 : 
      48                 : using mozilla::MonitorAutoLock;
      49                 : 
      50                 : template<>
      51                 : struct RunnableMethodTraits<mozilla::ipc::AsyncChannel>
      52               0 : {
      53               0 :     static void RetainCallee(mozilla::ipc::AsyncChannel* obj) { }
      54               0 :     static void ReleaseCallee(mozilla::ipc::AsyncChannel* obj) { }
      55                 : };
      56                 : 
      57                 : template<>
      58                 : struct RunnableMethodTraits<mozilla::ipc::AsyncChannel::ProcessLink>
      59               0 : {
      60               0 :     static void RetainCallee(mozilla::ipc::AsyncChannel::ProcessLink* obj) { }
      61               0 :     static void ReleaseCallee(mozilla::ipc::AsyncChannel::ProcessLink* obj) { }
      62                 : };
      63                 : 
      64                 : // We rely on invariants about the lifetime of the transport:
      65                 : //
      66                 : //  - outlives this AsyncChannel
      67                 : //  - deleted on the IO thread
      68                 : //
      69                 : // These invariants allow us to send messages directly through the
      70                 : // transport without having to worry about orphaned Send() tasks on
      71                 : // the IO thread touching AsyncChannel memory after it's been deleted
      72                 : // on the worker thread.  We also don't need to refcount the
      73                 : // Transport, because whatever task triggers its deletion only runs on
      74                 : // the IO thread, and only runs after this AsyncChannel is done with
      75                 : // the Transport.
      76                 : template<>
      77                 : struct RunnableMethodTraits<mozilla::ipc::AsyncChannel::Transport>
      78               0 : {
      79               0 :     static void RetainCallee(mozilla::ipc::AsyncChannel::Transport* obj) { }
      80               0 :     static void ReleaseCallee(mozilla::ipc::AsyncChannel::Transport* obj) { }
      81                 : };
      82                 : 
      83                 : namespace {
      84                 : 
      85                 : // This is an async message
      86                 : class GoodbyeMessage : public IPC::Message
      87               0 : {
      88                 : public:
      89                 :     enum { ID = GOODBYE_MESSAGE_TYPE };
      90               0 :     GoodbyeMessage() :
      91               0 :         IPC::Message(MSG_ROUTING_NONE, ID, PRIORITY_NORMAL)
      92                 :     {
      93               0 :     }
      94                 :     // XXX not much point in implementing this; maybe could help with
      95                 :     // debugging?
      96                 :     static bool Read(const Message* msg)
      97                 :     {
      98                 :         return true;
      99                 :     }
     100                 :     void Log(const std::string& aPrefix,
     101                 :              FILE* aOutf) const
     102                 :     {
     103                 :         fputs("(special `Goodbye' message)", aOutf);
     104                 :     }
     105                 : };
     106                 : 
     107                 : } // namespace <anon>
     108                 : 
     109                 : namespace mozilla {
     110                 : namespace ipc {
     111                 : 
     112               0 : AsyncChannel::Link::Link(AsyncChannel *aChan)
     113               0 :     : mChan(aChan)
     114                 : {
     115               0 : }
     116                 : 
     117               0 : AsyncChannel::Link::~Link()
     118                 : {
     119               0 :     mChan = 0;
     120               0 : }
     121                 : 
     122               0 : AsyncChannel::ProcessLink::ProcessLink(AsyncChannel *aChan)
     123                 :     : Link(aChan)
     124               0 :     , mExistingListener(NULL)
     125                 : {
     126               0 : }
     127                 : 
     128               0 : AsyncChannel::ProcessLink::~ProcessLink()
     129                 : {
     130               0 :     mIOLoop = 0;
     131               0 :     if (mTransport) {
     132               0 :         mTransport->set_listener(0);
     133                 :         
     134                 :         // we only hold a weak ref to the transport, which is "owned"
     135                 :         // by GeckoChildProcess/GeckoThread
     136               0 :         mTransport = 0;
     137                 :     }
     138               0 : }
     139                 : 
     140                 : void 
     141               0 : AsyncChannel::ProcessLink::Open(mozilla::ipc::Transport* aTransport,
     142                 :                                 MessageLoop *aIOLoop,
     143                 :                                 Side aSide)
     144                 : {
     145               0 :     NS_PRECONDITION(aTransport, "need transport layer");
     146                 : 
     147                 :     // FIXME need to check for valid channel
     148                 : 
     149               0 :     mTransport = aTransport;
     150               0 :     mExistingListener = mTransport->set_listener(this);
     151                 : 
     152                 :     // FIXME figure out whether we're in parent or child, grab IO loop
     153                 :     // appropriately
     154               0 :     bool needOpen = true;
     155               0 :     if(aIOLoop) {
     156                 :         // We're a child or using the new arguments.  Either way, we
     157                 :         // need an open.
     158               0 :         needOpen = true;
     159               0 :         mChan->mChild = (aSide == AsyncChannel::Unknown) || (aSide == AsyncChannel::Child);
     160                 :     } else {
     161               0 :         NS_PRECONDITION(aSide == Unknown, "expected default side arg");
     162                 : 
     163                 :         // parent
     164               0 :         mChan->mChild = false;
     165               0 :         needOpen = false;
     166               0 :         aIOLoop = XRE_GetIOMessageLoop();
     167                 :         // FIXME assuming that the parent waits for the OnConnected event.
     168                 :         // FIXME see GeckoChildProcessHost.cpp.  bad assumption!
     169               0 :         mChan->mChannelState = ChannelConnected;
     170                 :     }
     171                 : 
     172               0 :     mIOLoop = aIOLoop;
     173                 : 
     174               0 :     NS_ASSERTION(mIOLoop, "need an IO loop");
     175               0 :     NS_ASSERTION(mChan->mWorkerLoop, "need a worker loop");
     176                 : 
     177               0 :     if (needOpen) {             // child process
     178               0 :         MonitorAutoLock lock(*mChan->mMonitor);
     179                 : 
     180                 :         mIOLoop->PostTask(FROM_HERE, 
     181               0 :                           NewRunnableMethod(this, &ProcessLink::OnChannelOpened));
     182                 : 
     183                 :         // FIXME/cjones: handle errors
     184               0 :         while (mChan->mChannelState != ChannelConnected) {
     185               0 :             mChan->mMonitor->Wait();
     186                 :         }
     187                 :     }
     188               0 : }
     189                 : 
     190                 : void
     191               0 : AsyncChannel::ProcessLink::EchoMessage(Message *msg)
     192                 : {
     193               0 :     mChan->AssertWorkerThread();
     194               0 :     mChan->mMonitor->AssertCurrentThreadOwns();
     195                 : 
     196                 :     // NB: Go through this OnMessageReceived indirection so that
     197                 :     // echoing this message does the right thing for SyncChannel
     198                 :     // and RPCChannel too
     199                 :     mIOLoop->PostTask(
     200                 :         FROM_HERE,
     201               0 :         NewRunnableMethod(this, &ProcessLink::OnEchoMessage, msg));
     202                 :     // OnEchoMessage takes ownership of |msg|
     203               0 : }
     204                 : 
     205                 : void
     206               0 : AsyncChannel::ProcessLink::SendMessage(Message *msg)
     207                 : {
     208               0 :     mChan->AssertWorkerThread();
     209               0 :     mChan->mMonitor->AssertCurrentThreadOwns();
     210                 : 
     211                 :     mIOLoop->PostTask(
     212                 :         FROM_HERE,
     213               0 :         NewRunnableMethod(mTransport, &Transport::Send, msg));
     214               0 : }
     215                 : 
     216                 : void
     217               0 : AsyncChannel::ProcessLink::SendClose()
     218                 : {
     219               0 :     mChan->AssertWorkerThread();
     220               0 :     mChan->mMonitor->AssertCurrentThreadOwns();
     221                 : 
     222                 :     mIOLoop->PostTask(
     223               0 :         FROM_HERE, NewRunnableMethod(this, &ProcessLink::OnCloseChannel));
     224               0 : }
     225                 : 
     226               0 : AsyncChannel::ThreadLink::ThreadLink(AsyncChannel *aChan,
     227                 :                                      AsyncChannel *aTargetChan)
     228                 :     : Link(aChan),
     229               0 :       mTargetChan(aTargetChan)
     230                 : {
     231               0 : }
     232                 : 
     233               0 : AsyncChannel::ThreadLink::~ThreadLink()
     234                 : {
     235               0 :     mTargetChan = 0;
     236               0 : }
     237                 : 
     238                 : void
     239               0 : AsyncChannel::ThreadLink::EchoMessage(Message *msg)
     240                 : {
     241               0 :     mChan->AssertWorkerThread();
     242               0 :     mChan->mMonitor->AssertCurrentThreadOwns();
     243                 : 
     244               0 :     mChan->OnMessageReceivedFromLink(*msg);
     245               0 :     delete msg;
     246               0 : }
     247                 : 
     248                 : void
     249               0 : AsyncChannel::ThreadLink::SendMessage(Message *msg)
     250                 : {
     251               0 :     mChan->AssertWorkerThread();
     252               0 :     mChan->mMonitor->AssertCurrentThreadOwns();
     253                 : 
     254               0 :     mTargetChan->OnMessageReceivedFromLink(*msg);
     255               0 :     delete msg;
     256               0 : }
     257                 : 
     258                 : void
     259               0 : AsyncChannel::ThreadLink::SendClose()
     260                 : {
     261               0 :     mChan->AssertWorkerThread();
     262               0 :     mChan->mMonitor->AssertCurrentThreadOwns();
     263                 : 
     264               0 :     mChan->mChannelState = ChannelClosed;
     265                 : 
     266                 :     // In a ProcessLink, we would close our half the channel.  This
     267                 :     // would show up on the other side as an error on the I/O thread.
     268                 :     // The I/O thread would then invoke OnChannelErrorFromLink().
     269                 :     // As usual, we skip that process and just invoke the
     270                 :     // OnChannelErrorFromLink() method directly.
     271               0 :     mTargetChan->OnChannelErrorFromLink();
     272               0 : }
     273                 : 
     274               1 : AsyncChannel::AsyncChannel(AsyncListener* aListener)
     275                 :   : mListener(aListener),
     276                 :     mChannelState(ChannelClosed),
     277                 :     mWorkerLoop(),
     278                 :     mChild(false),
     279                 :     mChannelErrorTask(NULL),
     280               1 :     mLink(NULL)
     281                 : {
     282               1 :     MOZ_COUNT_CTOR(AsyncChannel);
     283               1 : }
     284                 : 
     285               0 : AsyncChannel::~AsyncChannel()
     286                 : {
     287               0 :     MOZ_COUNT_DTOR(AsyncChannel);
     288               0 :     Clear();
     289               0 : }
     290                 : 
     291                 : bool
     292               0 : AsyncChannel::Open(Transport* aTransport,
     293                 :                    MessageLoop* aIOLoop,
     294                 :                    AsyncChannel::Side aSide)
     295                 : {
     296                 :     ProcessLink *link;
     297               0 :     NS_PRECONDITION(!mLink, "Open() called > once");
     298               0 :     mMonitor = new RefCountedMonitor();
     299               0 :     mWorkerLoop = MessageLoop::current();
     300               0 :     mLink = link = new ProcessLink(this);
     301               0 :     link->Open(aTransport, aIOLoop, aSide); // n.b.: sets mChild
     302               0 :     return true;
     303                 : }
     304                 : 
     305                 : /* Opens a connection to another thread in the same process.
     306                 : 
     307                 :    This handshake proceeds as follows:
     308                 :    - Let A be the thread initiating the process (either child or parent)
     309                 :      and B be the other thread.
     310                 :    - A spawns thread for B, obtaining B's message loop
     311                 :    - A creates ProtocolChild and ProtocolParent instances.
     312                 :      Let PA be the one appropriate to A and PB the side for B.
     313                 :    - A invokes PA->Open(PB, ...):
     314                 :      - set state to mChannelOpening
     315                 :      - this will place a work item in B's worker loop (see next bullet)
     316                 :        and then spins until PB->mChannelState becomes mChannelConnected
     317                 :      - meanwhile, on PB's worker loop, the work item is removed and:
     318                 :        - invokes PB->SlaveOpen(PA, ...):
     319                 :          - sets its state and that of PA to Connected
     320                 :  */
     321                 : bool
     322               0 : AsyncChannel::Open(AsyncChannel *aTargetChan, 
     323                 :                    MessageLoop *aTargetLoop,
     324                 :                    AsyncChannel::Side aSide)
     325                 : {
     326               0 :     NS_PRECONDITION(aTargetChan, "Need a target channel");
     327               0 :     NS_PRECONDITION(ChannelClosed == mChannelState, "Not currently closed");
     328                 : 
     329               0 :     CommonThreadOpenInit(aTargetChan, aSide);
     330                 : 
     331               0 :     Side oppSide = Unknown;
     332               0 :     switch(aSide) {
     333               0 :       case Child: oppSide = Parent; break;
     334               0 :       case Parent: oppSide = Child; break;
     335               0 :       case Unknown: break;
     336                 :     }
     337                 : 
     338               0 :     mMonitor = new RefCountedMonitor();
     339                 : 
     340                 :     aTargetLoop->PostTask(
     341                 :         FROM_HERE,
     342                 :         NewRunnableMethod(aTargetChan, &AsyncChannel::OnOpenAsSlave,
     343               0 :                           this, oppSide));
     344                 : 
     345               0 :     MonitorAutoLock lock(*mMonitor);
     346               0 :     mChannelState = ChannelOpening;
     347               0 :     while (ChannelOpening == mChannelState)
     348               0 :         mMonitor->Wait();
     349               0 :     NS_ASSERTION(ChannelConnected == mChannelState, "not connected when awoken");
     350               0 :     return (ChannelConnected == mChannelState);
     351                 : }
     352                 : 
     353                 : void 
     354               0 : AsyncChannel::CommonThreadOpenInit(AsyncChannel *aTargetChan, Side aSide)
     355                 : {
     356               0 :     mWorkerLoop = MessageLoop::current();
     357               0 :     mLink = new ThreadLink(this, aTargetChan);
     358               0 :     mChild = (aSide == Child); 
     359               0 : }
     360                 : 
     361                 : // Invoked when the other side has begun the open.
     362                 : void
     363               0 : AsyncChannel::OnOpenAsSlave(AsyncChannel *aTargetChan, Side aSide)
     364                 : {
     365               0 :     NS_PRECONDITION(ChannelClosed == mChannelState, 
     366                 :                     "Not currently closed");
     367               0 :     NS_PRECONDITION(ChannelOpening == aTargetChan->mChannelState,
     368                 :                     "Target channel not in the process of opening");
     369                 :     
     370               0 :     CommonThreadOpenInit(aTargetChan, aSide);
     371               0 :     mMonitor = aTargetChan->mMonitor;
     372                 : 
     373               0 :     MonitorAutoLock lock(*mMonitor);
     374               0 :     NS_ASSERTION(ChannelOpening == aTargetChan->mChannelState,
     375                 :                  "Target channel not in the process of opening");
     376               0 :     mChannelState = ChannelConnected;
     377               0 :     aTargetChan->mChannelState = ChannelConnected;
     378               0 :     aTargetChan->mMonitor->Notify();
     379               0 : }
     380                 : 
     381                 : void
     382               0 : AsyncChannel::Close()
     383                 : {
     384               0 :     AssertWorkerThread();
     385                 : 
     386                 :     {
     387                 :         // n.b.: We increase the ref count of monitor temporarily
     388                 :         //       for the duration of this block.  Otherwise, the
     389                 :         //       function NotifyMaybeChannelError() will call
     390                 :         //       ::Clear() which can free the monitor.
     391               0 :         nsRefPtr<RefCountedMonitor> monitor(mMonitor);
     392               0 :         MonitorAutoLock lock(*monitor);
     393                 : 
     394               0 :         if (ChannelError == mChannelState ||
     395                 :             ChannelTimeout == mChannelState) {
     396                 :             // See bug 538586: if the listener gets deleted while the
     397                 :             // IO thread's NotifyChannelError event is still enqueued
     398                 :             // and subsequently deletes us, then the error event will
     399                 :             // also be deleted and the listener will never be notified
     400                 :             // of the channel error.
     401               0 :             if (mListener) {
     402               0 :                 MonitorAutoUnlock unlock(*monitor);
     403               0 :                 NotifyMaybeChannelError();
     404                 :             }
     405                 :             return;
     406                 :         }
     407                 : 
     408               0 :         if (ChannelConnected != mChannelState)
     409                 :             // XXX be strict about this until there's a compelling reason
     410                 :             // to relax
     411               0 :             NS_RUNTIMEABORT("Close() called on closed channel!");
     412                 : 
     413               0 :         AssertWorkerThread();
     414                 : 
     415                 :         // notify the other side that we're about to close our socket
     416               0 :         SendSpecialMessage(new GoodbyeMessage());
     417                 : 
     418               0 :         SynchronouslyClose();
     419                 :     }
     420                 : 
     421               0 :     NotifyChannelClosed();
     422                 : }
     423                 : 
     424                 : void 
     425               0 : AsyncChannel::SynchronouslyClose()
     426                 : {
     427               0 :     AssertWorkerThread();
     428               0 :     mMonitor->AssertCurrentThreadOwns();
     429               0 :     mLink->SendClose();
     430               0 :     while (ChannelClosed != mChannelState)
     431               0 :         mMonitor->Wait();
     432               0 : }
     433                 : 
     434                 : bool
     435               0 : AsyncChannel::Send(Message* _msg)
     436                 : {
     437               0 :     nsAutoPtr<Message> msg(_msg);
     438               0 :     AssertWorkerThread();
     439               0 :     mMonitor->AssertNotCurrentThreadOwns();
     440               0 :     NS_ABORT_IF_FALSE(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
     441                 : 
     442                 :     {
     443               0 :         MonitorAutoLock lock(*mMonitor);
     444                 : 
     445               0 :         if (!Connected()) {
     446               0 :             ReportConnectionError("AsyncChannel");
     447               0 :             return false;
     448                 :         }
     449                 : 
     450               0 :         mLink->SendMessage(msg.forget());
     451                 :     }
     452                 : 
     453               0 :     return true;
     454                 : }
     455                 : 
     456                 : bool
     457               0 : AsyncChannel::Echo(Message* _msg)
     458                 : {
     459               0 :     nsAutoPtr<Message> msg(_msg);
     460               0 :     AssertWorkerThread();
     461               0 :     mMonitor->AssertNotCurrentThreadOwns();
     462               0 :     NS_ABORT_IF_FALSE(MSG_ROUTING_NONE != msg->routing_id(), "need a route");
     463                 : 
     464                 :     {
     465               0 :         MonitorAutoLock lock(*mMonitor);
     466                 : 
     467               0 :         if (!Connected()) {
     468               0 :             ReportConnectionError("AsyncChannel");
     469               0 :             return false;
     470                 :         }
     471                 : 
     472               0 :         mLink->EchoMessage(msg.forget());
     473                 :     }
     474                 : 
     475               0 :     return true;
     476                 : }
     477                 : 
     478                 : void
     479               0 : AsyncChannel::OnDispatchMessage(const Message& msg)
     480                 : {
     481               0 :     AssertWorkerThread();
     482               0 :     NS_ASSERTION(!msg.is_reply(), "can't process replies here");
     483               0 :     NS_ASSERTION(!(msg.is_sync() || msg.is_rpc()), "async dispatch only");
     484                 : 
     485               0 :     if (MSG_ROUTING_NONE == msg.routing_id()) {
     486               0 :         if (!OnSpecialMessage(msg.type(), msg))
     487                 :             // XXX real error handling
     488               0 :             NS_RUNTIMEABORT("unhandled special message!");
     489               0 :         return;
     490                 :     }
     491                 : 
     492                 :     // it's OK to dispatch messages if the channel is closed/error'd,
     493                 :     // since we don't have a reply to send back
     494                 : 
     495               0 :     (void)MaybeHandleError(mListener->OnMessageReceived(msg), "AsyncChannel");
     496                 : }
     497                 : 
     498                 : bool
     499               0 : AsyncChannel::OnSpecialMessage(uint16 id, const Message& msg)
     500                 : {
     501               0 :     return false;
     502                 : }
     503                 : 
     504                 : void
     505               0 : AsyncChannel::SendSpecialMessage(Message* msg) const
     506                 : {
     507               0 :     AssertWorkerThread();
     508               0 :     mLink->SendMessage(msg);
     509               0 : }
     510                 : 
     511                 : void
     512               0 : AsyncChannel::OnNotifyMaybeChannelError()
     513                 : {
     514               0 :     AssertWorkerThread();
     515               0 :     mMonitor->AssertNotCurrentThreadOwns();
     516                 : 
     517                 :     // OnChannelError holds mMonitor when it posts this task and this
     518                 :     // task cannot be allowed to run until OnChannelError has
     519                 :     // exited. We enforce that order by grabbing the mutex here which
     520                 :     // should only continue once OnChannelError has completed.
     521                 :     {
     522               0 :         MonitorAutoLock lock(*mMonitor);
     523                 :         // nothing to do here
     524                 :     }
     525                 : 
     526               0 :     if (ShouldDeferNotifyMaybeError()) {
     527                 :         mChannelErrorTask =
     528               0 :             NewRunnableMethod(this, &AsyncChannel::OnNotifyMaybeChannelError);
     529                 :         // 10 ms delay is completely arbitrary
     530               0 :         mWorkerLoop->PostDelayedTask(FROM_HERE, mChannelErrorTask, 10);
     531               0 :         return;
     532                 :     }
     533                 : 
     534               0 :     NotifyMaybeChannelError();
     535                 : }
     536                 : 
     537                 : void
     538               0 : AsyncChannel::NotifyChannelClosed()
     539                 : {
     540               0 :     mMonitor->AssertNotCurrentThreadOwns();
     541                 : 
     542               0 :     if (ChannelClosed != mChannelState)
     543               0 :         NS_RUNTIMEABORT("channel should have been closed!");
     544                 : 
     545                 :     // OK, the IO thread just closed the channel normally.  Let the
     546                 :     // listener know about it.
     547               0 :     mListener->OnChannelClose();
     548                 : 
     549               0 :     Clear();
     550               0 : }
     551                 : 
     552                 : void
     553               0 : AsyncChannel::NotifyMaybeChannelError()
     554                 : {
     555               0 :     mMonitor->AssertNotCurrentThreadOwns();
     556                 : 
     557                 :     // TODO sort out Close() on this side racing with Close() on the
     558                 :     // other side
     559               0 :     if (ChannelClosing == mChannelState) {
     560                 :         // the channel closed, but we received a "Goodbye" message
     561                 :         // warning us about it. no worries
     562               0 :         mChannelState = ChannelClosed;
     563               0 :         NotifyChannelClosed();
     564               0 :         return;
     565                 :     }
     566                 : 
     567                 :     // Oops, error!  Let the listener know about it.
     568               0 :     mChannelState = ChannelError;
     569               0 :     mListener->OnChannelError();
     570                 : 
     571               0 :     Clear();
     572                 : }
     573                 : 
     574                 : void
     575               0 : AsyncChannel::Clear()
     576                 : {
     577               0 :     mListener = 0;
     578               0 :     mWorkerLoop = 0;
     579                 : 
     580               0 :     delete mLink;
     581               0 :     mLink = 0;
     582               0 :     mMonitor = 0;
     583                 : 
     584               0 :     if (mChannelErrorTask) {
     585               0 :         mChannelErrorTask->Cancel();
     586               0 :         mChannelErrorTask = NULL;
     587                 :     }
     588               0 : }
     589                 : 
     590                 : static void
     591               0 : PrintErrorMessage(bool isChild, const char* channelName, const char* msg)
     592                 : {
     593                 : #ifdef DEBUG
     594                 :     fprintf(stderr, "\n###!!! [%s][%s] Error: %s\n\n",
     595               0 :             isChild ? "Child" : "Parent", channelName, msg);
     596                 : #endif
     597               0 : }
     598                 : 
     599                 : bool
     600               0 : AsyncChannel::MaybeHandleError(Result code, const char* channelName)
     601                 : {
     602               0 :     if (MsgProcessed == code)
     603               0 :         return true;
     604                 : 
     605                 :     const char* errorMsg;
     606               0 :     switch (code) {
     607                 :     case MsgNotKnown:
     608               0 :         errorMsg = "Unknown message: not processed";
     609               0 :         break;
     610                 :     case MsgNotAllowed:
     611               0 :         errorMsg = "Message not allowed: cannot be sent/recvd in this state";
     612               0 :         break;
     613                 :     case MsgPayloadError:
     614               0 :         errorMsg = "Payload error: message could not be deserialized";
     615               0 :         break;
     616                 :     case MsgProcessingError:
     617               0 :         errorMsg = "Processing error: message was deserialized, but the handler returned false (indicating failure)";
     618               0 :         break;
     619                 :     case MsgRouteError:
     620               0 :         errorMsg = "Route error: message sent to unknown actor ID";
     621               0 :         break;
     622                 :     case MsgValueError:
     623               0 :         errorMsg = "Value error: message was deserialized, but contained an illegal value";
     624               0 :         break;
     625                 : 
     626                 :     default:
     627               0 :         NS_RUNTIMEABORT("unknown Result code");
     628               0 :         return false;
     629                 :     }
     630                 : 
     631               0 :     PrintErrorMessage(mChild, channelName, errorMsg);
     632                 : 
     633               0 :     mListener->OnProcessingError(code);
     634                 : 
     635               0 :     return false;
     636                 : }
     637                 : 
     638                 : void
     639               0 : AsyncChannel::ReportConnectionError(const char* channelName) const
     640                 : {
     641                 :     const char* errorMsg;
     642               0 :     switch (mChannelState) {
     643                 :     case ChannelClosed:
     644               0 :         errorMsg = "Closed channel: cannot send/recv";
     645               0 :         break;
     646                 :     case ChannelOpening:
     647               0 :         errorMsg = "Opening channel: not yet ready for send/recv";
     648               0 :         break;
     649                 :     case ChannelTimeout:
     650               0 :         errorMsg = "Channel timeout: cannot send/recv";
     651               0 :         break;
     652                 :     case ChannelClosing:
     653               0 :         errorMsg = "Channel closing: too late to send/recv, messages will be lost";
     654               0 :         break;
     655                 :     case ChannelError:
     656               0 :         errorMsg = "Channel error: cannot send/recv";
     657               0 :         break;
     658                 : 
     659                 :     default:
     660               0 :         NS_RUNTIMEABORT("unreached");
     661                 :     }
     662                 : 
     663               0 :     PrintErrorMessage(mChild, channelName, errorMsg);
     664                 : 
     665               0 :     mListener->OnProcessingError(MsgDropped);
     666               0 : }
     667                 : 
     668                 : void
     669               0 : AsyncChannel::DispatchOnChannelConnected(int32 peer_pid)
     670                 : {
     671               0 :     AssertWorkerThread();
     672               0 :     if (mListener)
     673               0 :         mListener->OnChannelConnected(peer_pid);
     674               0 : }
     675                 : 
     676                 : //
     677                 : // The methods below run in the context of the IO thread
     678                 : //
     679                 : 
     680                 : void
     681               0 : AsyncChannel::ProcessLink::OnMessageReceived(const Message& msg)
     682                 : {
     683               0 :     AssertIOThread();
     684               0 :     NS_ASSERTION(mChan->mChannelState != ChannelError, "Shouldn't get here!");
     685               0 :     MonitorAutoLock lock(*mChan->mMonitor);
     686               0 :     mChan->OnMessageReceivedFromLink(msg);
     687               0 : }
     688                 : 
     689                 : void
     690               0 : AsyncChannel::ProcessLink::OnEchoMessage(Message* msg)
     691                 : {
     692               0 :     AssertIOThread();
     693               0 :     OnMessageReceived(*msg);
     694               0 :     delete msg;
     695               0 : }
     696                 : 
     697                 : void
     698               0 : AsyncChannel::ProcessLink::OnChannelOpened()
     699                 : {
     700               0 :     mChan->AssertLinkThread();
     701                 :     {
     702               0 :         MonitorAutoLock lock(*mChan->mMonitor);
     703               0 :         mChan->mChannelState = ChannelOpening;
     704                 :     }
     705               0 :     /*assert*/mTransport->Connect();
     706               0 : }
     707                 : 
     708                 : void
     709               0 : AsyncChannel::ProcessLink::OnChannelConnected(int32 peer_pid)
     710                 : {
     711               0 :     AssertIOThread();
     712                 : 
     713                 :     {
     714               0 :         MonitorAutoLock lock(*mChan->mMonitor);
     715               0 :         mChan->mChannelState = ChannelConnected;
     716               0 :         mChan->mMonitor->Notify();
     717                 :     }
     718                 : 
     719               0 :     if(mExistingListener)
     720               0 :         mExistingListener->OnChannelConnected(peer_pid);
     721                 : 
     722                 :     mChan->mWorkerLoop->PostTask(
     723                 :         FROM_HERE, 
     724                 :         NewRunnableMethod(mChan, 
     725                 :                           &AsyncChannel::DispatchOnChannelConnected, 
     726               0 :                           peer_pid));
     727               0 : }
     728                 : 
     729                 : void
     730               0 : AsyncChannel::ProcessLink::OnChannelError()
     731                 : {
     732               0 :     AssertIOThread();
     733               0 :     MonitorAutoLock lock(*mChan->mMonitor);
     734               0 :     mChan->OnChannelErrorFromLink();
     735               0 : }
     736                 : 
     737                 : void
     738               0 : AsyncChannel::ProcessLink::OnCloseChannel()
     739                 : {
     740               0 :     AssertIOThread();
     741                 : 
     742               0 :     mTransport->Close();
     743                 : 
     744               0 :     MonitorAutoLock lock(*mChan->mMonitor);
     745               0 :     mChan->mChannelState = ChannelClosed;
     746               0 :     mChan->mMonitor->Notify();
     747               0 : }
     748                 : 
     749                 : //
     750                 : // The methods below run in the context of the link thread
     751                 : //
     752                 : 
     753                 : void
     754               0 : AsyncChannel::OnMessageReceivedFromLink(const Message& msg)
     755                 : {
     756               0 :     AssertLinkThread();
     757               0 :     mMonitor->AssertCurrentThreadOwns();
     758                 : 
     759               0 :     if (!MaybeInterceptSpecialIOMessage(msg))
     760                 :         // wake up the worker, there's work to do
     761                 :         mWorkerLoop->PostTask(
     762                 :             FROM_HERE,
     763               0 :             NewRunnableMethod(this, &AsyncChannel::OnDispatchMessage, msg));
     764               0 : }
     765                 : 
     766                 : void
     767               0 : AsyncChannel::OnChannelErrorFromLink()
     768                 : {
     769               0 :     AssertLinkThread();
     770               0 :     mMonitor->AssertCurrentThreadOwns();
     771                 : 
     772               0 :     if (ChannelClosing != mChannelState)
     773               0 :         mChannelState = ChannelError;
     774                 : 
     775               0 :     PostErrorNotifyTask();
     776               0 : }
     777                 : 
     778                 : void
     779               0 : AsyncChannel::PostErrorNotifyTask()
     780                 : {
     781               0 :     AssertLinkThread();
     782               0 :     mMonitor->AssertCurrentThreadOwns();
     783                 : 
     784               0 :     NS_ASSERTION(!mChannelErrorTask, "OnChannelError called twice?");
     785                 : 
     786                 :     // This must be the last code that runs on this thread!
     787                 :     mChannelErrorTask =
     788               0 :         NewRunnableMethod(this, &AsyncChannel::OnNotifyMaybeChannelError);
     789               0 :     mWorkerLoop->PostTask(FROM_HERE, mChannelErrorTask);
     790               0 : }
     791                 : 
     792                 : bool
     793               0 : AsyncChannel::MaybeInterceptSpecialIOMessage(const Message& msg)
     794                 : {
     795               0 :     AssertLinkThread();
     796               0 :     mMonitor->AssertCurrentThreadOwns();
     797                 : 
     798               0 :     if (MSG_ROUTING_NONE == msg.routing_id()
     799               0 :         && GOODBYE_MESSAGE_TYPE == msg.type()) {
     800               0 :         ProcessGoodbyeMessage();
     801               0 :         return true;
     802                 :     }
     803               0 :     return false;
     804                 : }
     805                 : 
     806                 : void
     807               0 : AsyncChannel::ProcessGoodbyeMessage()
     808                 : {
     809               0 :     AssertLinkThread();
     810               0 :     mMonitor->AssertCurrentThreadOwns();
     811                 : 
     812                 :     // TODO sort out Close() on this side racing with Close() on the
     813                 :     // other side
     814               0 :     mChannelState = ChannelClosing;
     815                 : 
     816                 :     printf("NOTE: %s process received `Goodbye', closing down\n",
     817               0 :            mChild ? "child" : "parent");
     818               0 : }
     819                 : 
     820                 : 
     821                 : } // namespace ipc
     822                 : } // namespace mozilla

Generated by: LCOV version 1.7