Browse Source

Properly catch exceptions in C API

Paul-Louis Ageneau 5 years ago
parent
commit
d0695aa9cb
5 changed files with 308 additions and 307 deletions
  1. 6 1
      include/rtc/rtc.h
  2. 1 1
      src/certificate.cpp
  3. 3 0
      src/peerconnection.cpp
  4. 295 301
      src/rtc.cpp
  5. 3 4
      test/benchmark.cpp

+ 6 - 1
include/rtc/rtc.h

@@ -60,6 +60,10 @@ typedef enum { // Don't change, it must match plog severity
 	RTC_LOG_VERBOSE = 6
 } rtcLogLevel;
 
+const int RTC_ERR_SUCCESS = 0;
+const int RTC_ERR_INVALID = -1; // invalid argument
+const int RTC_ERR_FAILURE = -2; // runtime error
+
 typedef struct {
 	const char **iceServers;
 	int iceServersCount;
@@ -129,7 +133,8 @@ int rtcGetAvailableAmount(int id); // total size available to receive
 int rtcSetAvailableCallback(int id, availableCallbackFunc cb);
 int rtcReceiveMessage(int id, char *buffer, int *size);
 
-// Cleanup
+// Optional preload and cleanup
+void rtcPreload();
 void rtcCleanup();
 
 #ifdef __cplusplus

+ 1 - 1
src/certificate.cpp

@@ -235,7 +235,7 @@ namespace {
 template <class F, class... Args>
 std::future<std::result_of_t<std::decay_t<F>(std::decay_t<Args>...)>> thread_call(F &&f,
                                                                                   Args &&... args) {
-	using R = std::result_of_t<std::decay_t<F>(std::decay_t<Args>...)>;
+	using R = std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>;
 	std::packaged_task<R()> task(std::bind(f, std::forward<Args>(args)...));
 	std::future<R> future = task.get_future();
 	std::thread t(std::move(task));

+ 3 - 0
src/peerconnection.cpp

@@ -42,6 +42,9 @@ PeerConnection::PeerConnection(const Configuration &config)
     : mConfig(config), mCertificate(make_certificate()), mState(State::New),
       mGatheringState(GatheringState::New) {
 	PLOG_VERBOSE << "Creating PeerConnection";
+
+	if (config.portRangeEnd && config.portRangeBegin > config.portRangeEnd)
+		throw std::invalid_argument("Invalid port range");
 }
 
 PeerConnection::~PeerConnection() {

+ 295 - 301
src/rtc.cpp

@@ -29,6 +29,7 @@
 
 #include <exception>
 #include <mutex>
+#include <type_traits>
 #include <unordered_map>
 #include <utility>
 
@@ -36,14 +37,6 @@ using namespace rtc;
 using std::shared_ptr;
 using std::string;
 
-#define CATCH(statement)                                                                           \
-	try {                                                                                          \
-		statement;                                                                                 \
-	} catch (const std::exception &e) {                                                            \
-		PLOG_ERROR << e.what();                                                                    \
-		return -1;                                                                                 \
-	}
-
 namespace {
 
 std::unordered_map<int, shared_ptr<PeerConnection>> peerConnectionMap;
@@ -71,14 +64,18 @@ void setUserPointer(int i, void *ptr) {
 
 shared_ptr<PeerConnection> getPeerConnection(int id) {
 	std::lock_guard lock(mutex);
-	auto it = peerConnectionMap.find(id);
-	return it != peerConnectionMap.end() ? it->second : nullptr;
+	if (auto it = peerConnectionMap.find(id); it != peerConnectionMap.end())
+		return it->second;
+	else
+		throw std::invalid_argument("PeerConnection ID does not exist");
 }
 
 shared_ptr<DataChannel> getDataChannel(int id) {
 	std::lock_guard lock(mutex);
-	auto it = dataChannelMap.find(id);
-	return it != dataChannelMap.end() ? it->second : nullptr;
+	if (auto it = dataChannelMap.find(id); it != dataChannelMap.end())
+		return it->second;
+	else
+		throw std::invalid_argument("DataChannel ID does not exist");
 }
 
 int emplacePeerConnection(shared_ptr<PeerConnection> ptr) {
@@ -95,27 +92,27 @@ int emplaceDataChannel(shared_ptr<DataChannel> ptr) {
 	return dc;
 }
 
-bool erasePeerConnection(int pc) {
+void erasePeerConnection(int pc) {
 	std::lock_guard lock(mutex);
 	if (peerConnectionMap.erase(pc) == 0)
-		return false;
+		throw std::invalid_argument("PeerConnection ID does not exist");
 	userPointerMap.erase(pc);
-	return true;
 }
 
-bool eraseDataChannel(int dc) {
+void eraseDataChannel(int dc) {
 	std::lock_guard lock(mutex);
 	if (dataChannelMap.erase(dc) == 0)
-		return false;
+		throw std::invalid_argument("DataChannel ID does not exist");
 	userPointerMap.erase(dc);
-	return true;
 }
 
 #if RTC_ENABLE_WEBSOCKET
 shared_ptr<WebSocket> getWebSocket(int id) {
 	std::lock_guard lock(mutex);
-	auto it = webSocketMap.find(id);
-	return it != webSocketMap.end() ? it->second : nullptr;
+	if (auto it = webSocketMap.find(id); it != webSocketMap.end())
+		return it->second;
+	else
+		throw std::invalid_argument("WebSocket ID does not exist");
 }
 
 int emplaceWebSocket(shared_ptr<WebSocket> ptr) {
@@ -125,12 +122,11 @@ int emplaceWebSocket(shared_ptr<WebSocket> ptr) {
 	return ws;
 }
 
-bool eraseWebSocket(int ws) {
+void eraseWebSocket(int ws) {
 	std::lock_guard lock(mutex);
 	if (webSocketMap.erase(ws) == 0)
-		return false;
+		throw std::invalid_argument("WebSocket ID does not exist");
 	userPointerMap.erase(ws);
-	return true;
 }
 #endif
 
@@ -142,9 +138,28 @@ shared_ptr<Channel> getChannel(int id) {
 	if (auto it = webSocketMap.find(id); it != webSocketMap.end())
 		return it->second;
 #endif
-	return nullptr;
+	throw std::invalid_argument("DataChannel or WebSocket ID does not exist");
 }
 
+template <typename F> int wrap(F func) {
+	try {
+		return func();
+
+	} catch (const std::invalid_argument &e) {
+		PLOG_ERROR << e.what();
+		return RTC_ERR_INVALID;
+	} catch (const std::exception &e) {
+		PLOG_ERROR << e.what();
+		return RTC_ERR_FAILURE;
+	}
+}
+
+#define WRAP(statement)                                                                            \
+	wrap([&]() {                                                                                   \
+		statement;                                                                                 \
+		return RTC_ERR_SUCCESS;                                                                    \
+	})
+
 } // namespace
 
 void rtcInitLogger(rtcLogLevel level) { InitLogger(static_cast<LogLevel>(level)); }
@@ -152,370 +167,349 @@ void rtcInitLogger(rtcLogLevel level) { InitLogger(static_cast<LogLevel>(level))
 void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
 
 int rtcCreatePeerConnection(const rtcConfiguration *config) {
-	Configuration c;
-	for (int i = 0; i < config->iceServersCount; ++i)
-		c.iceServers.emplace_back(string(config->iceServers[i]));
+	return WRAP({
+		Configuration c;
+		for (int i = 0; i < config->iceServersCount; ++i)
+			c.iceServers.emplace_back(string(config->iceServers[i]));
 
-	if (config->portRangeBegin || config->portRangeEnd) {
-		c.portRangeBegin = config->portRangeBegin;
-		c.portRangeEnd = config->portRangeEnd;
-	}
+		if (config->portRangeBegin || config->portRangeEnd) {
+			c.portRangeBegin = config->portRangeBegin;
+			c.portRangeEnd = config->portRangeEnd;
+		}
 
-	return emplacePeerConnection(std::make_shared<PeerConnection>(c));
+		return emplacePeerConnection(std::make_shared<PeerConnection>(c));
+	});
 }
 
 int rtcDeletePeerConnection(int pc) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
-
-	peerConnection->onDataChannel(nullptr);
-	peerConnection->onLocalDescription(nullptr);
-	peerConnection->onLocalCandidate(nullptr);
-	peerConnection->onStateChange(nullptr);
-	peerConnection->onGatheringStateChange(nullptr);
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+		peerConnection->onDataChannel(nullptr);
+		peerConnection->onLocalDescription(nullptr);
+		peerConnection->onLocalCandidate(nullptr);
+		peerConnection->onStateChange(nullptr);
+		peerConnection->onGatheringStateChange(nullptr);
 
-	erasePeerConnection(pc);
-	return 0;
+		erasePeerConnection(pc);
+	});
 }
 
 int rtcCreateDataChannel(int pc, const char *label) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
-
-	int dc = emplaceDataChannel(peerConnection->createDataChannel(string(label)));
-	void *ptr = getUserPointer(pc);
-	rtcSetUserPointer(dc, ptr);
-	return dc;
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+		int dc = emplaceDataChannel(peerConnection->createDataChannel(string(label)));
+		rtcSetUserPointer(dc, getUserPointer(pc));
+		return dc;
+	});
 }
 
 int rtcDeleteDataChannel(int dc) {
-	auto dataChannel = getDataChannel(dc);
-	if (!dataChannel)
-		return -1;
-
-	dataChannel->onOpen(nullptr);
-	dataChannel->onClosed(nullptr);
-	dataChannel->onError(nullptr);
-	dataChannel->onMessage(nullptr);
-	dataChannel->onBufferedAmountLow(nullptr);
-	dataChannel->onAvailable(nullptr);
-
-	eraseDataChannel(dc);
-	return 0;
+	return WRAP({
+		auto dataChannel = getDataChannel(dc);
+		dataChannel->onOpen(nullptr);
+		dataChannel->onClosed(nullptr);
+		dataChannel->onError(nullptr);
+		dataChannel->onMessage(nullptr);
+		dataChannel->onBufferedAmountLow(nullptr);
+		dataChannel->onAvailable(nullptr);
+
+		eraseDataChannel(dc);
+	});
 }
 
 #if RTC_ENABLE_WEBSOCKET
 int rtcCreateWebSocket(const char *url) {
-	auto ws = std::make_shared<WebSocket>();
-	ws->open(url);
-	return emplaceWebSocket(ws);
+	return WRAP({
+		auto ws = std::make_shared<WebSocket>();
+		ws->open(url);
+		return emplaceWebSocket(ws);
+	});
 }
 
 int rtcDeleteWebsocket(int ws) {
-	auto webSocket = getWebSocket(ws);
-	if (!webSocket)
-		return -1;
-
-	webSocket->onOpen(nullptr);
-	webSocket->onClosed(nullptr);
-	webSocket->onError(nullptr);
-	webSocket->onMessage(nullptr);
-	webSocket->onBufferedAmountLow(nullptr);
-	webSocket->onAvailable(nullptr);
-
-	eraseWebSocket(ws);
-	return 0;
+	return WRAP({
+		auto webSocket = getWebSocket(ws);
+		webSocket->onOpen(nullptr);
+		webSocket->onClosed(nullptr);
+		webSocket->onError(nullptr);
+		webSocket->onMessage(nullptr);
+		webSocket->onBufferedAmountLow(nullptr);
+		webSocket->onAvailable(nullptr);
+
+		eraseWebSocket(ws);
+	});
 }
-
 #endif
 
 int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
-
-	if (cb)
-		peerConnection->onDataChannel([pc, cb](std::shared_ptr<DataChannel> dataChannel) {
-			int dc = emplaceDataChannel(dataChannel);
-			void *ptr = getUserPointer(pc);
-			rtcSetUserPointer(dc, ptr);
-			cb(dc, ptr);
-		});
-	else
-		peerConnection->onDataChannel(nullptr);
-	return 0;
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+		if (cb)
+			peerConnection->onDataChannel([pc, cb](std::shared_ptr<DataChannel> dataChannel) {
+				int dc = emplaceDataChannel(dataChannel);
+				void *ptr = getUserPointer(pc);
+				rtcSetUserPointer(dc, ptr);
+				cb(dc, ptr);
+			});
+		else
+			peerConnection->onDataChannel(nullptr);
+	});
 }
 
 int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
-
-	if (cb)
-		peerConnection->onLocalDescription([pc, cb](const Description &desc) {
-			cb(string(desc).c_str(), desc.typeString().c_str(), getUserPointer(pc));
-		});
-	else
-		peerConnection->onLocalDescription(nullptr);
-	return 0;
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+		if (cb)
+			peerConnection->onLocalDescription([pc, cb](const Description &desc) {
+				cb(string(desc).c_str(), desc.typeString().c_str(), getUserPointer(pc));
+			});
+		else
+			peerConnection->onLocalDescription(nullptr);
+	});
 }
 
 int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
