Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

register callback in one application to be retrieved in another

gcc 4.6.0 c89

I have type of client server application. The server some code in an event loop that will wait for an event from the client.

This is not a client server that will be using UDP/TCP sockets. But the client and server will be running on the same linux machine. I guess is like app1 talking to app2 running in the same server.

I know I need to use function pointers (callback) and I need to register the callbacks in client application. The server will wait for an event from the client and act accordingly.

So my design in the server would be something like this:

while(running) {
    switch(event) {
        case START_SDL:
        /* DO something */
        break;

        case DELETE_EDL:
        /* Do something */
        break;
    }
}

With this the server is running in the loop, waiting to receive events from the client. However, I am not sure how to get started.

Many thanks for any suggestions,

like image 863
ant2009 Avatar asked Apr 23 '11 18:04

ant2009


People also ask

What is registering a callback function?

Registering a callback function simply means that you are arranging for an external entity to call your function. It might happen at a later time, or it might happen straight away. A straightforward example is qsort .

What is callback invocation?

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.

When callback is executed?

A callback function is a function that occurs after some event has occurred. The reference in memory to the callback function is usually passed to another function. This allows the other function to execute the callback when it has completed its duties by using the language-specific syntax for executing a function.

What are the types of callback?

There are two types of callbacks, differing in how they control data flow at runtime: blocking callbacks (also known as synchronous callbacks or just callbacks) and deferred callbacks (also known as asynchronous callbacks).


2 Answers

You should use a worker thread that waits for events from the main thread and processes them. This is a long answer and to avoid making it longer I shall omit error-checking although it is against the sixth commandment.

The task structure and the queue

Make a structure that specifies a task. I will use the generic get_task and push_task functions. In a real example thread-safe queue of tasks should be used but this would uselessly complicate the answer. I only sketched this from old programs I had laying around.

struct task {
    /* function callback */
    void (*fun)(void *);
    /* parameter to pass to callback */
    void *arg;
};

Synchronization

Use a mutex to protect the task queue and a semaphore to signal there is work to be done. Please see what I wrote above in bold.

/* this has to be initialized in main */
sem_t sem;
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

The worker function

The worker function simply waits around and executes tasks when it is told.

static void *worker(void *arg)
{
    struct task t;

    /* detach */
    pthread_detach(pthread_self());

    /* loop forever */
    while (1) {
        /* block until we have work to do */
        sem_wait(&sem);

        /* we've got work to do */
        pthread_mutex_lock(&mtx);
        /* get the task */
        t = get_task();
        pthread_mutex_unlock(&mtx);

        /* we are safe now, nobody can touch t */
        /* execute the callback - here is your function pointer*/
        (*t.fun)(t.arg);
    }
    return NULL;
}

The main function

The role of the main function is to initialize stuff and to push tasks.

pthread_t t1;

/* initialize unnamed semaphore */
sem_init(&sem, 0, 0);

/* start worker thread */
if (0 != pthread_create(&t1, NULL, worker, NULL)) {
    perror("pthread_create");
    exit(1);
}

Pushing tasks

At this point the worker thread is waiting for tasks that you can push from main.

pthread_mutex_lock(&mtx);
push_task(my_task);
pthread_mutex_unlock(&mtx);

How is this server going to ever know the client is triggering events ? That is for you to decide, there are lots and lots of methods to do IPC on Unix. My suggestion would be to use a message queue.

Server message queue example

#define MSGSIZE 1024

int main()
{
    mqd_t mq;
    struct mq_attr attr;
    char message[MSGSIZE];
    int read_bytes;

    attr.mq_maxmsg = 10;
    attr.mq_msgsize = MSGSIZE;

    mq = mq_open("/queue", O_RDWR | O_CREAT, 0700, &attr);
    if ((mqd_t)-1 == mq) {
        perror("mq_open");
        exit(1);
    }

    while (1) {
        /* get message from queue */
        read_bytes = mq_receive(mq, message, MSGSIZE, NULL);
        if (-1 == read_bytes) {
            perror("mq_receive");
            exit(1);
        }

        /* do what you wish with the message */
    }
}

So in the "do what you wish" part you can call interpret the event type and push it for the worker. Sending the message from the client is trivially similar so I won't post that (if you really can't do it just ask).

These are all just (possibly broken) bits of one large puzzle. Your task is to assemble them into whatever you're building.

like image 136
cnicutar Avatar answered Oct 19 '22 10:10

cnicutar


Unless this is a homework assignment, instead of reinventing the wheel, I'd recommend using one of the many available IPC libraries around:

  • libassuan is very lightweight and secure, used between the GnuPG components.
  • libdbus, the standard (desktop-)IPC library on Unix.
  • any number of CORBA implementations, eg. MICO, TAO, or omniORB.
  • any number of MPI implementations, e.g. LAM (nowadays OpenMPI).
  • ...
like image 30
Marc Mutz - mmutz Avatar answered Oct 19 '22 11:10

Marc Mutz - mmutz