On 10/7/23 23:40, Pepijn Noltes wrote:
Hi All,

As previously mentioned, Apache Celix is moving towards a new major
release, and this comes with some API-breaking changes.

One of these changes is the removal of the old C API (all
headers/functions without a `celix_` prefix). This means that users
will need to adopt the updated API. However, with this new API, users
won't have access to underlying service management objects like
service reference, service registration, or service tracker. Even
though these objects are part of the OSGi specification, they were
omitted in the updated API for several reasons:

- C does not have garbage collection, and using elements like service
reference without additional locking or reference counting can lead to
malfunctioning code. One could potentially address this with an
explicit reference count or locking mechanism, but this would also
deviate from the OSGi specification and iIMO such a change is more
error-prone.
- I believe service references, by design, are flawed. This is because
one can use a service reference to check if an underlying service
object remains available and then proceed to use it. However, due to
the dynamic nature of OSGi services and the fact that the service
reference does not lock the underlying service objects to prevent
their removal, the actual service can be removed during its use. For
Java, this is often not an issue, because the service remains in
memory thanks to garbage collection. But if the service symbolizes an
external resource, this could potentially (as per Murphy's Law) become
problematic.
- Abstraction leak: While this might be more of a personal preference,
I believe that the utilization of a dynamic service framework
shouldn't leak into application code. An Apache Celix service
reference object is a good example of such a leak. It's worth noting
that since the C standard doesn't have a concept of properties,
leaking `celix_properties` is, in my view, acceptable. Also,
`celix_properties` is a part of the utils library, not the framework
library.
- Using a long type as service ID, service tracker ID, etc., is more
safe, because they can be reliably retained (always copied as value).
The API will check for users if the provided ID remains valid and will
act accordingly.

Given these reasons, the updated Apache Celix API uses service IDs and
tracking IDs to identify services or trackers and offers `useService*`
and `useBundle*` calls. Users can offer a callback to avoid the use of
dangling C objects or the utilization of services and bundles that
have been removed. The intention was that this would cover all use
cases for service reference, service registration, and service tracker
within Apache Celix.
There is one exception : Modifying service properties of an already
registered service via the service registration object. However,
changing the properties of a registered service in C is challenging,
because it relies on garbage collection or requires additional ref
counting.

I fully agree with the assessment of service reference and service
registration, and I also agree that IDs can serve as safer handles
to services/trackers. But unfortunately, the current tracker API provided
by celix_bundle_context.h is still not good enough for my current
usage, which I will describe first to provide some backgrounds:

Celix is used for progressive modularization of a legacy C/C++ project
consisting of about 5MLOC, which could take several years' efforts.
This means the non-modular and the modular parts of the app will
coexist for a long time. The only way these two parts can communicate
with each other is a single bundle context: if Celix is embedded, this
will be the system bundle context, otherwise the non-modular part as
a whole is embedded in a bundle. Either way, the non-modular part
can be viewed as a single bundle.

I consider the current tracker API insufficient for two reasons:

- useService can easily lead to ABBA deadlocks. Consider the event
loop is trying to acquire a tracker's lock to add/remove services, while
the lock holder is calling a service, which is implemented by calling
useService on another service. Note that useService is waiting for
the event loop, the event loop is waiting for the tracker's lock, and
the locker holder is blocked in calling use service. In practice, this kind
of issues manifest themselves as long chain ABCD...A deadlocks.
To completely avoid this kind of issues, I forbid usage of useService
and recommend using now deprecated celix_serviceTracker_useServices.
Then these deadlocks never return.

- useService is costly, even with CELIX_SERVICE_USE_DIRECT set.
I'm afraid having a bundle of MLOC size to compete for the precious
global event loop will not work. Even if we add an implementation similar
to celix_serviceTracker_useServices, all usage within a single bundle
will compete for a single bundle context to transform the tracker ID
into the real tracker. For normal sized bundle, this cost might be bearable.
For a huge bundle like the legacy code, the cost of this contention is high.

I have migrated the lagecy project to Celix 2.4.0, and found that
all deprecated APIs but celix_serviceTracker can be replaced. I added
celix_serviceTracker back by removing its deprecation temporarily.


For most of the APIs and SPIs provided by Apache Celix, the use of
service reference, service registration, or service tracker objects
has already been removed. However, this isn't the case for the Apache
Celix Remote Service Admin SPI.
The Apache Celix RSA SPIs, has 2 locations where a service
registration and service reference is used:

- In `remote_proxy.h`: "service_registration_t *registration" within
the `remote_proxy_factory` structure.
- In `remote_service_admin.h`: "service_reference_pt" inside the
`exportReference_getExportedService` function in the
`remote_service_admin_service_t` structure.

IMO in both instances, these could be replaced with a service id. For
the `remote_proxy`, this would mean registering and unregistering
services using the updated API. I also think that
`exportReference_getExportedService` might be unneeded in the RSA
interface, because it is not used by the topology manager.

It's worth mentioning that the service reference is also used in the
internal API, and ideally, this should also be updated.

Any thoughts?

Kind regards,
Pepijn


--
Peng Zheng

Reply via email to