Skip to content

How to use callback functions when calling celix service #775

Open
@xuzhenbao

Description

@xuzhenbao

In some service usage scenarios, the result may not be returned immediately after the service call is completed, it may be returned through a callback function at some time after the service call is completed. How should we use callback functions in celix services (it includes remote services and local services)?

In my opinion, it is unsafe to design function pointers directly in the service method parameters, because the service provider may have been destroyed after the service call is completed. At this time, if the callback function is called again, problems will occur. In addition, designing callback function pointers in the service method may make the implementation of RSA complex.

Therefore, I think the callback function can be designed as a celix service. If a service method needs a callback function, the service method's input parameters should include the service identifier of the callback function (Maybe the service identifier is a string composed of framework uuid and service id). When calling the service method, the service caller needs to register the callback function service first, and then pass the callback function service identifier to the service method. The service provider can invoke the callback function through the callback function service. The example is as follows:

//provider api

#define MY_SERVICE_NAME "my_service"
#define MY_SERVICE_VERSION "1.0.0"

typedef struct my_service {
    void* handle;
    void (*doSomething)(void *handle, const char *input, const char *callbackServiceUUID);
} my_service_t;

#define MY_SERVICE_CALLBACK_SERVICE_NAME "my_service_callback"
#define MY_SERVICE_CALLBACK_SERVICE_VERSION "1.0.0"
typedef struct callback_service {
    void* handle;
    void (*callback)(void *handle, const char *result);
} callback_service_t;
//provider impl
typedef struct provider {
    celix_bundle_context_t *context;
    my_service_t service;
    long svcId;
    long trkId;
} provider_t;

typedef struct use_callback_data {
    provider_t *provider;
    const char *input;
    const char *callbackServiceUUID;
}use_callback_data_t;

void useCallback(void *handle, void *svc) {
    use_callback_data_t *data = handle;
    callback_service_t *callback = svc;
    callback->callback(callback->handle, "result");
}

void my_service_doSomething(void *handle, const char *input, const char *callbackServiceUUID) {
    provider_t *provider = handle;

// invoke callback service
    use_callback_data_t data = {provider, input, callbackServiceUUID};
    celix_bundleContext_useTrackedServices(provider->context, provider->trkId, &data, useCallback);
}

void provider_init(celix_bundle_context_t *context, provider_t *provider) {
    provider->context = context;
    provider->service.handle = provider;
    provider->service.doSomething = my_service_doSomething;
    provider->svcId = celix_bundleContext_registerService(context, &provider->service, MY_SERVICE_NAME, MY_SERVICE_VERSION, NULL);

//track callback service
    celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
    opts.filter.serviceName = MY_SERVICE_CALLBACK_SERVICE_NAME;
    provider->trkId = celix_bundleContext_trackServicesWithOptions(ctx, &opts);
}
//consumer impl

typedef struct consumer {
    celix_bundle_context_t *context;
    callback_service_t service;
    long svcId;
    long trkId;
} consumer_t;

void my_service_callback(void *handle, const char *result) {
    printf("my_service_callback called with result %s\n", result);
}

void useService(void *handle, void *svc) {
    consumer_t *consumer = handle;
    my_service_t *service = svc;
    service->doSomething(service->handle, "input", "<frameworkUUID>+<service id>");
}

void consumer_init(celix_bundle_context_t *context, consumer_t *consumer) {
// register callback function service
    consumer->context = context;
    consumer->service.handle = consumer;
    consumer->service.callback = my_service_callback;
    consumer->svcId = celix_bundleContext_registerService(context, &consumer->service, MY_SERVICE_CALLBACK_SERVICE_NAME, MY_SERVICE_CALLBACK_SERVICE_VERSION, NULL);

//use target service
    celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
    opts.filter.serviceName = MY_SERVICE_NAME;
    celix_bundleContext_useServiceWithOptions(context, &opts, consumer, useService);
}

@pnoltes
Do you have any suggestions for the design of callback function?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions