blob: 9d9fbcef06b06310fb03fbb771ffb673c7ec8c37 [file] [log] [blame]
/*
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef NET_DCSCTP_SOCKET_CALLBACK_DEFERRER_H_
#define NET_DCSCTP_SOCKET_CALLBACK_DEFERRER_H_
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "absl/types/variant.h"
#include "api/array_view.h"
#include "api/ref_counted_base.h"
#include "api/scoped_refptr.h"
#include "api/task_queue/task_queue_base.h"
#include "net/dcsctp/public/dcsctp_message.h"
#include "net/dcsctp/public/dcsctp_socket.h"
namespace dcsctp {
// Defers callbacks until they can be safely triggered.
//
// There are a lot of callbacks from the dcSCTP library to the client,
// such as when messages are received or streams are closed. When the client
// receives these callbacks, the client is expected to be able to call into the
// library - from within the callback. For example, sending a reply message when
// a certain SCTP message has been received, or to reconnect when the connection
// was closed for any reason. This means that the dcSCTP library must always be
// in a consistent and stable state when these callbacks are delivered, and to
// ensure that's the case, callbacks are not immediately delivered from where
// they originate, but instead queued (deferred) by this class. At the end of
// any public API method that may result in callbacks, they are triggered and
// then delivered.
//
// There are a number of exceptions, which is clearly annotated in the API.
class CallbackDeferrer : public DcSctpSocketCallbacks {
public:
class ScopedDeferrer {
public:
explicit ScopedDeferrer(CallbackDeferrer& callback_deferrer)
: callback_deferrer_(callback_deferrer) {
callback_deferrer_.Prepare();
}
~ScopedDeferrer() { callback_deferrer_.TriggerDeferred(); }
private:
CallbackDeferrer& callback_deferrer_;
};
explicit CallbackDeferrer(DcSctpSocketCallbacks& underlying)
: underlying_(underlying) {}
// Implementation of DcSctpSocketCallbacks
SendPacketStatus SendPacketWithStatus(
rtc::ArrayView<const uint8_t> data) override;
std::unique_ptr<Timeout> CreateTimeout(
webrtc::TaskQueueBase::DelayPrecision precision) override;
TimeMs TimeMillis() override;
webrtc::Timestamp Now() override { return underlying_.Now(); }
uint32_t GetRandomInt(uint32_t low, uint32_t high) override;
void OnMessageReceived(DcSctpMessage message) override;
void OnError(ErrorKind error, absl::string_view message) override;
void OnAborted(ErrorKind error, absl::string_view message) override;
void OnConnected() override;
void OnClosed() override;
void OnConnectionRestarted() override;
void OnStreamsResetFailed(rtc::ArrayView<const StreamID> outgoing_streams,
absl::string_view reason) override;
void OnStreamsResetPerformed(
rtc::ArrayView<const StreamID> outgoing_streams) override;
void OnIncomingStreamsReset(
rtc::ArrayView<const StreamID> incoming_streams) override;
void OnBufferedAmountLow(StreamID stream_id) override;
void OnTotalBufferedAmountLow() override;
void OnLifecycleMessageExpired(LifecycleId lifecycle_id,
bool maybe_delivered) override;
void OnLifecycleMessageFullySent(LifecycleId lifecycle_id) override;
void OnLifecycleMessageDelivered(LifecycleId lifecycle_id) override;
void OnLifecycleEnd(LifecycleId lifecycle_id) override;
private:
struct Error {
ErrorKind error;
std::string message;
};
struct StreamReset {
std::vector<StreamID> streams;
std::string message;
};
// Use a pre-sized variant for storage to avoid double heap allocation. This
// variant can hold all cases of stored data.
using CallbackData = absl::
variant<absl::monostate, DcSctpMessage, Error, StreamReset, StreamID>;
using Callback = void (*)(CallbackData, DcSctpSocketCallbacks&);
void Prepare();
void TriggerDeferred();
DcSctpSocketCallbacks& underlying_;
bool prepared_ = false;
std::vector<std::pair<Callback, CallbackData>> deferred_;
};
} // namespace dcsctp
#endif // NET_DCSCTP_SOCKET_CALLBACK_DEFERRER_H_