File indexing completed on 2024-05-12 09:46:11
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