kulst commented on PR #627:
URL: https://github.com/apache/celix/pull/627#issuecomment-2110255869

   Two weeks have passed and I wanted to share again my progress :)
   
   **BundleContext lifetime**
   Another way I came up with is to hand out a BundleContext to the bundle and 
having an Arc<(Condvar, Mutex<Bool>)> flag in the BundleContext (not accessible 
by the BundleContexts API). When the BundleContext is dropped, the flag is set 
and the Condvar is notified.
   A copy of that Arc is stored in the internal part of the activator. When the 
stop/delete function of the bundle is invoked the flag is checked periodically 
if it is set. By using the Condvar we can block between these checks (and also 
log an error similar to the cpp API).
   
   However I postponed this topic a little bit as I think there are a few 
feasible solutions. We can later review these solutions and settle on one of 
them.
   
   **Services**
   The next topic I wanted to approach is services. When using a service a 
bundle calls the function of another bundle. Both bundles need to agree upon 
the the API of the service and also on the ABI of the service. 
   In my eyes the Celix Rust API should be safe to use for the user. This means 
it should not be possible to cause undefined behaviour without using unsafe 
code in Rust. But we can not enforce during compilation that the API and ABI of 
the service we want to use is the same as we think it is.
   
   So I think we have two options here:
   
   1. Using a service is inherently unsafe and invariants must be upheld by the 
user. For C and C++ services this will probably be the only solution, so using 
these services from Rust will allways be unsafe. However for Rust services I 
think there is another way!
   2. Checking ABI and API compatibility at runtime. If we can achieve this, 
using a service is safe, incompatible ABI/API would not cause undefined 
behaviour, returning an Error would be enough. 
   
   ** How can we achieve ABI and API checks at runtime?**
   There is one Rust crate that already tries to deal with this issue. Only a 
very small part of the Rust API is already stable. Most part of the API is 
allowed to change between compiler versions but also between different compiler 
runs. 
   [abi_stable](https://docs.rs/abi_stable/latest/abi_stable/) provides ABI 
stability by transforming Rust types with unstable ABI into types with a stable 
ABI (#[repr(C)]) types. It utilizes the Rust procedural macro system for this 
which can transform abstract syntax trees into other abstract syntax trees.
   To get API stability the crate provides type information of these types, 
which can be checked at runtime.
   To make sure that the API and ABI of the abi_stable crate is compatible a 
static variable is provided which can also be checked against at runtime.
   
   I think we can utilize this crate to provide safe services. 
   
   ** How could this look like?**
   A service would be an object that implements a trait (the service 
interface). This is similar to C++ services. The trait is defined in an Service 
API crate. 
   The bundle that provides the service statically links against this API 
crate. When registering this service a pointer to the bundle-static type 
information and abi_stable library information is stored in the framework (for 
that it is necessary to implement one or two functions that are only usable 
internally by the Celix Rust API). 
   
   When another bundle now wants to use the service it must statically link 
against the API crate as well. When using the service it is checked that the 
type information and abi_stable library information are compatible. 
   
   ** Additional opportunities**
   It would be great if we could use Rust services from C and C++. For this the 
Service API crate must export some C/C++ header files. Luckily there is a crate 
that already does this: [cglue](https://docs.rs/cglue/latest/cglue/). On top of 
that it is compatible and works together with abi_stable. 
   This makes it possible to easily create services that are usable from C, C++ 
and Rust and provide additional guaranties. 
   
   **Final hurdles**
   I would like to rewrite the POC in the next step to make sure all of this 
works. However there is one problem I could not solve yet:
   What if a service of a bundle provides a 'static reference in its interface? 
The reference could point to 'static data of the bundle. After unloading the 
providing bundle this reference would be invalid.
   There should be a way to constrain the lifetimes to the lifetime of the 
service use callback. 
   However I do not know how yet. 
   Maybe there is a way by checking the service API with an additional proc 
macro. 
   
   **Summary**
   Again a lot of text. I hope you are fine with documenting my thoughts and 
progress here. There are a lot of topics that need to be considered. At the 
moment Rust is not perfectly well suited for dynamic linking. Its mostly 
unstable ABI makes it necessary to heavily use the macro system to provide a 
safe interface. If you want to have all of the Rust safety guaranties as well, 
a lot of possibilites need to be considered. 
   But I still think it is possible and I will continue working on it.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@celix.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to