Skip to content

Commit 4c62bfe

Browse files
Add error types
1 parent e560df9 commit 4c62bfe

6 files changed

Lines changed: 98 additions & 49 deletions

File tree

‎include/livekit/local_participant.h‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,20 @@
2222
#include <functional>
2323
#include <memory>
2424
#include <mutex>
25+
#include <optional>
2526
#include <string>
2627
#include <unordered_map>
2728
#include <vector>
2829

30+
#include "livekit/data_track_error.h"
2931
#include "livekit/data_track_options.h"
3032
#include "livekit/data_track_schema.h"
3133
#include "livekit/ffi_handle.h"
3234
#include "livekit/local_audio_track.h"
3335
#include "livekit/local_data_track.h"
3436
#include "livekit/local_video_track.h"
3537
#include "livekit/participant.h"
38+
#include "livekit/result.h"
3639
#include "livekit/room_event_types.h"
3740
#include "livekit/rpc_error.h"
3841
#include "livekit/visibility.h"
@@ -209,9 +212,9 @@ class LIVEKIT_API LocalParticipant : public Participant {
209212
/// parsed nor validated against its encoding, so the
210213
/// caller is responsible for ensuring it is well-formed.
211214
///
212-
/// @throws std::runtime_error If the schema could not be defined or the FFI
213-
/// call fails.
214-
void defineSchema(const DataTrackSchemaId& id, const std::string& definition);
215+
/// @return @c true if the schema was defined; otherwise @c false. Failure
216+
/// details are written to the SDK log.
217+
[[nodiscard]] bool defineSchema(const DataTrackSchemaId& id, const std::string& definition);
215218

216219
/// Retrieve the definition for a data track schema.
217220
///
@@ -221,11 +224,10 @@ class LIVEKIT_API LocalParticipant : public Participant {
221224
/// @param id Identifies the schema to retrieve.
222225
/// @param participant_identity Identity of the participant that defined the
223226
/// schema.
224-
/// @return The schema definition.
225-
///
226-
/// @throws std::runtime_error If the participant has not defined a schema
227-
/// with this ID or the FFI call fails.
228-
std::string getSchema(const DataTrackSchemaId& id, const std::string& participant_identity);
227+
/// @return The schema definition on success, or @c std::nullopt on failure.
228+
/// Failure details are written to the SDK log.
229+
[[nodiscard]] std::optional<std::string> getSchema(const DataTrackSchemaId& id,
230+
const std::string& participant_identity);
229231

230232
/// Initiate an RPC call to a remote participant.
231233
///

‎src/ffi_client.cpp‎

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ inline void logAndThrow(const std::string& error_msg) {
4444
throw std::runtime_error(error_msg);
4545
}
4646

47+
template <typename T>
48+
std::future<T> makeReadyFuture(T value) {
49+
std::promise<T> promise;
50+
promise.set_value(std::move(value));
51+
return promise.get_future();
52+
}
53+
4754
// Helper for debug logging of optional values
4855
const auto optional_to_string = [](const auto& value) -> std::string {
4956
if (!value) {
@@ -1185,24 +1192,25 @@ std::future<void> FfiClient::sendStreamTrailerAsync(std::uint64_t local_particip
11851192
return fut;
11861193
}
11871194

1188-
std::future<void> FfiClient::defineSchemaAsync(std::uint64_t local_participant_handle,
1189-
const DataTrackSchemaId& schema_id, const std::string& definition) {
1195+
std::future<Result<void, std::string>> FfiClient::defineSchemaAsync(std::uint64_t local_participant_handle,
1196+
const DataTrackSchemaId& schema_id,
1197+
const std::string& definition) {
11901198
// Generate client-side async_id first
11911199
const AsyncId async_id = generateAsyncId();
11921200

11931201
// Register the async handler BEFORE sending the request
1194-
auto fut = registerAsync<void>(
1202+
auto fut = registerAsync<Result<void, std::string>>(
11951203
async_id,
11961204
[async_id](const proto::FfiEvent& event) {
11971205
return event.has_define_schema() && event.define_schema().async_id() == async_id;
11981206
},
1199-
[](const proto::FfiEvent& event, std::promise<void>& pr) {
1207+
[](const proto::FfiEvent& event, std::promise<Result<void, std::string>>& pr) {
12001208
const auto& cb = event.define_schema();
12011209
if (cb.has_error() && !cb.error().empty()) {
1202-
pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error())));
1210+
pr.set_value(Result<void, std::string>::failure(cb.error()));
12031211
return;
12041212
}
1205-
pr.set_value();
1213+
pr.set_value(Result<void, std::string>::success());
12061214
});
12071215

12081216
// Build and send the request
@@ -1216,35 +1224,36 @@ std::future<void> FfiClient::defineSchemaAsync(std::uint64_t local_participant_h
12161224
try {
12171225
const proto::FfiResponse resp = sendRequest(req);
12181226
if (!resp.has_define_schema()) {
1219-
logAndThrow("FfiResponse missing define_schema");
1227+
cancelPendingByAsyncId(async_id);
1228+
return makeReadyFuture(Result<void, std::string>::failure("FfiResponse missing define_schema"));
12201229
}
1221-
} catch (...) {
1230+
} catch (const std::exception& e) {
12221231
cancelPendingByAsyncId(async_id);
1223-
throw;
1232+
return makeReadyFuture(Result<void, std::string>::failure(e.what()));
12241233
}
12251234

12261235
return fut;
12271236
}
12281237

1229-
std::future<std::string> FfiClient::getSchemaAsync(std::uint64_t local_participant_handle,
1230-
const DataTrackSchemaId& schema_id,
1231-
const std::string& participant_identity) {
1238+
std::future<Result<std::string, std::string>> FfiClient::getSchemaAsync(std::uint64_t local_participant_handle,
1239+
const DataTrackSchemaId& schema_id,
1240+
const std::string& participant_identity) {
12321241
// Generate client-side async_id first
12331242
const AsyncId async_id = generateAsyncId();
12341243

12351244
// Register the async handler BEFORE sending the request
1236-
auto fut = registerAsync<std::string>(
1245+
auto fut = registerAsync<Result<std::string, std::string>>(
12371246
async_id,
12381247
[async_id](const proto::FfiEvent& event) {
12391248
return event.has_get_schema() && event.get_schema().async_id() == async_id;
12401249
},
1241-
[](const proto::FfiEvent& event, std::promise<std::string>& pr) {
1250+
[](const proto::FfiEvent& event, std::promise<Result<std::string, std::string>>& pr) {
12421251
const auto& cb = event.get_schema();
12431252
if (cb.has_error() && !cb.error().empty()) {
1244-
pr.set_exception(std::make_exception_ptr(std::runtime_error(cb.error())));
1253+
pr.set_value(Result<std::string, std::string>::failure(cb.error()));
12451254
return;
12461255
}
1247-
pr.set_value(cb.definition());
1256+
pr.set_value(Result<std::string, std::string>::success(cb.definition()));
12481257
});
12491258

12501259
// Build and send the request
@@ -1258,11 +1267,12 @@ std::future<std::string> FfiClient::getSchemaAsync(std::uint64_t local_participa
12581267
try {
12591268
const proto::FfiResponse resp = sendRequest(req);
12601269
if (!resp.has_get_schema()) {
1261-
logAndThrow("FfiResponse missing get_schema");
1270+
cancelPendingByAsyncId(async_id);
1271+
return makeReadyFuture(Result<std::string, std::string>::failure("FfiResponse missing get_schema"));
12621272
}
1263-
} catch (...) {
1273+
} catch (const std::exception& e) {
12641274
cancelPendingByAsyncId(async_id);
1265-
throw;
1275+
return makeReadyFuture(Result<std::string, std::string>::failure(e.what()));
12661276
}
12671277

12681278
return fut;

‎src/ffi_client.h‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,12 @@ class LIVEKIT_INTERNAL_API FfiClient {
126126
std::optional<std::uint32_t> response_timeout_ms = std::nullopt);
127127

128128
// Data Track schema APIs
129-
std::future<void> defineSchemaAsync(std::uint64_t local_participant_handle, const DataTrackSchemaId& schema_id,
130-
const std::string& definition);
131-
std::future<std::string> getSchemaAsync(std::uint64_t local_participant_handle, const DataTrackSchemaId& schema_id,
132-
const std::string& participant_identity);
129+
std::future<Result<void, std::string>> defineSchemaAsync(std::uint64_t local_participant_handle,
130+
const DataTrackSchemaId& schema_id,
131+
const std::string& definition);
132+
std::future<Result<std::string, std::string>> getSchemaAsync(std::uint64_t local_participant_handle,
133+
const DataTrackSchemaId& schema_id,
134+
const std::string& participant_identity);
133135

134136
// Data Track APIs
135137
std::future<Result<proto::OwnedLocalDataTrack, PublishDataTrackError>> publishDataTrackAsync(

‎src/local_participant.cpp‎

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "livekit/local_video_track.h"
3030
#include "livekit/room_delegate.h"
3131
#include "livekit/track.h"
32+
#include "lk_log.h"
3233
#include "participant.pb.h"
3334
#include "room.pb.h"
3435
#include "room_proto_converter.h"
@@ -297,24 +298,37 @@ void LocalParticipant::unpublishDataTrack(const std::shared_ptr<LocalDataTrack>&
297298
track->unpublishDataTrack();
298299
}
299300

300-
void LocalParticipant::defineSchema(const DataTrackSchemaId& id, const std::string& definition) {
301+
bool LocalParticipant::defineSchema(const DataTrackSchemaId& id, const std::string& definition) {
301302
auto handle_id = ffiHandleId();
302303
if (handle_id == 0) {
303-
throw std::runtime_error("LocalParticipant::defineSchema: invalid FFI handle");
304+
LK_LOG_ERROR("LocalParticipant::defineSchema failed: invalid FFI handle");
305+
return false;
304306
}
305307

306308
auto fut = FfiClient::instance().defineSchemaAsync(static_cast<std::uint64_t>(handle_id), id, definition);
307-
fut.get();
309+
auto result = fut.get();
310+
if (!result) {
311+
LK_LOG_ERROR("LocalParticipant::defineSchema failed: {}", result.error());
312+
return false;
313+
}
314+
return true;
308315
}
309316

310-
std::string LocalParticipant::getSchema(const DataTrackSchemaId& id, const std::string& participant_identity) {
317+
std::optional<std::string> LocalParticipant::getSchema(const DataTrackSchemaId& id,
318+
const std::string& participant_identity) {
311319
auto handle_id = ffiHandleId();
312320
if (handle_id == 0) {
313-
throw std::runtime_error("LocalParticipant::getSchema: invalid FFI handle");
321+
LK_LOG_ERROR("LocalParticipant::getSchema failed: invalid FFI handle");
322+
return std::nullopt;
314323
}
315324

316325
auto fut = FfiClient::instance().getSchemaAsync(static_cast<std::uint64_t>(handle_id), id, participant_identity);
317-
return fut.get();
326+
auto result = fut.get();
327+
if (!result) {
328+
LK_LOG_ERROR("LocalParticipant::getSchema failed: {}", result.error());
329+
return std::nullopt;
330+
}
331+
return std::move(result).value();
318332
}
319333

320334
std::string LocalParticipant::performRpc(const std::string& destination_identity, const std::string& method,

‎src/tests/integration/test_data_track.cpp‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -601,11 +601,11 @@ TEST_F(DataTrackE2ETest, DefineAndGetSchema) {
601601
const DataTrackSchemaId schema_id{"some_schema", DataTrackSchemaEncoding::JsonSchema};
602602
const std::string definition(kMaxSchemaDefinitionBytes, 'a');
603603

604-
ASSERT_NO_THROW(lockLocalParticipant(*publisher_room)->defineSchema(schema_id, definition));
604+
ASSERT_TRUE(lockLocalParticipant(*publisher_room)->defineSchema(schema_id, definition));
605605

606-
std::string retrieved;
607-
ASSERT_NO_THROW(retrieved = lockLocalParticipant(*subscriber_room)->getSchema(schema_id, publisher_identity));
608-
EXPECT_EQ(retrieved, definition);
606+
const auto retrieved = lockLocalParticipant(*subscriber_room)->getSchema(schema_id, publisher_identity);
607+
ASSERT_TRUE(retrieved);
608+
EXPECT_EQ(*retrieved, definition);
609609
}
610610

611611
TEST_F(DataTrackE2ETest, DefineAndGetCustomSchemaEncoding) {
@@ -617,11 +617,11 @@ TEST_F(DataTrackE2ETest, DefineAndGetCustomSchemaEncoding) {
617617
const DataTrackSchemaId schema_id{"custom_schema", DataTrackSchemaEncoding::custom("custom-schema")};
618618
const std::string definition("custom schema definition");
619619

620-
ASSERT_NO_THROW(lockLocalParticipant(*publisher_room)->defineSchema(schema_id, definition));
620+
ASSERT_TRUE(lockLocalParticipant(*publisher_room)->defineSchema(schema_id, definition));
621621

622-
std::string retrieved;
623-
ASSERT_NO_THROW(retrieved = lockLocalParticipant(*subscriber_room)->getSchema(schema_id, publisher_identity));
624-
EXPECT_EQ(retrieved, definition);
622+
const auto retrieved = lockLocalParticipant(*subscriber_room)->getSchema(schema_id, publisher_identity);
623+
ASSERT_TRUE(retrieved);
624+
EXPECT_EQ(*retrieved, definition);
625625
}
626626

627627
TEST_F(DataTrackE2ETest, DefineSchemaOverLimitFails) {
@@ -632,7 +632,8 @@ TEST_F(DataTrackE2ETest, DefineSchemaOverLimitFails) {
632632
// Deliberately exceed the maximum allowed schema definition size.
633633
const std::string definition(2 * kMaxSchemaDefinitionBytes, 'a');
634634

635-
EXPECT_THROW(lockLocalParticipant(*room)->defineSchema(schema_id, definition), std::runtime_error);
635+
const auto result = lockLocalParticipant(*room)->defineSchema(schema_id, definition);
636+
EXPECT_FALSE(result);
636637
}
637638

638639
TEST_F(DataTrackE2ETest, DefineDuplicateSchemaFails) {
@@ -642,9 +643,10 @@ TEST_F(DataTrackE2ETest, DefineDuplicateSchemaFails) {
642643
const DataTrackSchemaId schema_id{"some_schema", DataTrackSchemaEncoding::JsonSchema};
643644
const std::string definition(kMaxSchemaDefinitionBytes, 'a');
644645

645-
ASSERT_NO_THROW(lockLocalParticipant(*room)->defineSchema(schema_id, definition));
646+
ASSERT_TRUE(lockLocalParticipant(*room)->defineSchema(schema_id, definition));
646647
// Defining the same schema again must fail.
647-
EXPECT_THROW(lockLocalParticipant(*room)->defineSchema(schema_id, definition), std::runtime_error);
648+
const auto duplicate_result = lockLocalParticipant(*room)->defineSchema(schema_id, definition);
649+
EXPECT_FALSE(duplicate_result);
648650
}
649651

650652
TEST_F(DataTrackE2ETest, GetUndefinedSchemaFails) {
@@ -654,7 +656,8 @@ TEST_F(DataTrackE2ETest, GetUndefinedSchemaFails) {
654656
const auto identity = lockLocalParticipant(*room)->identity();
655657
const DataTrackSchemaId schema_id{"undefined", DataTrackSchemaEncoding::JsonSchema};
656658

657-
EXPECT_THROW((void)lockLocalParticipant(*room)->getSchema(schema_id, identity), std::runtime_error);
659+
const auto result = lockLocalParticipant(*room)->getSchema(schema_id, identity);
660+
EXPECT_FALSE(result.has_value());
658661
}
659662

660663
TEST_F(DataTrackE2ETest, PublishWithSchemaAndFrameEncodingMetadata) {

‎src/tests/unit/test_ffi_client.cpp‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,24 @@ TEST_F(FfiClientTest, NotInitialized_PublishDataTrackAsyncFails) {
518518
EXPECT_EQ(result.error().code, PublishDataTrackErrorCode::INTERNAL);
519519
}
520520

521+
TEST_F(FfiClientTest, NotInitialized_DefineSchemaAsyncFails) {
522+
ASSERT_FALSE(FfiClient::instance().isInitialized());
523+
524+
const DataTrackSchemaId schema_id{"schema", DataTrackSchemaEncoding::JsonSchema};
525+
auto result = FfiClient::instance().defineSchemaAsync(1, schema_id, "{}").get();
526+
EXPECT_FALSE(result);
527+
EXPECT_FALSE(result.error().empty());
528+
}
529+
530+
TEST_F(FfiClientTest, NotInitialized_GetSchemaAsyncFails) {
531+
ASSERT_FALSE(FfiClient::instance().isInitialized());
532+
533+
const DataTrackSchemaId schema_id{"schema", DataTrackSchemaEncoding::JsonSchema};
534+
auto result = FfiClient::instance().getSchemaAsync(1, schema_id, "participant").get();
535+
EXPECT_FALSE(result);
536+
EXPECT_FALSE(result.error().empty());
537+
}
538+
521539
TEST_F(FfiClientTest, NotInitialized_SubscribeDataTrackFails) {
522540
ASSERT_FALSE(FfiClient::instance().isInitialized());
523541

0 commit comments

Comments
 (0)