| /* |
| * 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_ |