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 }