-
-	if (cb)
-		peerConnection->onLocalCandidate([pc, cb](const Candidate &cand) {
-			cb(cand.candidate().c_str(), cand.mid().c_str(), getUserPointer(pc));
-		});
-	else
-		peerConnection->onLocalCandidate(nullptr);
-	return 0;
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+		if (cb)
+			peerConnection->onLocalCandidate([pc, cb](const Candidate &cand) {
+				cb(cand.candidate().c_str(), cand.mid().c_str(), getUserPointer(pc));
+			});
+		else
+			peerConnection->onLocalCandidate(nullptr);
+	});
 }
 
 int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
-
-	if (cb)
-		peerConnection->onStateChange([pc, cb](PeerConnection::State state) {
-			cb(static_cast<rtcState>(state), getUserPointer(pc));
-		});
-	else
-		peerConnection->onStateChange(nullptr);
-	return 0;
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+		if (cb)
+			peerConnection->onStateChange([pc, cb](PeerConnection::State state) {
+				cb(static_cast<rtcState>(state), getUserPointer(pc));
+			});
+		else
+			peerConnection->onStateChange(nullptr);
+	});
 }
 
 int rtcSetGatheringStateChangeCallback(int pc, gatheringStateCallbackFunc cb) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
-
-	if (cb)
-		peerConnection->onGatheringStateChange([pc, cb](PeerConnection::GatheringState state) {
-			cb(static_cast<rtcGatheringState>(state), getUserPointer(pc));
-		});
-	else
-		peerConnection->onGatheringStateChange(nullptr);
-	return 0;
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+		if (cb)
+			peerConnection->onGatheringStateChange([pc, cb](PeerConnection::GatheringState state) {
+				cb(static_cast<rtcGatheringState>(state), getUserPointer(pc));
+			});
+		else
+			peerConnection->onGatheringStateChange(nullptr);
+	});
 }
 
 int rtcSetRemoteDescription(int pc, const char *sdp, const char *type) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
 
