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