Skip to content

Commit c3a4669

Browse files
committed
Error fixing
1 parent 4f6c707 commit c3a4669

15 files changed

Lines changed: 593 additions & 60 deletions

File tree

sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <cstdlib>
1919
#include <cstring>
2020
#include <dirent.h>
21+
#include <memory>
2122
#include <string>
2223
#include <sys/stat.h>
2324
#include <vector>
@@ -251,6 +252,13 @@ static std::string find_nested_directory(const char* extracted_dir) {
251252
return visible_dirs[0];
252253
}
253254

255+
if (visible_dirs.size() > 1) {
256+
RAC_LOG_WARNING(LOG_TAG,
257+
"find_nested_directory: found %zu subdirectories in '%s', "
258+
"falling back to root (expected exactly 1)",
259+
visible_dirs.size(), extracted_dir);
260+
}
261+
254262
return extracted_dir;
255263
}
256264

@@ -283,25 +291,46 @@ struct orchestrate_context {
283291
void* user_data;
284292
};
285293

294+
/**
295+
* Prevent double-free of orchestrate_context when async callbacks race with error paths.
296+
*
297+
* The context is wrapped in a shared_ptr stored in a shared_ctx_holder.
298+
* The holder is passed as raw void* to C callbacks.
299+
* Both the caller and the callback own a reference via the shared_ptr,
300+
* ensuring the context outlives all users.
301+
*/
302+
struct shared_ctx_holder {
303+
std::shared_ptr<orchestrate_context> ctx;
304+
};
305+
286306
/**
287307
* HTTP progress callback — forwards to download manager which recalculates overall progress.
288308
*/
289309
static void orchestrate_http_progress(int64_t bytes_downloaded, int64_t total_bytes,
290310
void* callback_user_data) {
291-
auto* ctx = static_cast<orchestrate_context*>(callback_user_data);
292-
if (!ctx || !ctx->dm_handle) return;
311+
auto* holder = static_cast<shared_ctx_holder*>(callback_user_data);
312+
if (!holder || !holder->ctx || !holder->ctx->dm_handle) return;
293313

314+
auto& ctx = holder->ctx;
294315
rac_download_manager_update_progress(ctx->dm_handle, ctx->task_id.c_str(), bytes_downloaded,
295316
total_bytes);
296317
}
297318