-	CATCH(peerConnection->setRemoteDescription({string(sdp), type ? string(type) : ""}));
-	return 0;
+		if (!sdp)
+			throw std::invalid_argument("Unexpected null pointer");
+
+		peerConnection->setRemoteDescription({string(sdp), type ? string(type) : ""});
+	});
 }
 
 int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+
+		if (!cand)
+			throw std::invalid_argument("Unexpected null pointer");
 
-	CATCH(peerConnection->addRemoteCandidate({string(cand), mid ? string(mid) : ""}))
-	return 0;
+		peerConnection->addRemoteCandidate({string(cand), mid ? string(mid) : ""});
+	});
 }
 
 int rtcGetLocalAddress(int pc, char *buffer, int size) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
-
-	if (auto addr = peerConnection->localAddress()) {
-		size = std::min(size_t(size - 1), addr->size());
-		std::copy(addr->data(), addr->data() + size, buffer);
-		buffer[size] = '\0';
-		return size + 1;
-	}
-	return -1;
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+
+		if (!buffer)
+			throw std::invalid_argument("Unexpected null pointer");
+
+		if (auto addr = peerConnection->localAddress()) {
+			size = std::min(size_t(size - 1), addr->size());
+			std::copy(addr->data(), addr->data() + size, buffer);
+			buffer[size] = '\0';
+			return size + 1;
+		}
+	});
 }
 
 int rtcGetRemoteAddress(int pc, char *buffer, int size) {
-	auto peerConnection = getPeerConnection(pc);
-	if (!peerConnection)
-		return -1;
-
-	if (auto addr = peerConnection->remoteAddress()) {
-		size = std::min(size_t(size - 1), addr->size());
-		std::copy(addr->data(), addr->data() + size, buffer);
-		buffer[size] = '\0';
-		return size + 1;
-	}
-	return -1;
+	return WRAP({
+		auto peerConnection = getPeerConnection(pc);
+
+		if (!buffer)
+			throw std::invalid_argument("Unexpected null pointer");
+
+		if (auto addr = peerConnection->remoteAddress()) {
+			size = std::min(size_t(size - 1), addr->size());
+			std::copy(addr->data(), addr->data() + size, buffer);
+			buffer[size] = '\0';
+			return size + 1;
+		}
+	});
 }
 
 int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
