Merge branch 'feature/expose-is-shutdown-api' into 'main'

Expose the is_shutdown API to all bindings

See merge request veilid/veilid!392
This commit is contained in:
Brandon Vandegrift 2025-04-27 01:33:53 +00:00
commit bff7a0c718
17 changed files with 135 additions and 2 deletions

View file

@ -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**

View file

@ -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");
}

View file

@ -46,6 +46,7 @@ pub enum RequestOp {
args: Vec<String>,
},
GetState,
IsShutdown,
Attach,
Detach,
NewPrivateRoute,
@ -143,6 +144,9 @@ pub enum ResponseOp {
#[serde(flatten)]
result: ApiResult<Box<VeilidState>>,
},
IsShutdown {
value: bool,
},
Attach {
#[serde(flatten)]
result: ApiResult<()>,

View file

@ -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),
},

View file

@ -137,6 +137,7 @@ abstract class Veilid {
void changeLogIgnore(String layer, List<String> changes);
Future<Stream<VeilidUpdate>> startupVeilidCore(VeilidConfig config);
Future<VeilidState> getVeilidState();
Future<bool> isShutdown();
Future<void> attach();
Future<void> detach();
Future<void> shutdownVeilidCore();

View file

@ -38,6 +38,8 @@ typedef _ChangeLogIgnoreDart = void Function(Pointer<Utf8>, Pointer<Utf8>);
typedef _StartupVeilidCoreDart = void Function(int, int, Pointer<Utf8>);
// 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<Utf8>),
_StartupVeilidCoreDart>('startup_veilid_core'),
_isShutdown = dylib.lookupFunction<Void Function(Int64), _IsShutdownDart>(
'is_shutdown'),
_getVeilidState =
dylib.lookupFunction<Void Function(Int64), _GetVeilidStateDart>(
'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<bool> isShutdown() async {
final recvPort = ReceivePort('is_shutdown');
final sendPort = recvPort.sendPort;
_isShutdown(sendPort.nativePort);
return processFuturePlain<bool>(recvPort.first);
}
@override
Future<void> attach() async {
final recvPort = ReceivePort('attach');

View file

@ -627,6 +627,11 @@ class VeilidJS extends Veilid {
VeilidState.fromJson(jsonDecode(await _wrapApiPromise<String>(
js_util.callMethod(wasm, 'get_veilid_state', []))));
@override
Future<bool> isShutdown() async =>
await _wrapApiPromise<bool>(
js_util.callMethod(wasm, 'is_shutdown', []));
@override
Future<void> attach() =>
_wrapApiPromise(js_util.callMethod(wasm, 'attach', []));

View file

@ -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) {

View file

@ -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

View file

@ -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))

View file

@ -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"

View file

@ -81,6 +81,24 @@
}
}
},
{
"type": "object",
"required": [
"op",
"value"
],
"properties": {
"op": {
"type": "string",
"enum": [
"IsShutdown"
]
},
"value": {
"type": "boolean"
}
}
},
{
"type": "object",
"anyOf": [

View file

@ -38,6 +38,20 @@
}
}
},
{
"type": "object",
"required": [
"op"
],
"properties": {
"op": {
"type": "string",
"enum": [
"IsShutdown"
]
}
}
},
{
"type": "object",
"required": [

View file

@ -326,6 +326,17 @@ pub fn get_veilid_state() -> Promise {
})
}
#[wasm_bindgen()]
pub fn is_shutdown() -> APIResult<bool> {
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 {

View file

@ -162,6 +162,17 @@ impl VeilidClient {
APIRESULT_UNDEFINED
}
/// Check if Veilid is shutdown.
pub fn isShutdown() -> APIResult<bool> {
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<VeilidState> {
let veilid_api = get_veilid_api()?;

View file

@ -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);

View file

@ -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 () {