From 759af948bc03cbcb239f70f9431304d2888ea594 Mon Sep 17 00:00:00 2001 From: Brandon Vandegrift <798832-bmv437@users.noreply.gitlab.com> Date: Fri, 25 Apr 2025 22:54:07 -0400 Subject: [PATCH] Expose the is_shutdown API to all bindings --- CHANGELOG.md | 5 +++++ .../src/tests/common/test_veilid_core.rs | 12 ++++++++++++ veilid-core/src/veilid_api/json_api/mod.rs | 4 ++++ veilid-core/src/veilid_api/json_api/process.rs | 3 +++ veilid-flutter/lib/veilid.dart | 1 + veilid-flutter/lib/veilid_ffi.dart | 13 +++++++++++++ veilid-flutter/lib/veilid_js.dart | 5 +++++ veilid-flutter/rust/src/dart_ffi.rs | 17 +++++++++++++++++ veilid-python/veilid/api.py | 4 ++++ veilid-python/veilid/json_api.py | 3 +++ veilid-python/veilid/operations.py | 1 + veilid-python/veilid/schema/RecvMessage.json | 18 ++++++++++++++++++ veilid-python/veilid/schema/Request.json | 14 ++++++++++++++ veilid-wasm/src/lib.rs | 11 +++++++++++ veilid-wasm/src/veilid_client_js.rs | 11 +++++++++++ veilid-wasm/tests/src/utils/wait-utils.ts | 10 ++++++++++ veilid-wasm/tests/src/veilidClient.test.ts | 5 +++-- 17 files changed, 135 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a05f6704..07b6f758 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,10 +19,15 @@ - veilid-flutter: - Bindings updated for API changes - Corrosion version in cmake build for linux and windows updated to 0.5.1: https://gitlab.com/veilid/veilid/-/issues/447 + - Expose the isShutdown API: https://gitlab.com/veilid/veilid/-/merge_requests/392 - veilid-python: - Fix type assertion bug in watch_dht_values - Update watchvalue integration tests + - Expose the is_shutdown API: https://gitlab.com/veilid/veilid/-/merge_requests/392 + +- veilid-wasm: + - Expose the isShutdown API: https://gitlab.com/veilid/veilid/-/merge_requests/392 **Changed in Veilid 0.4.4** diff --git a/veilid-core/src/tests/common/test_veilid_core.rs b/veilid-core/src/tests/common/test_veilid_core.rs index e54038d7..e0a4812f 100644 --- a/veilid-core/src/tests/common/test_veilid_core.rs +++ b/veilid-core/src/tests/common/test_veilid_core.rs @@ -7,8 +7,20 @@ pub async fn test_startup_shutdown() { let api = api_startup(update_callback, config_callback) .await .expect("startup failed"); + + // Test initial state + assert!(!api.is_shutdown(), "API should not be shut down initially"); + trace!("test_startup_shutdown: shutting down"); + let api_clone = api.clone(); api.shutdown().await; + + // Test state after shutdown + assert!( + api_clone.is_shutdown(), + "API should be shut down after shutdown()" + ); + trace!("test_startup_shutdown: finished"); } diff --git a/veilid-core/src/veilid_api/json_api/mod.rs b/veilid-core/src/veilid_api/json_api/mod.rs index 7b7a2d38..c45f2f5e 100644 --- a/veilid-core/src/veilid_api/json_api/mod.rs +++ b/veilid-core/src/veilid_api/json_api/mod.rs @@ -46,6 +46,7 @@ pub enum RequestOp { args: Vec, }, GetState, + IsShutdown, Attach, Detach, NewPrivateRoute, @@ -143,6 +144,9 @@ pub enum ResponseOp { #[serde(flatten)] result: ApiResult>, }, + IsShutdown { + value: bool, + }, Attach { #[serde(flatten)] result: ApiResult<()>, diff --git a/veilid-core/src/veilid_api/json_api/process.rs b/veilid-core/src/veilid_api/json_api/process.rs index d6b727f4..c0ace32d 100644 --- a/veilid-core/src/veilid_api/json_api/process.rs +++ b/veilid-core/src/veilid_api/json_api/process.rs @@ -619,6 +619,9 @@ impl JsonRequestProcessor { RequestOp::GetState => ResponseOp::GetState { result: to_json_api_result(self.api.get_state().await.map(Box::new)), }, + RequestOp::IsShutdown => ResponseOp::IsShutdown { + value: self.api.is_shutdown(), + }, RequestOp::Attach => ResponseOp::Attach { result: to_json_api_result(self.api.attach().await), }, diff --git a/veilid-flutter/lib/veilid.dart b/veilid-flutter/lib/veilid.dart index bd0555d0..5df6e970 100644 --- a/veilid-flutter/lib/veilid.dart +++ b/veilid-flutter/lib/veilid.dart @@ -137,6 +137,7 @@ abstract class Veilid { void changeLogIgnore(String layer, List changes); Future> startupVeilidCore(VeilidConfig config); Future getVeilidState(); + Future isShutdown(); Future attach(); Future detach(); Future shutdownVeilidCore(); diff --git a/veilid-flutter/lib/veilid_ffi.dart b/veilid-flutter/lib/veilid_ffi.dart index 164e56e7..b1c819a6 100644 --- a/veilid-flutter/lib/veilid_ffi.dart +++ b/veilid-flutter/lib/veilid_ffi.dart @@ -38,6 +38,8 @@ typedef _ChangeLogIgnoreDart = void Function(Pointer, Pointer); typedef _StartupVeilidCoreDart = void Function(int, int, Pointer); // fn get_veilid_state(port: i64) typedef _GetVeilidStateDart = void Function(int); +// fn is_shutdown(port: i64) +typedef _IsShutdownDart = void Function(int); // fn attach(port: i64) typedef _AttachDart = void Function(int); // fn detach(port: i64) @@ -1255,6 +1257,8 @@ class VeilidFFI extends Veilid { _startupVeilidCore = dylib.lookupFunction< Void Function(Int64, Int64, Pointer), _StartupVeilidCoreDart>('startup_veilid_core'), + _isShutdown = dylib.lookupFunction( + 'is_shutdown'), _getVeilidState = dylib.lookupFunction( 'get_veilid_state'), @@ -1494,6 +1498,7 @@ class VeilidFFI extends Veilid { final _ChangeLogIgnoreDart _changeLogIgnore; final _StartupVeilidCoreDart _startupVeilidCore; final _GetVeilidStateDart _getVeilidState; + final _IsShutdownDart _isShutdown; final _AttachDart _attach; final _DetachDart _detach; final _ShutdownVeilidCoreDart _shutdownVeilidCore; @@ -1627,6 +1632,14 @@ class VeilidFFI extends Veilid { return processFutureJson(VeilidState.fromJson, recvPort.first); } + @override + Future isShutdown() async { + final recvPort = ReceivePort('is_shutdown'); + final sendPort = recvPort.sendPort; + _isShutdown(sendPort.nativePort); + return processFuturePlain(recvPort.first); + } + @override Future attach() async { final recvPort = ReceivePort('attach'); diff --git a/veilid-flutter/lib/veilid_js.dart b/veilid-flutter/lib/veilid_js.dart index cc2ccf4e..b1269a03 100644 --- a/veilid-flutter/lib/veilid_js.dart +++ b/veilid-flutter/lib/veilid_js.dart @@ -627,6 +627,11 @@ class VeilidJS extends Veilid { VeilidState.fromJson(jsonDecode(await _wrapApiPromise( js_util.callMethod(wasm, 'get_veilid_state', [])))); + @override + Future isShutdown() async => + await _wrapApiPromise( + js_util.callMethod(wasm, 'is_shutdown', [])); + @override Future attach() => _wrapApiPromise(js_util.callMethod(wasm, 'attach', [])); diff --git a/veilid-flutter/rust/src/dart_ffi.rs b/veilid-flutter/rust/src/dart_ffi.rs index 50dd622e..de9d141c 100644 --- a/veilid-flutter/rust/src/dart_ffi.rs +++ b/veilid-flutter/rust/src/dart_ffi.rs @@ -475,6 +475,23 @@ pub extern "C" fn get_veilid_state(port: i64) { ); } +#[no_mangle] +#[instrument(level = "trace", target = "ffi", skip_all)] +pub extern "C" fn is_shutdown(port: i64) { + DartIsolateWrapper::new(port).spawn_result( + async move { + let veilid_api = get_veilid_api().await; + if let Err(veilid_core::VeilidAPIError::NotInitialized) = veilid_api { + return APIResult::Ok(true); + } + let veilid_api = veilid_api.unwrap(); + let is_shutdown = veilid_api.is_shutdown(); + APIResult::Ok(is_shutdown) + } + .in_current_span(), + ); +} + #[no_mangle] #[instrument(level = "trace", target = "ffi", skip_all)] pub extern "C" fn attach(port: i64) { diff --git a/veilid-python/veilid/api.py b/veilid-python/veilid/api.py index 9305e79e..264424ce 100644 --- a/veilid-python/veilid/api.py +++ b/veilid-python/veilid/api.py @@ -374,6 +374,10 @@ class VeilidAPI(ABC): async def get_state(self) -> VeilidState: pass + @abstractmethod + async def is_shutdown(self) -> bool: + pass + @abstractmethod async def attach(self): pass diff --git a/veilid-python/veilid/json_api.py b/veilid-python/veilid/json_api.py index 243e04f1..48786a22 100644 --- a/veilid-python/veilid/json_api.py +++ b/veilid-python/veilid/json_api.py @@ -323,6 +323,9 @@ class _JsonVeilidAPI(VeilidAPI): return VeilidState.from_json( raise_api_result(await self.send_ndjson_request(Operation.GET_STATE)) ) + + async def is_shutdown(self) -> bool: + return raise_api_result(await self.send_ndjson_request(Operation.IS_SHUTDOWN)) async def attach(self): raise_api_result(await self.send_ndjson_request(Operation.ATTACH)) diff --git a/veilid-python/veilid/operations.py b/veilid-python/veilid/operations.py index 24d26121..7b61f2b2 100644 --- a/veilid-python/veilid/operations.py +++ b/veilid-python/veilid/operations.py @@ -5,6 +5,7 @@ from typing import Self class Operation(StrEnum): CONTROL = "Control" GET_STATE = "GetState" + IS_SHUTDOWN = "IsShutdown" ATTACH = "Attach" DETACH = "Detach" NEW_PRIVATE_ROUTE = "NewPrivateRoute" diff --git a/veilid-python/veilid/schema/RecvMessage.json b/veilid-python/veilid/schema/RecvMessage.json index ecc8239d..48d7f168 100644 --- a/veilid-python/veilid/schema/RecvMessage.json +++ b/veilid-python/veilid/schema/RecvMessage.json @@ -81,6 +81,24 @@ } } }, + { + "type": "object", + "required": [ + "op", + "value" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "IsShutdown" + ] + }, + "value": { + "type": "boolean" + } + } + }, { "type": "object", "anyOf": [ diff --git a/veilid-python/veilid/schema/Request.json b/veilid-python/veilid/schema/Request.json index 1eacced3..045a18e3 100644 --- a/veilid-python/veilid/schema/Request.json +++ b/veilid-python/veilid/schema/Request.json @@ -38,6 +38,20 @@ } } }, + { + "type": "object", + "required": [ + "op" + ], + "properties": { + "op": { + "type": "string", + "enum": [ + "IsShutdown" + ] + } + } + }, { "type": "object", "required": [ diff --git a/veilid-wasm/src/lib.rs b/veilid-wasm/src/lib.rs index ae62f669..5dee2f71 100644 --- a/veilid-wasm/src/lib.rs +++ b/veilid-wasm/src/lib.rs @@ -326,6 +326,17 @@ pub fn get_veilid_state() -> Promise { }) } +#[wasm_bindgen()] +pub fn is_shutdown() -> APIResult { + let veilid_api = get_veilid_api(); + if let Err(veilid_core::VeilidAPIError::NotInitialized) = veilid_api { + return APIResult::Ok(true); + } + let veilid_api = veilid_api.unwrap(); + let is_shutdown = veilid_api.is_shutdown(); + APIResult::Ok(is_shutdown) +} + #[wasm_bindgen()] pub fn attach() -> Promise { wrap_api_future_void(async move { diff --git a/veilid-wasm/src/veilid_client_js.rs b/veilid-wasm/src/veilid_client_js.rs index cdfb8731..e35f78fd 100644 --- a/veilid-wasm/src/veilid_client_js.rs +++ b/veilid-wasm/src/veilid_client_js.rs @@ -162,6 +162,17 @@ impl VeilidClient { APIRESULT_UNDEFINED } + /// Check if Veilid is shutdown. + pub fn isShutdown() -> APIResult { + let veilid_api = get_veilid_api(); + if let Err(veilid_core::VeilidAPIError::NotInitialized) = veilid_api { + return APIResult::Ok(true); + } + let veilid_api = veilid_api.unwrap(); + let is_shutdown = veilid_api.is_shutdown(); + APIResult::Ok(is_shutdown) + } + /// Get a full copy of the current state of Veilid. pub async fn getState() -> APIResult { let veilid_api = get_veilid_api()?; diff --git a/veilid-wasm/tests/src/utils/wait-utils.ts b/veilid-wasm/tests/src/utils/wait-utils.ts index c14886cb..daa2ab9d 100644 --- a/veilid-wasm/tests/src/utils/wait-utils.ts +++ b/veilid-wasm/tests/src/utils/wait-utils.ts @@ -60,6 +60,16 @@ export const waitForDetached = async () => { } } +export const waitForShutdown = async () => { + while (true) { + let isShutdown = veilidClient.isShutdown(); + if (isShutdown) { + break; + } + await waitForMs(1000); + } +} + export const waitForOfflineSubkeyWrite = async (routingContext: VeilidRoutingContext, key: TypedKey) => { while ((await routingContext.inspectDhtRecord(key)).offline_subkeys.length != 0) { await waitForMs(200); diff --git a/veilid-wasm/tests/src/veilidClient.test.ts b/veilid-wasm/tests/src/veilidClient.test.ts index 8a879c2f..64917a83 100644 --- a/veilid-wasm/tests/src/veilidClient.test.ts +++ b/veilid-wasm/tests/src/veilidClient.test.ts @@ -5,8 +5,8 @@ import { veilidCoreStartupConfig, } from './utils/veilid-config'; -import { VeilidState, veilidClient } from '../../pkg/veilid_wasm'; -import { asyncCallWithTimeout, waitForDetached, waitForPublicAttachment } from './utils/wait-utils'; +import { VeilidState, veilidClient } from 'veilid-wasm'; +import { asyncCallWithTimeout, waitForDetached, waitForPublicAttachment, waitForShutdown } from './utils/wait-utils'; describe('veilidClient', function () { before('veilid startup', async function () { @@ -20,6 +20,7 @@ describe('veilidClient', function () { after('veilid shutdown', async function () { await veilidClient.shutdownCore(); + await asyncCallWithTimeout(waitForShutdown(), 10000); }); it('should print version', async function () {