298319
/**
299320
* HTTP completion callback — handles post-download extraction and cleanup.
321+
* Deletes the holder (releasing its shared_ptr reference) when done.
300322
*/
301323
static void orchestrate_http_complete(rac_result_t result, const char* downloaded_path,
302324
void* callback_user_data) {
303-
auto* ctx = static_cast<orchestrate_context*>(callback_user_data);
304-
if (!ctx) return;
325+
auto* holder = static_cast<shared_ctx_holder*>(callback_user_data);
326+
if (!holder || !holder->ctx) {
327+
delete holder;
328+
return;
329+
}
330+
331+
// Take ownership — holder is deleted at every exit path below
332+
auto ctx = holder->ctx;
333+
delete holder;
305334

306335
if (result != RAC_SUCCESS) {
307336
// HTTP download failed
@@ -312,7 +341,6 @@ static void orchestrate_http_complete(rac_result_t result, const char* downloade
312341
if (ctx->user_complete_callback) {
313342
ctx->user_complete_callback(ctx->task_id.c_str(), result, nullptr, ctx->user_data);
314343
}
315-
delete ctx;
316344
return;
317345
}
318346

@@ -345,7 +373,6 @@ static void orchestrate_http_complete(rac_result_t result, const char* downloade
345373

346374
// Cleanup temp archive
347375
delete_file(ctx->download_dest_path.c_str());
348-
delete ctx;
349376
return;
350377
}
351378

@@ -392,8 +419,6 @@ static void orchestrate_http_complete(rac_result_t result, const char* downloade
392419
ctx->user_complete_callback(ctx->task_id.c_str(), RAC_SUCCESS, final_path.c_str(),
393420
ctx->user_data);
394421
}
395-
396-
delete ctx;
397422
}
398423

399424
// =============================================================================
@@ -469,8 +494,8 @@ rac_result_t rac_download_orchestrate(rac_download_manager_handle_t dm_handle,
469494
return start_result;
470495
}
471496

472-
// 5. Create orchestration context for callbacks
473-
auto* ctx = new orchestrate_context();
497+
// 5. Create orchestration context for callbacks (shared_ptr for safe async lifetime)
498+
auto ctx = std::make_shared<orchestrate_context>();
474499
ctx->dm_handle = dm_handle;
475500
ctx->model_id = model_id;
476501
ctx->download_url = download_url;
@@ -485,17 +510,20 @@ rac_result_t rac_download_orchestrate(rac_download_manager_handle_t dm_handle,
485510
ctx->user_complete_callback = complete_callback;
486511
ctx->user_data = user_data;
487512

513+
// Wrap in holder for C callback void* — callback takes ownership and deletes holder
514+
auto* holder = new shared_ctx_holder{ctx};
515+
488516
// 6. Start HTTP download via platform adapter
489517
char* http_task_id = nullptr;
490518
rac_result_t http_result =
491519
rac_http_download(download_url, download_dest.c_str(), orchestrate_http_progress,
492-
orchestrate_http_complete, ctx, &http_task_id);
520+
orchestrate_http_complete, holder, &http_task_id);
493521

494522
if (http_result != RAC_SUCCESS) {
495523
RAC_LOG_ERROR(LOG_TAG, "Failed to start HTTP download for: %s", model_id);
496524
rac_download_manager_mark_failed(dm_handle, task_id, http_result,
497525
"Failed to start HTTP download");
498-
delete ctx;
526+
delete holder; // Safe — ctx shared_ptr ref still alive until scope exit
499527
rac_free(task_id);
500528
return http_result;
501529
}
@@ -583,26 +611,37 @@ rac_result_t rac_download_orchestrate_multi(
583611
// For now we use the platform adapter's synchronous path.
584612
char* http_task_id = nullptr;
585613

586-
// For multi-file we need a simpler blocking approach
587-
// The complete callback will be called by the platform when done
614+
// For multi-file we need a simpler blocking approach.
615+
// Use shared_ptr to prevent double-free if callback fires unexpectedly.
588616
struct multi_file_ctx {
589617
bool completed;
590618
rac_result_t result;
591619
std::string downloaded_path;
592620
};
593621

594-
auto* file_ctx = new multi_file_ctx{false, RAC_SUCCESS, ""};
622+
auto file_ctx = std::make_shared<multi_file_ctx>(multi_file_ctx{false, RAC_SUCCESS, ""});
623+
624+
// Wrap in a raw holder for C callback void* — callback takes ownership of the holder
625+
struct multi_file_holder {
626+
std::shared_ptr<multi_file_ctx> ctx;
627+
};
628+
auto* file_holder = new multi_file_holder{file_ctx};
595629

596630
auto file_complete = [](rac_result_t result, const char* path, void* ud) {
597-
auto* fctx = static_cast<multi_file_ctx*>(ud);
598-
fctx->completed = true;
599-
fctx->result = result;
600-
if (path) fctx->downloaded_path = path;
631+
auto* holder = static_cast<multi_file_holder*>(ud);
632+
if (!holder || !holder->ctx) {
633+
delete holder;
634+
return;
635+
}
636+
holder->ctx->completed = true;
637+
holder->ctx->result = result;
638+
if (path) holder->ctx->downloaded_path = path;
639+
delete holder;
601640
};
602641

603642
rac_result_t http_result = rac_http_download(
604643
file_url.c_str(), dest_path.c_str(), nullptr /* no per-file progress */, file_complete,
605-
file_ctx, &http_task_id);
644+
file_holder, &http_task_id);
606645

607646
if (http_task_id) rac_free(http_task_id);
608647

@@ -611,19 +650,18 @@ rac_result_t rac_download_orchestrate_multi(
611650
any_failed = true;
612651
RAC_LOG_ERROR(LOG_TAG, "Required file download failed to start: %s",
613652
file.relative_path);
614-
delete file_ctx;
653+
// Download never started, so callback won't fire — delete holder safely
654+
delete file_holder;
615655
break;
616656
}
617657
RAC_LOG_WARNING(LOG_TAG, "Optional file download failed to start: %s",
618658
file.relative_path);
619-
delete file_ctx;
659+
// Download never started, so callback won't fire — delete holder safely
660+
delete file_holder;
620661
continue;
621662
}
622663

