Skip to content

Commit

Permalink
Add Tokio and config-rs features (#37)
Browse files Browse the repository at this point in the history
As discussed offline, I have some oddball integration scenarios where
much of the SF runtime interaction is already handled by existing
application logic written in other languages, but we'd like to move
parts of the system into Rust.

At least for now, my project is not making use of any of the
functionality in mssf-core that actually necessitates use of Tokio, nor
are we currently using the config_rs crate. At the same time, mssf_com
is a bit too low level to be ideal for us - we have no desire to
duplicate / rewrite existing Rust wrappers such as:
* mssf_core::runtime::config types
* mssf_core::runtime::ActivationContext

To enable minimizing dependencies and compile times for those of use who
don't need the entire runtime at present, this PR adds two default-on
features (naming suggestions of course welcome) - one for config-rs, one
for tokio (tokio feature also requires ctrlc as that's small and part of
the Executor, and I'm assuming most native-Rust applications that are ok
with tokio will want that too).

It also moves some of the code form runtimes/mod.rs to its own file to
make the conditional complication a bit less messy, though it could be
cleaner.

Additionally, one of the echomain (echomain-stateful) examples is
adjusted to demonstrate that config_rs feature is optional. The example
already did not need any of the APIs that are now gated behind that
feature, demonstrating that a thinner version of mssf_core could have
utility to pure Rust SF applications as well.
I think it's just the hostname function that even needs the tokio_async
feature, but haven't tried to create a "neither feature" example yet.
  • Loading branch information
cgettys-microsoft authored Jun 12, 2024
1 parent 1a6e8ab commit 32fde93
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 65 deletions.
14 changes: 11 additions & 3 deletions crates/libs/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,22 @@ include = [
"Cargo.toml",
]

[features]
default = ["config_source", "tokio_async"]
# Required for a lot of callback functionality.
# Also requires ctrlc for signal handling
tokio_async = ["dep:tokio", "ctrlc"]
# Config crate required to implement its interface.
config_source = ["config"]

[dependencies]
tracing.workspace = true
tokio = { version = "1", features = ["sync" , "rt-multi-thread", "rt", "macros"] }
tokio = { version = "1", features = ["sync" , "rt-multi-thread", "rt", "macros"], optional = true }
windows-core = "0.56"
ctrlc = { version = "3.0", features = ["termination"] }
ctrlc = { version = "3.0", features = ["termination"], optional = true }
trait-variant = "0.1.1"
bitflags = "2.5.0"
config = { version = "0.14.0", default-features = false}
config = { version = "0.14.0", default-features = false, optional = true }

[dev-dependencies]
paste = "1.0"
Expand Down
18 changes: 17 additions & 1 deletion crates/libs/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,33 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

//! # Features
//! All features are enabled by default unless otherwise noted.
//! For most scenarios, you'll want the features. However, in some scenarios, such as:
//! - integrating Rust into an existing Service Fabric Application written in another language
//! - when you are using the lower-level COM API to do something more custom
//! You might not need all of the functionality that the mssf-core crate provides
//! In this case, you can configure only what you need to reduce dependencies and compile times.
//!
//! * ** config_source ** -
//! Provides an implementation of config::Source. Requires config_rs crate
//!
//! * ** Tokio ** -
//! A lot of the sophoisticated functionality in this crate requires Tokio.
//! However, even without tokio, some of the higher level wrappers over COM types have utility.
#![allow(non_snake_case)]

// lib that contains all common extensions for the raw fabric apis.

#[cfg(feature = "tokio_async")]
pub mod client;
#[cfg(feature = "config_source")]
pub mod conf;
pub mod debug;
mod iter;
pub mod runtime;
pub mod strings;
#[cfg(feature = "tokio_async")]
pub mod sync;

// re-export some windows types
Expand Down
80 changes: 19 additions & 61 deletions crates/libs/core/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,43 @@
// ------------------------------------------------------------

use mssf_com::{
FabricCommon::{
FabricRuntime::{
FabricCreateRuntime, FabricGetActivationContext, IFabricCodePackageActivationContext,
IFabricRuntime, IFabricStatefulServiceFactory, IFabricStatelessServiceFactory,
},
IFabricAsyncOperationCallback, IFabricAsyncOperationContext,
FabricCommon::FabricRuntime::{
FabricCreateRuntime, FabricGetActivationContext, IFabricCodePackageActivationContext,
IFabricRuntime,
},
FABRIC_ENDPOINT_RESOURCE_DESCRIPTION,
};
use windows_core::{Error, Interface, HSTRING, PCWSTR};

use self::{
config::ConfigurationPackage, executor::Executor, stateful::StatefulServiceFactory,
stateful_bridge::StatefulServiceFactoryBridge, stateless::StatelessServiceFactory,
stateless_bridge::StatelessServiceFactoryBridge,
};
#[cfg(feature = "tokio_async")]
use mssf_com::FabricCommon::{IFabricAsyncOperationCallback, IFabricAsyncOperationContext};

use self::config::ConfigurationPackage;

#[cfg(feature = "tokio_async")]
pub use self::runtime_wrapper::Runtime;

#[cfg(feature = "tokio_async")]
mod bridge;
pub mod config;
pub mod error;
#[cfg(feature = "tokio_async")]
pub mod executor;
#[cfg(feature = "tokio_async")]
pub mod node_context;
#[cfg(feature = "tokio_async")]
pub mod runtime_wrapper;
pub mod stateful;
#[cfg(feature = "tokio_async")]
pub mod stateful_bridge;
#[cfg(feature = "tokio_async")]
pub mod stateful_proxy;
pub mod stateful_types;
pub mod stateless;
#[cfg(feature = "tokio_async")]
pub mod stateless_bridge;
pub mod store;
#[cfg(feature = "tokio_async")]
pub mod store_proxy;
pub mod store_types;

Expand All @@ -52,56 +60,6 @@ pub fn get_com_activation_context() -> ::windows_core::Result<IFabricCodePackage
Ok(activation_ctx)
}

// safe wrapping for runtime
pub struct Runtime<E>
where
E: Executor,
{
com_impl: IFabricRuntime,
rt: E,
}

impl<E> Runtime<E>
where
E: Executor,
{
pub fn create(rt: E) -> ::windows_core::Result<Runtime<E>> {
let com = create_com_runtime()?;
Ok(Runtime { com_impl: com, rt })
}

pub fn register_stateless_service_factory<F>(
&self,
servicetypename: &HSTRING,
factory: F,
) -> windows_core::Result<()>
where
F: StatelessServiceFactory,
{
let rt_cp = self.rt.clone();
let bridge: IFabricStatelessServiceFactory =
StatelessServiceFactoryBridge::create(factory, rt_cp).into();
unsafe {
self.com_impl
.RegisterStatelessServiceFactory(servicetypename, &bridge)
}
}

pub fn register_stateful_service_factory(
&self,
servicetypename: &HSTRING,
factory: impl StatefulServiceFactory,
) -> windows_core::Result<()> {
let rt_cp = self.rt.clone();
let bridge: IFabricStatefulServiceFactory =
StatefulServiceFactoryBridge::create(factory, rt_cp).into();
unsafe {
self.com_impl
.RegisterStatefulServiceFactory(servicetypename, &bridge)
}
}
}

#[derive(Debug)]
pub struct EndpointResourceDesc {
pub Name: ::windows_core::HSTRING,
Expand Down
59 changes: 59 additions & 0 deletions crates/libs/core/src/runtime/runtime_wrapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::HSTRING;
/// safe wrapping for runtime
use mssf_com::FabricCommon::FabricRuntime::{
IFabricRuntime, IFabricStatefulServiceFactory, IFabricStatelessServiceFactory,
};

use super::{
create_com_runtime, executor::Executor, stateful::StatefulServiceFactory,
stateful_bridge::StatefulServiceFactoryBridge, stateless::StatelessServiceFactory,
stateless_bridge::StatelessServiceFactoryBridge,
};
pub struct Runtime<E>
where
E: Executor,
{
com_impl: IFabricRuntime,
rt: E,
}

impl<E> Runtime<E>
where
E: Executor,
{
pub fn create(rt: E) -> ::windows_core::Result<Runtime<E>> {
let com = create_com_runtime()?;
Ok(Runtime { com_impl: com, rt })
}

pub fn register_stateless_service_factory<F>(
&self,
servicetypename: &HSTRING,
factory: F,
) -> windows_core::Result<()>
where
F: StatelessServiceFactory,
{
let rt_cp = self.rt.clone();
let bridge: IFabricStatelessServiceFactory =
StatelessServiceFactoryBridge::create(factory, rt_cp).into();
unsafe {
self.com_impl
.RegisterStatelessServiceFactory(servicetypename, &bridge)
}
}

pub fn register_stateful_service_factory(
&self,
servicetypename: &HSTRING,
factory: impl StatefulServiceFactory,
) -> windows_core::Result<()> {
let rt_cp = self.rt.clone();
let bridge: IFabricStatefulServiceFactory =
StatefulServiceFactoryBridge::create(factory, rt_cp).into();
unsafe {
self.com_impl
.RegisterStatefulServiceFactory(servicetypename, &bridge)
}
}
}
4 changes: 4 additions & 0 deletions crates/samples/echomain-stateful/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ features = [

[dependencies.mssf-core]
path = "../../libs/core"
# We don't showcase config integration in this particular example
default-features = false
features = ["tokio_async"]

0 comments on commit 32fde93

Please sign in to comment.