-	auto dataChannel = getDataChannel(dc);
-	if (!dataChannel)
-		return -1;
-
-	if (!size)
-		return 0;
-
-	string label = dataChannel->label();
-	size = std::min(size_t(size - 1), label.size());
-	std::copy(label.data(), label.data() + size, buffer);
-	buffer[size] = '\0';
-	return size + 1;
+	return WRAP({
+		auto dataChannel = getDataChannel(dc);
+
+		if (!buffer)
+			throw std::invalid_argument("Unexpected null pointer");
+
+		if (size >= 0) {
+			string label = dataChannel->label();
+			size = std::min(size_t(size - 1), label.size());
+			std::copy(label.data(), label.data() + size, buffer);
+			buffer[size] = '\0';
+			return size + 1;
+		} else {
+			return 0;
+		}
+	});
 }
 
 int rtcSetOpenCallback(int id, openCallbackFunc cb) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	if (cb)
-		channel->onOpen([id, cb]() { cb(getUserPointer(id)); });
-	else
-		channel->onOpen(nullptr);
-	return 0;
+	return WRAP({
+		auto channel = getChannel(id);
+		if (cb)
+			channel->onOpen([id, cb]() { cb(getUserPointer(id)); });
+		else
+			channel->onOpen(nullptr);
+	});
 }
 
 int rtcSetClosedCallback(int id, closedCallbackFunc cb) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	if (cb)