623-
// Note: The HTTP download is asynchronous. For multi-file orchestration,
624-
// the platform SDK should handle sequencing. This is a best-effort sequential trigger.
625-
// The actual completion will be handled by the platform's async dispatch.
626-
delete file_ctx;
664+
// Download started — async callback will delete file_holder
627665
}
628666

629667
if (any_failed) {

sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,24 +260,31 @@ rac_result_t rac_extract_archive_native(const char* archive_path, const char* de
260260
size_t size;
261261
la_int64_t offset;
262262

263+
bool data_error = false;
263264
while (true) {
264265
r = archive_read_data_block(a, &buff, &size, &offset);
265266
if (r == ARCHIVE_EOF) break;
266267
if (r != ARCHIVE_OK) {
267268
const char* err = archive_error_string(a);
268-
RAC_LOG_WARNING(kLogTag, "Error reading data for: %s (%s)", pathname,
269-
err ? err : "unknown");
269+
RAC_LOG_ERROR(kLogTag, "Error reading data for: %s (%s)", pathname,
270+
err ? err : "unknown");
271+
data_error = true;
270272
break;
271273
}
272274
r = archive_write_data_block(ext, buff, size, offset);
273275
if (r != ARCHIVE_OK) {
274276
const char* err = archive_error_string(ext);
275-
RAC_LOG_WARNING(kLogTag, "Error writing data for: %s (%s)", pathname,
276-
err ? err : "unknown");
277+
RAC_LOG_ERROR(kLogTag, "Error writing data for: %s (%s)", pathname,
278+
err ? err : "unknown");
279+
data_error = true;
277280
break;
278281
}
279282
result.bytes_extracted += static_cast<int64_t>(size);
280283
}
284+
if (data_error) {
285+
status = RAC_ERROR_EXTRACTION_FAILED;
286+
break;
287+
}
281288
}
282289

283290
// Finish entry (sets permissions, timestamps)

sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_file_manager.dart

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,112 @@ class DartBridgeFileManager {
162162
}
163163
}
164164

