generic message framework

Name

ggNewChannel, ggDelChannel, ggSetController, ggObserve, ggDelObserver, ggBroadcast, ggControl : generic message framework

Synopsis

#include <ggi/gg.h>

struct gg_channel;

typedef int (ggfunc_channel_control_cb)(void *arg, uint32_t ctl, void *data);

typedef int (ggfunc_channel_observe_cb)(void *arg, uint32_t msg, void *data);

struct gg_channel * ggNewChannel(void *arg, ggfunc_channel_control_cb *cb);

void ggDelChannel(struct gg_channel *channel);

int ggSetController(struct gg_channel *channel, ggfunc_channel_control_cb *cb);

struct gg_observer * ggObserve(struct gg_channel *channel,
                               ggfunc_channel_observe_cb *cb,
                               void *arg);

void ggDelObserver(struct gg_observer *observer);

void ggBroadcast(struct gg_channel *channel, uint32_t msg, void *data);

int ggControl(struct gg_channel *channel, uint32_t ctl, void *data);

Description

This set of functions provide a simple message-passing infrastructure, consisting of a general observer pattern and command dispatching implementation. It is primary intended to support the light-weight cooperation model between libraries implemented as part as the libgg-api framework. It can also very well be useful to any application writer.

struct gg_channel defines a channel on which observers can be registered. An observer is simply an opaque value and a callback receiving that value as first argument, a flag, and an opaque event-specific message. The idea is that if you know the channel you're listening on, you know the semantics behind the flag and the message. When the channel is triggered, all observers' callbacks will be fired. Specific control commands can also be sent to a channel, pretty much in the way fcntl(2) or sysctl(3) work.

ggNewChannel creates a new communication channel. The arg parameter will be passed as the first argument of controllers callback. Generally, this will be a pointer to the entity that is managed through this specific channel. The cb callback is the primary controller that will be tried when ggControl is called on that channel. If this callback is 'NULL', or if it returns 'GGI_ENOFUNC', then the secondary controller (set by ggSetController) will be tried. This allows the creator of the channel to define a set of non-overridable controls, and let some other code (for example a module) install an additional set of specific controls.

ggSetController installs cb as the secondary controller on a channel, as discussed above. If one was already set, it is replaced.

ggDelChannel frees a channel as well as observers still registered on it. If at least one is left, then there is probably a logical error in the observer code, since it must already have been notified somehow of the channel going down, and unregistered all callbacks before.

ggObserve registers a new observer on the channel. The callback will be fired when an event is broadcasted on that channel. The arg parameter is a user value that will be passed as the first argument of the callback.

ggDelObserver unregisters the given observer from its channel and frees it. Note that within a callback, an observer must not call ggDelObserver on itselt to unregister. Instead it must return a non-zero value.

ggBroadcast triggers all observers registered on the channel. msg and data will be passed to the observers' callback.

ggControl sends a specific control code identified by ctl to the channel. This triggers the channel controllers.

Return values

ggNewChannel returns a newly allocated channel, or 'NULL' on error.

ggSetController returns '0' or a negative error code on failure.

ggObserve returns a newly constructed observer hook. Normally, the caller will have to keep a reference to it if he needs to call ggDelObserver later.

ggControl returns '0' or a negative error code on failure. The 'GGI_ENOFUNC' error code means that the requested control code was not recognized.

Example

#include <ggi/gg.h>
#include <stdio.h>

int update(void *o, uint32_t msg, void *d)
{
    printf("update called for observer %p, msg=%i, data=%p\n", o, f ,d);
    if (msg == 1) {
       return 1; /* unregister */
    }
    return 0;
}

int control(void *a, uint32_t ctl, void *s)
{
  switch(ctl) {
  case 0:
      printf("You say \"%s\", I say \"Hello\"!\n", );
      break;
  default:
      return GGI_NOFUNC;
  }
  
  return 0;
}

int main(void)
{
    struct gg_channel *chn;
    struct gg_observer *o1, *o2;

    chn = ggNewChannel(NULL, control);
    
    o1 = ggObserve(chn, update, (void*)1);
    o2 = ggObserve(chn, update, (void*)2);

    ggBroadcast(chn, 0, NULL);
    ggBroadcast(chn, 1, NULL);

    ggControl(chn, 0, "Goodbye");

    ggDelChannel(chn);

    return 0;
}