-		channel->onClosed([id, cb]() { cb(getUserPointer(id)); });
-	else
-		channel->onClosed(nullptr);
-	return 0;
+	return WRAP({
+		auto channel = getChannel(id);
+		if (cb)
+			channel->onClosed([id, cb]() { cb(getUserPointer(id)); });
+		else
+			channel->onClosed(nullptr);
+	});
 }
 
 int rtcSetErrorCallback(int id, errorCallbackFunc cb) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	if (cb)
-		channel->onError([id, cb](const string &error) { cb(error.c_str(), getUserPointer(id)); });
-	else
-		channel->onError(nullptr);
-	return 0;
+	return WRAP({
+		auto channel = getChannel(id);
+		if (cb)
+			channel->onError(
+			    [id, cb](const string &error) { cb(error.c_str(), getUserPointer(id)); });
+		else
+			channel->onError(nullptr);
+	});
 }
 
 int rtcSetMessageCallback(int id, messageCallbackFunc cb) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	if (cb)
-		channel->onMessage(
-		    [id, cb](const binary &b) {
-			    cb(reinterpret_cast<const char *>(b.data()), b.size(), getUserPointer(id));
-		    },
-		    [id, cb](const string &s) { cb(s.c_str(), -1, getUserPointer(id)); });
-	else
-		channel->onMessage(nullptr);
-
-	return 0;
+	return WRAP({
+		auto channel = getChannel(id);
+		if (cb)
+			channel->onMessage(
+			    [id, cb](const binary &b) {
+				    cb(reinterpret_cast<const char *>(b.data()), b.size(), getUserPointer(id));
+			    },
+			    [id, cb](const string &s) { cb(s.c_str(), -1, getUserPointer(id)); });
+		else
+			channel->onMessage(nullptr);
+	});
 }
 
 int rtcSendMessage(int id, const char *data, int size) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	if (size >= 0) {
-		auto b = reinterpret_cast<const byte *>(data);
-		CATCH(channel->send(binary(b, b + size)));
-		return size;
-	} else {
-		string str(data);
-		int len = str.size();
-		CATCH(channel->send(std::move(str)));
-		return len;
-	}
+	return WRAP({
+		auto channel = getChannel(id);
+
+		if (!data)
+			throw std::invalid_argument("Unexpected null pointer");
+
+		if (size >= 0) {
+			auto b = reinterpret_cast<const byte *>(data);
+			channel->send(binary(b, b + size));
+			return size;
+		} else {
+			string str(data);
+			int len = str.size();
+			channel->send(std::move(str));
+			return len;
+		}
+	});
 }
 
 int rtcGetBufferedAmount(int id) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	CATCH(return int(channel->bufferedAmount()));
+	return WRAP({
+		auto channel = getChannel(id);
+		return int(channel->bufferedAmount());
+	});
 }
 
 int rtcSetBufferedAmountLowThreshold(int id, int amount) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	CATCH(channel->setBufferedAmountLowThreshold(size_t(amount)));
