#define MS_CLASS "DepUsrSCTP"
#include "DepUsrSCTP.hpp"
#include "DepLibUV.hpp"
#include "Logger.hpp"
#include <usrsctp.h>
#include <mutex>
static constexpr size_t CheckerInterval{ 10u }; static std::mutex GlobalSyncMutex;
static size_t GlobalInstances{ 0u };
inline static int onSendSctpData(void* addr, void* data, size_t len, uint8_t , uint8_t )
{
auto* sctpAssociation = DepUsrSCTP::RetrieveSctpAssociation(reinterpret_cast<uintptr_t>(addr));
if (!sctpAssociation)
{
MS_WARN_TAG(sctp, "no SctpAssociation found");
return -1;
}
sctpAssociation->OnUsrSctpSendSctpData(data, len);
return 0;
}
inline static void sctpDebug(const char* format, ...)
{
char buffer[10000];
va_list ap;
va_start(ap, format);
vsprintf(buffer, format, ap);
buffer[std::strlen(buffer) - 1] = '\0';
MS_DEBUG_TAG(sctp, "%s", buffer);
va_end(ap);
}
thread_local DepUsrSCTP::Checker* DepUsrSCTP::checker{ nullptr };
uint64_t DepUsrSCTP::numSctpAssociations{ 0u };
uintptr_t DepUsrSCTP::nextSctpAssociationId{ 0u };
std::unordered_map<uintptr_t, RTC::SctpAssociation*> DepUsrSCTP::mapIdSctpAssociation;
void DepUsrSCTP::ClassInit()
{
MS_TRACE();
MS_DEBUG_TAG(info, "usrsctp");
std::lock_guard<std::mutex> lock(GlobalSyncMutex);
if (GlobalInstances == 0)
{
usrsctp_init_nothreads(0, onSendSctpData, sctpDebug);
usrsctp_sysctl_set_sctp_ecn_enable(0);
#ifdef SCTP_DEBUG
usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
#endif
}
++GlobalInstances;
}
void DepUsrSCTP::ClassDestroy()
{
MS_TRACE();
std::lock_guard<std::mutex> lock(GlobalSyncMutex);
--GlobalInstances;
if (GlobalInstances == 0)
{
usrsctp_finish();
numSctpAssociations = 0u;
nextSctpAssociationId = 0u;
DepUsrSCTP::mapIdSctpAssociation.clear();
}
}
void DepUsrSCTP::CreateChecker()
{
MS_TRACE();
MS_ASSERT(DepUsrSCTP::checker == nullptr, "Checker already created");
DepUsrSCTP::checker = new DepUsrSCTP::Checker();
}
void DepUsrSCTP::CloseChecker()
{
MS_TRACE();
MS_ASSERT(DepUsrSCTP::checker != nullptr, "Checker not created");
delete DepUsrSCTP::checker;
}
uintptr_t DepUsrSCTP::GetNextSctpAssociationId()
{
MS_TRACE();
std::lock_guard<std::mutex> lock(GlobalSyncMutex);
if (DepUsrSCTP::nextSctpAssociationId == 0u)
++DepUsrSCTP::nextSctpAssociationId;
while (DepUsrSCTP::mapIdSctpAssociation.find(DepUsrSCTP::nextSctpAssociationId) !=
DepUsrSCTP::mapIdSctpAssociation.end())
{
++DepUsrSCTP::nextSctpAssociationId;
if (DepUsrSCTP::nextSctpAssociationId == 0u)
++DepUsrSCTP::nextSctpAssociationId;
}
return DepUsrSCTP::nextSctpAssociationId++;
}
void DepUsrSCTP::RegisterSctpAssociation(RTC::SctpAssociation* sctpAssociation)
{
MS_TRACE();
std::lock_guard<std::mutex> lock(GlobalSyncMutex);
MS_ASSERT(DepUsrSCTP::checker != nullptr, "Checker not created");
auto it = DepUsrSCTP::mapIdSctpAssociation.find(sctpAssociation->id);
MS_ASSERT(
it == DepUsrSCTP::mapIdSctpAssociation.end(),
"the id of the SctpAssociation is already in the map");
DepUsrSCTP::mapIdSctpAssociation[sctpAssociation->id] = sctpAssociation;
if (++DepUsrSCTP::numSctpAssociations == 1u)
DepUsrSCTP::checker->Start();
}
void DepUsrSCTP::DeregisterSctpAssociation(RTC::SctpAssociation* sctpAssociation)
{
MS_TRACE();
std::lock_guard<std::mutex> lock(GlobalSyncMutex);
MS_ASSERT(DepUsrSCTP::checker != nullptr, "Checker not created");
auto found = DepUsrSCTP::mapIdSctpAssociation.erase(sctpAssociation->id);
MS_ASSERT(found > 0, "SctpAssociation not found");
MS_ASSERT(DepUsrSCTP::numSctpAssociations > 0u, "numSctpAssociations was not higher than 0");
if (--DepUsrSCTP::numSctpAssociations == 0u)
DepUsrSCTP::checker->Stop();
}
RTC::SctpAssociation* DepUsrSCTP::RetrieveSctpAssociation(uintptr_t id)
{
MS_TRACE();
std::lock_guard<std::mutex> lock(GlobalSyncMutex);
auto it = DepUsrSCTP::mapIdSctpAssociation.find(id);
if (it == DepUsrSCTP::mapIdSctpAssociation.end())
return nullptr;
return it->second;
}
DepUsrSCTP::Checker::Checker()
{
MS_TRACE();
this->timer = new Timer(this);
}
DepUsrSCTP::Checker::~Checker()
{
MS_TRACE();
delete this->timer;
}
void DepUsrSCTP::Checker::Start()
{
MS_TRACE();
MS_DEBUG_TAG(sctp, "usrsctp periodic check started");
this->lastCalledAtMs = 0u;
this->timer->Start(CheckerInterval, CheckerInterval);
}
void DepUsrSCTP::Checker::Stop()
{
MS_TRACE();
MS_DEBUG_TAG(sctp, "usrsctp periodic check stopped");
this->lastCalledAtMs = 0u;
this->timer->Stop();
}
void DepUsrSCTP::Checker::OnTimer(Timer* )
{
MS_TRACE();
auto nowMs = DepLibUV::GetTimeMs();
int elapsedMs = this->lastCalledAtMs ? static_cast<int>(nowMs - this->lastCalledAtMs) : 0;
usrsctp_handle_timers(elapsedMs);
this->lastCalledAtMs = nowMs;
}