165+
/// Create a model folder and return its path.
166+
static String? createModelFolder(String modelId, int framework) {
167+
final fn = _lookup<
168+
Int32 Function(Pointer<RacFileCallbacksStruct>, Pointer<Utf8>, Int32,
169+
Pointer<Utf8>, Size),
170+
int Function(Pointer<RacFileCallbacksStruct>, Pointer<Utf8>, int,
171+
Pointer<Utf8>, int)>('rac_file_manager_create_model_folder');
172+
if (fn == null || _callbacksPtr == null) return null;
173+
174+
final modelIdPtr = modelId.toNativeUtf8();
175+
const bufSize = 1024;
176+
final outPath = calloc<Uint8>(bufSize).cast<Utf8>();
177+
try {
178+
final result = fn(_callbacksPtr!, modelIdPtr, framework, outPath, bufSize);
179+
if (result != RacResultCode.success) return null;
180+
return outPath.toDartString();
181+
} finally {
182+
calloc.free(modelIdPtr);
183+
calloc.free(outPath);
184+
}
185+
}
186+
187+
/// Check if a model folder exists.
188+
static bool modelFolderExists(String modelId, int framework) {
189+
final fn = _lookup<
190+
Int32 Function(Pointer<RacFileCallbacksStruct>, Pointer<Utf8>, Int32,
191+
Pointer<Int32>, Pointer<Int32>),
192+
int Function(Pointer<RacFileCallbacksStruct>, Pointer<Utf8>, int,
193+
Pointer<Int32>, Pointer<Int32>)>(
194+
'rac_file_manager_model_folder_exists');
195+
if (fn == null || _callbacksPtr == null) return false;
196+
197+
final modelIdPtr = modelId.toNativeUtf8();
198+
final existsPtr = calloc<Int32>();
199+
try {
200+
fn(_callbacksPtr!, modelIdPtr, framework, existsPtr, nullptr);
201+
return existsPtr.value == RAC_TRUE;
202+
} finally {
203+
calloc.free(modelIdPtr);
204+
calloc.free(existsPtr);
205+
}
206+
}
207+
208+
/// Get combined storage information.
209+
static StorageInfo? getStorageInfo() {
210+
final fn = _lookup<
211+
Int32 Function(
212+
Pointer<RacFileCallbacksStruct>, Pointer<RacFileManagerStorageInfoStruct>),
213+
int Function(Pointer<RacFileCallbacksStruct>,
214+
Pointer<RacFileManagerStorageInfoStruct>)>(
215+
'rac_file_manager_get_storage_info');
216+
if (fn == null || _callbacksPtr == null) return null;
217+
218+
final infoPtr = calloc<RacFileManagerStorageInfoStruct>();
219+
try {
220+
final result = fn(_callbacksPtr!, infoPtr);
221+
if (result != RacResultCode.success) return null;
222+
return StorageInfo(
223+
deviceTotal: infoPtr.ref.deviceTotal,
224+
deviceFree: infoPtr.ref.deviceFree,
225+
modelsSize: infoPtr.ref.modelsSize,
226+
cacheSize: infoPtr.ref.cacheSize,
227+
tempSize: infoPtr.ref.tempSize,
228+
totalAppSize: infoPtr.ref.totalAppSize,
229+
);
230+
} finally {
231+
calloc.free(infoPtr);
232+
}
233+
}
234+
235+
/// Check storage availability via C++ rac_file_manager_check_storage.
236+
/// Returns full availability result including warnings and recommendations.
237+
static StorageAvailability? checkStorageAvailability(int requiredBytes) {
238+
final fn = _lookup<
239+
Int32 Function(Pointer<RacFileCallbacksStruct>, Int64,
240+
Pointer<RacStorageAvailabilityStruct>),
241+
int Function(Pointer<RacFileCallbacksStruct>, int,
242+
Pointer<RacStorageAvailabilityStruct>)>(
243+
'rac_file_manager_check_storage');
244+
if (fn == null || _callbacksPtr == null) return null;
245+
246+
final availPtr = calloc<RacStorageAvailabilityStruct>();
247+
try {
248+
final result = fn(_callbacksPtr!, requiredBytes, availPtr);
249+
if (result != RacResultCode.success) return null;
250+
final rec = availPtr.ref.recommendation;
251+
return StorageAvailability(
252+
isAvailable: availPtr.ref.isAvailable == RAC_TRUE,
253+
requiredSpace: availPtr.ref.requiredSpace,
254+
availableSpace: availPtr.ref.availableSpace,
255+
hasWarning: availPtr.ref.hasWarning == RAC_TRUE,
256+
recommendation: rec != nullptr ? rec.toDartString() : null,
257+
);
258+
} finally {
259+
calloc.free(availPtr);
260+
}
261+
}
262+
263+
/// Check storage availability for a given number of bytes.
264+
/// Convenience wrapper that returns a simple bool.
265+
static bool checkStorage(int requiredBytes) {
266+
final result = checkStorageAvailability(requiredBytes);
267+
if (result == null) return true; // Default to available if check fails
268+
return result.isAvailable;
269+
}
270+
165271
/// Delete a model folder.
166272
static bool deleteModel(String modelId, int framework) {
167273
final fn = _lookup<
@@ -195,6 +301,46 @@ class DartBridgeFileManager {
195301
}
196302
}
197303

304+
// =============================================================================
305+
// Storage Info Data Class
306+
// =============================================================================
307+
308+
/// Storage availability result from C++ rac_file_manager_check_storage.
309+
class StorageAvailability {
310+
final bool isAvailable;
311+
final int requiredSpace;
312+
final int availableSpace;
313+
final bool hasWarning;
314+
final String? recommendation;
315+
316+
const StorageAvailability({
317+
required this.isAvailable,
318+
required this.requiredSpace,
319+
required this.availableSpace,
320+
required this.hasWarning,
321+
this.recommendation,
322+
});
323+
}
324+
325+
/// Combined storage information from C++ file manager.
326+
class StorageInfo {
327+
final int deviceTotal;
328+
final int deviceFree;
329+
final int modelsSize;
330+
final int cacheSize;
331+
final int tempSize;
332+
final int totalAppSize;
333+
334+
const StorageInfo({
335+
required this.deviceTotal,
336+
required this.deviceFree,
337+
required this.modelsSize,
338+
required this.cacheSize,
339+
required this.tempSize,
340+
required this.totalAppSize,
341+
});
342+
}
343+
198344
// =============================================================================
199345
// C Callbacks (Platform I/O)
200346
// =============================================================================

0 commit comments

Comments
 (0)