File indexing completed on 2024-04-28 16:59:42
0001 /* 0002 Copyright (C) 2014 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 #include "pendingreply.h" 0025 #include "pendingreply_p.h" 0026 0027 #include "imessagereceiver.h" 0028 #include "connection.h" 0029 #include "connection_p.h" 0030 0031 #include <cassert> 0032 #include <iostream> 0033 0034 PendingReply::PendingReply() 0035 : d(nullptr) 0036 { 0037 } 0038 0039 PendingReply::~PendingReply() 0040 { 0041 if (!d) { 0042 return; 0043 } 0044 if (!d->m_isFinished) { 0045 if (d->m_connectionOrReply.connection) { 0046 d->m_connectionOrReply.connection->unregisterPendingReply(d); 0047 } 0048 } else { 0049 if (d->m_connectionOrReply.reply) { 0050 delete d->m_connectionOrReply.reply; 0051 } 0052 } 0053 delete d; 0054 d = nullptr; 0055 } 0056 0057 PendingReply::PendingReply(PendingReplyPrivate *priv) 0058 : d(priv) 0059 { 0060 d->m_owner = this; 0061 } 0062 0063 PendingReply::PendingReply(PendingReply &&other) 0064 : d(other.d) 0065 { 0066 other.d = nullptr; 0067 if (d) { 0068 d->m_owner = this; 0069 } 0070 } 0071 0072 PendingReply &PendingReply::operator=(PendingReply &&other) 0073 { 0074 if (this == &other) { 0075 return *this; 0076 } 0077 delete d; 0078 d = other.d; 0079 other.d = nullptr; 0080 // note that in this class, !d is a valid state; otherwise this check wouldn't be necessary because 0081 // moved-from objects (that also have !d) are not safe for any operation but destruction 0082 if (d) { 0083 d->m_owner = this; 0084 } 0085 return *this; 0086 } 0087 0088 void PendingReplyPrivate::handleReceived(Message *reply) 0089 { 0090 m_isFinished = true; 0091 // Connection has already unregistered us because it knows this reply is done 0092 Connection *const connection = m_connectionOrReply.connection->m_connection; 0093 m_connectionOrReply.reply = reply; 0094 m_replyTimeout.stop(); 0095 if (m_receiver) { 0096 m_receiver->handlePendingReplyFinished(m_owner, connection); 0097 } 0098 } 0099 0100 void PendingReply::dumpState() 0101 { 0102 std::cerr << "PendingReply::dumpState() " << d << '\n'; 0103 if (d) { 0104 std::cerr << d->m_owner << " " << d->m_connectionOrReply.reply << " " << d->m_serial << " " 0105 << int(d->m_error.code()) << " " /* << d->m_reply->type() */ << '\n'; 0106 } 0107 } 0108 0109 bool PendingReply::isNull() const 0110 { 0111 return !d; 0112 } 0113 0114 bool PendingReply::isFinished() const 0115 { 0116 return !d || d->m_isFinished; 0117 } 0118 0119 bool PendingReply::hasNonErrorReply() const 0120 { 0121 return d && d->m_isFinished && !d->m_error.isError(); 0122 } 0123 0124 Error PendingReply::error() const 0125 { 0126 if (!d) { 0127 return Error::DetachedPendingReply; 0128 } 0129 return d->m_error; 0130 } 0131 0132 bool PendingReply::isError() const 0133 { 0134 return d && d->m_error.isError(); 0135 } 0136 0137 void PendingReply::setCookie(void *cookie) 0138 { 0139 d->m_cookie = cookie; 0140 } 0141 0142 void *PendingReply::cookie() const 0143 { 0144 return d->m_cookie; 0145 } 0146 0147 void PendingReply::setReceiver(IMessageReceiver *receiver) 0148 { 0149 if (d) { 0150 d->m_receiver = receiver; 0151 } else { 0152 // if !d, this is a detached (invalid) instance, and that can't be changed. 0153 std::cerr << "PendingReply::setReceiver() on a detached instance does nothing.\n"; 0154 } 0155 } 0156 0157 IMessageReceiver *PendingReply::receiver() const 0158 { 0159 return d ? d->m_receiver : nullptr; 0160 } 0161 0162 const Message *PendingReply::reply() const 0163 { 0164 return d->m_isFinished ? d->m_connectionOrReply.reply : nullptr; 0165 } 0166 0167 Message PendingReply::takeReply() 0168 { 0169 Message reply; 0170 if (d->m_isFinished) { 0171 reply = std::move(*d->m_connectionOrReply.reply); 0172 delete d->m_connectionOrReply.reply; 0173 d->m_connectionOrReply.reply = nullptr; 0174 } 0175 return reply; 0176 } 0177 0178 void PendingReplyPrivate::handleCompletion(void *task) 0179 { 0180 assert(task == &m_replyTimeout); 0181 (void) task; 0182 assert(!m_isFinished); 0183 // if a reply comes after the timout, it's too late and the reply is probably served as a spontaneous 0184 // message by Connection 0185 if (m_connectionOrReply.connection) { 0186 m_connectionOrReply.connection->unregisterPendingReply(this); 0187 } 0188 handleError(Error::Timeout); 0189 } 0190 0191 void PendingReplyPrivate::handleError(Error error) 0192 { 0193 // When there is an error before or during sending, we already have an error, and the timeout it set to 0194 // zero seconds instead of calling the callback right away, in order to provide more consistent behavior 0195 // to API clients. In that case, the timeout itself is not the error. 0196 if (!m_error.isError()) { 0197 m_error = error; 0198 } 0199 m_isFinished = true; 0200 Connection *const connection = m_connectionOrReply.connection->m_connection; 0201 m_connectionOrReply.reply = nullptr; 0202 if (m_receiver) { 0203 m_receiver->handlePendingReplyFinished(m_owner, connection); 0204 } 0205 }