-	return 0;
+	return WRAP({
+		auto channel = getChannel(id);
+		channel->setBufferedAmountLowThreshold(size_t(amount));
+	});
 }
 
 int rtcSetBufferedAmountLowCallback(int id, bufferedAmountLowCallbackFunc cb) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	if (cb)
-		channel->onBufferedAmountLow([id, cb]() { cb(getUserPointer(id)); });
-	else
-		channel->onBufferedAmountLow(nullptr);
-	return 0;
+	return WRAP({
+		auto channel = getChannel(id);
+		if (cb)
+			channel->onBufferedAmountLow([id, cb]() { cb(getUserPointer(id)); });
+		else
+			channel->onBufferedAmountLow(nullptr);
+	});
 }
 
 int rtcGetAvailableAmount(int id) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	CATCH(return int(channel->availableAmount()));
+	return WRAP({ return int(getChannel(id)->availableAmount()); });
 }
 
 int rtcSetAvailableCallback(int id, availableCallbackFunc cb) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	if (cb)
-		channel->onOpen([id, cb]() { cb(getUserPointer(id)); });
-	else
-		channel->onOpen(nullptr);
-	return 0;
+	return WRAP({
+		auto channel = getChannel(id);
+		if (cb)
+			channel->onOpen([id, cb]() { cb(getUserPointer(id)); });
+		else
+			channel->onOpen(nullptr);
+	});
 }
 
 int rtcReceiveMessage(int id, char *buffer, int *size) {
-	auto channel = getChannel(id);
-	if (!channel)
-		return -1;
-
-	if (!size)
-		return -1;
-
-	CATCH({
-		auto message = channel->receive();
-		if (!message)
+	return WRAP({
+		auto channel = getChannel(id);
+
+		if (!buffer || !size)
+			throw std::invalid_argument("Unexpected null pointer");
+
+		if (auto message = channel->receive())
+			return std::visit( //
+			    overloaded{    //
+			               [&](const binary &b) {
+				               *size = std::min(*size, int(b.size()));
+				               auto data = reinterpret_cast<const char *>(b.data());
+				               std::copy(data, data + *size, buffer);
+				               return 1;
+			               },
+			               [&](const string &s) {
+				               int len = std::min(*size - 1, int(s.size()));
+				               if (len >= 0) {
+					               std::copy(s.data(), s.data() + len, buffer);
+					               buffer[len] = '\0';
+				               }
+				               *size = -(len + 1);
+				               return 1;
+			               }},
+			    *message);
+		else
 			return 0;
-
-		return std::visit( //
-		    overloaded{    //
-		               [&](const binary &b) {
-			               *size = std::min(*size, int(b.size()));
-			               auto data = reinterpret_cast<const char *>(b.data());
-			               std::copy(data, data + *size, buffer);
-			               return *size;
-		               },
-		               [&](const string &s) {
-			               int len = std::min(*size - 1, int(s.size()));
-			               if (len >= 0) {
-				               std::copy(s.data(), s.data() + len, buffer);
-				               buffer[len] = '\0';
-			               }
-			               *size = -(len + 1);
-			               return len + 1;
-		               }},
-		    *message);
 	});
 }
 
+void rtcPreload() { rtc::Preload(); }
 void rtcCleanup() { rtc::Cleanup(); }

+ 3 - 4
test/benchmark.cpp

@@ -35,7 +35,8 @@ using chrono::steady_clock;
 template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
 
 size_t benchmark(milliseconds duration) {
-	InitLogger(LogLevel::Warning);
+	rtc::InitLogger(LogLevel::Warning);
+	rtc::Preload();
 
 	Configuration config1;
 	// config1.iceServers.emplace_back("stun:stun.l.google.com:19302");
@@ -177,19 +178,17 @@ size_t benchmark(milliseconds duration) {
 	pc2->close();
 	this_thread::sleep_for(1s);
 
+	rtc::Cleanup();
 	return goodput;
 }
 
 #ifdef BENCHMARK_MAIN
 int main(int argc, char **argv) {
 	try {
-		rtc::Preload();
-
 		size_t goodput = benchmark(30s);
 		if (goodput == 0)
 			throw runtime_error("No data received");
 
-		rtc::Cleanup();
 		return 0;
 
 	} catch (const std::exception &e) {