File indexing completed on 2024-04-28 16:59:42

0001 /*
0002    Copyright (C) 2013 Andreas Hartmetz <ahartmetz@gmail.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LGPL.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 
0019    Alternatively, this file is available under the Mozilla Public License
0020    Version 1.1.  You may obtain a copy of the License at
0021    http://www.mozilla.org/MPL/
0022 */
0023 
0024 #ifndef CONNECTION_P_H
0025 #define CONNECTION_P_H
0026 
0027 #include "connection.h"
0028 
0029 #include "connectaddress.h"
0030 #include "eventdispatcher_p.h"
0031 #include "icompletionlistener.h"
0032 #include "iioeventforwarder.h"
0033 #include "spinlock.h"
0034 
0035 #include <deque>
0036 #include <unordered_map>
0037 #include <vector>
0038 
0039 class AuthClient;
0040 class HelloReceiver;
0041 class IMessageReceiver;
0042 class ITransport;
0043 class ClientConnectedHandler;
0044 
0045 /*
0046  How to handle destruction of connected Connections
0047 
0048  Main thread connection destroyed: Need to
0049  - "cancel" registered PendingReplies from other threads
0050    (I guess also own ones, we're not doing that, I think...)
0051  - Make sure that other threads stop calling us because that's going to be a memory error when
0052    our instance has been deleted
0053 
0054  Secondary thread Connection destroyed: Need to
0055   - "cancel" PendingReplies registered in main thread
0056   - unregister from main thread as receiver of spontaneous messages because receiving events about
0057     it is going to be a memory error when our instance has been deleted
0058 
0059  Problem areas:
0060   - destroying a Connection with a locked lock (locked from another thread, obviously)
0061     - can solved by "thoroughly" disconnecting from everything before destruction
0062   - deadlocks / locking order - preliminary solution: always main Connection first, then secondary
0063     - what about the lock in EventDispatcher?
0064   - blocking: secondary blocking (as in waiting for an event - both Connections wait on *locks* of
0065     the other) on main is okay, it does that all the time anyway. main blocking on secondary is
0066     probably (not sure) not okay.
0067 
0068  Let's define some invariants:
0069   - When a Connection is destroyed, all its PendingReply instances must have been  detached
0070     (completed with or without error) or destroyed. "Its" means sent through that Connection's
0071     send() method, not when a PendingReply is using the connection of the Connection but send()
0072     was called on the Connection of another thread.
0073   - When a master and a secondary connection try to communicate in any way, and the other party
0074     has been destroyed, communication will fail gracefully and there will be no crash or undefined
0075     behavior. Any pending replies that cannot finish successfully anymore will finish with an
0076     LocalDisconnect error.
0077 */
0078 
0079 class ConnectionStateChanger;
0080 
0081 // This class sits between EventDispatcher and ITransport for I/O event forwarding purposes,
0082 // which is why it is both a listener (for EventDispatcher) and a source (mainly for ITransport)
0083 class ConnectionPrivate : public IIoEventForwarder, public ICompletionListener
0084 {
0085 public:
0086     enum State {
0087         Unconnected = 0,
0088         ServerWaitingForClient,
0089         Authenticating,
0090         AwaitingUniqueName,
0091         Connected
0092     };
0093 
0094     static ConnectionPrivate *get(Connection *c) { return c->d; }
0095 
0096     ConnectionPrivate(Connection *connection, EventDispatcher *dispatcher);
0097     void close(Error withError);
0098 
0099     // from IIOEventForwarder
0100     IO::Status handleIoReady(IO::RW rw) override;
0101 
0102     void startAuthentication();
0103     void handleHelloReply();
0104     void handleHelloFailed();
0105     // invokes m_connectionStateListener, if any
0106     void notifyStateChange(Connection::State oldUserState, Connection::State newUserState);
0107 
0108     void handleClientConnected();
0109 
0110     uint32 takeNextSerial();
0111 
0112     Error prepareSend(Message *msg);
0113     void sendPreparedMessage(Message msg);
0114 
0115     void handleCompletion(void *task) override;
0116     bool maybeDispatchToPendingReply(Message *m);
0117     bool maybeDispatchToPendingReply(uint32 serial, Error error);
0118     void receiveNextMessage();
0119 
0120     void unregisterPendingReply(PendingReplyPrivate *p);
0121     void cancelAllPendingReplies(Error withError);
0122     void discardPendingRepliesForSecondaryThread(ConnectionPrivate *t);
0123 
0124     // For cross-thread communication between thread Connections. We could have a more complete event
0125     // system, but there is currently no need, so keep it simple and limited.
0126     void processEvent(Event *evt); // called from thread-local EventDispatcher
0127 
0128     State m_state = Unconnected;
0129     bool m_closing = false;
0130     bool m_unixFdPassingEnabled = false;
0131 
0132     Connection *m_connection = nullptr;
0133     IMessageReceiver *m_client = nullptr;
0134     IConnectionStateListener *m_connectionStateListener = nullptr;
0135 
0136     Message *m_receivingMessage = nullptr;
0137     std::deque<Message> m_sendQueue; // waiting to be sent
0138 
0139     // only one of them can be non-null. exception: in the main thread, m_mainThreadConnection
0140     // equals this, so that the main thread knows it's the main thread and not just a thread-local
0141     // connection.
0142     ITransport *m_transport = nullptr;
0143 
0144     HelloReceiver *m_helloReceiver = nullptr;
0145     ClientConnectedHandler *m_clientConnectedHandler = nullptr;
0146 
0147     EventDispatcher *m_eventDispatcher = nullptr;
0148     ConnectAddress m_connectAddress;
0149     std::string m_uniqueName;
0150     AuthClient *m_authClient = nullptr;
0151 
0152     int m_defaultTimeout = 25000;
0153 
0154     class PendingReplyRecord
0155     {
0156     public:
0157         PendingReplyRecord(PendingReplyPrivate *pr) : isForSecondaryThread(false), ptr(pr) {}
0158         PendingReplyRecord(ConnectionPrivate *tp) : isForSecondaryThread(true), ptr(tp) {}
0159 
0160         PendingReplyPrivate *asPendingReply() const
0161             { return isForSecondaryThread ? nullptr : static_cast<PendingReplyPrivate *>(ptr); }
0162         ConnectionPrivate *asConnection() const
0163             { return isForSecondaryThread ? static_cast<ConnectionPrivate *>(ptr) : nullptr; }
0164 
0165     private:
0166         bool isForSecondaryThread;
0167         void *ptr;
0168     };
0169     std::unordered_map<uint32, PendingReplyRecord> m_pendingReplies; // replies we're waiting for
0170 
0171     Spinlock m_lock; // only one lock because things done with lock held are quick, and anyway you shouldn't
0172                      // be using one connection from multiple threads if you need best performance
0173 
0174     std::atomic<uint32> m_sendSerial { 1 };
0175 
0176     std::unordered_map<ConnectionPrivate *, CommutexPeer> m_secondaryThreadLinks;
0177     std::vector<CommutexPeer> m_unredeemedCommRefs; // for createCommRef() and the constructor from CommRef
0178 
0179     ConnectionPrivate *m_mainThreadConnection = nullptr;
0180     CommutexPeer m_mainThreadLink;
0181 };
0182 
0183 // This class helps with notifying a Connection's StateChanegListener when connection state changes.
0184 // Its benefits are:
0185 // - Tracks state changes in a few easily verified pieces of code
0186 // - Prevents crashes from the  following scenario: IConnectionStateListener is notified about a change. As a
0187 //   reaction, it may delete the Connection. The listener returns and control passes back into Connection
0188 //   code. Connection code touches some of its (or rather, ConnectionPrivate's) data, which has been deleted
0189 //   at that point. Undefined behavior ensues.
0190 //   With the help of this class, the IConnectionStateListener is always called just before exit, so that no
0191 //   member data can be touched afterwards. (This pattern is a good idea for almost any kind of callback.)
0192 class ConnectionStateChanger
0193 {
0194 public:
0195     ConnectionStateChanger(ConnectionPrivate *cp);
0196     ConnectionStateChanger(ConnectionPrivate *cp, ConnectionPrivate::State newState);
0197 
0198     ConnectionStateChanger(const ConnectionStateChanger &) = delete;
0199     ConnectionStateChanger &operator=(const ConnectionStateChanger &) = delete;
0200 
0201     ~ConnectionStateChanger();
0202 
0203     void setNewState(ConnectionPrivate::State newState);
0204     void disable();
0205 
0206 private:
0207     ConnectionPrivate *m_connPrivate;
0208     int32 m_oldState = -1; // either -1 or a valid ConnectionPrivate::State
0209 };
0210 
0211 #endif // CONNECTION_P_H