In Licq 2 the plan is to use events as the primary way for plugins to 
interchange data. This document describes how this is (will be) done.

All files referenced below can be accessed by prepending the path with 
http://trac.licq.org/browser/branches/erijo-dev/licq/ or checking it out with 
svn co http://svn.licq.org/svn/branches/erijo-dev/licq.


Event

An event has a name (e.g. user-change-status, recv-user-message), a way to 
identify the sender (plugin id), a "globally" unique id and any number of 
properties (e.g. user-id [string], new-status [int]). A property is a 
name/value pair where the value can be of any data type.

The properties hold all data and e.g. an user-change-status event would be 
required to have at least two properties: user-id and new-status. The 
required part is not enforced by the compiler, only by documentation. This 
means it's easier to make misstakes when developing, but on the other hand we 
get a flexible system where plugins can send any type of events without the 
daemon needing an update.

Please take a look at the draft interface licq/interface/event.h 
(implementation in src/event/). With this interface, a plugin would create an 
event like this:

IEventPtr event = IEvent::create(myPluginId, "user-change-status");
event->setProperty("user-id", 1234567);
event->setProperty("new-status", TUserStatus::ONLINE);
// send the event to somebody

And the receiver would do:
int userId;
TUserStatus newStatus;
if (event->getProperty("user-id", &userId)
    && event->getProperty("new-status", &newStatus))
{
   std::cout << "User " << userId << ", status " << newStatus << "\n";
}


Event queue

Upon creation, every plugin gets their own (FIFO) event queue (interface: 
licq/interface/eventqueue.h, implementation: src/event/). This queue is used 
by the daemon to send events to the plugin. The daemon pushes an event to the 
queue and writes a byte to a pipe. The plugin can select() on the other side 
of the pipe and pop events as they arrive. By using a file descriptor 
(instead of thread conditions or similar) it's easy to integrate it with e.g. 
Qt's event loop.


Event loop

To aid plugin developers, an event loop (licq/eventloop.h) has been created. 
The event loop constructor takes the event queue as its first argument and a 
pointer to an event handler as its second. When an event is received from the 
daemon, the loop calls IEventHandler::onEvent() passing the event as an 
argument.

In addition, it's also possible to add other file descriptors (e.g. sockets) 
that the event loop should watch for activity. For most (all?) plugins this 
will be enough and they shouldn't need to use threads. The event loop also 
supports timers.


Event dispatcher

To further aid plugin developers, we have an event dispatcher (interface: 
licq/eventdispatcher.h, implementation: src/event/). This emulates the 
behavior from the old proposal (as seen on the wiki) where plugins register 
callbacks with the daemon. In this proposal, this is instead kept within the 
plugin itself. An instance of TEventDispatcher is passed as the second 
argument to the event loop and the plugin can use the instance to dispatch 
events to different handlers.


For an example on how event (queue|loop|dispatcher) works together, take a 
look at src/daemon.cpp and ../testplugin/src/plugin.cpp. You can also compile 
it and run it to get some nice output :)
# svn co http://svn.licq.org/svn/branches/erijo-dev
# cd erijo-dev && mkdir build && cd build
# cmake .. && make
# licq/src/licq testplugin/src 


Event pipeline

For a plugin to receive events, it has to register with the daemon. E.g.: "I'm 
interested in the 'user-change-status' event, and since I'm such an important 
plugin and would like to receive it first, give me priority 1."

The daemon will order all plugins interested in event X in a queue based on 
their priority. When an event arrives, the plugin with the highest priority 
will get it first. When it is done, it is passed on to the next plugin and so 
on. This way, an encryption plugin gets a chance to decrypt the message 
before it is passed on to the history plugin and then the gui. When the last 
plugin in the chain is done with the event, it is sent back to the sender as 
a way to ack the event.


The question now is, what do you think about this proposal? I'm especially 
interested in comments on the event pipeline. Is this a good way to do 
things? What happens if a plugin crashes? Or simply drops an event? Should 
there be a timeout?

Should it be possible to "broadcast" an event to all plugins at the same time? 

What about pointers in event properties? Should we allow this or should they 
be value only?

When a plugin sends an event, does it send it to the daemon and let it decide 
what to do with it, inform the daemon that "this event should be sent to 
protocol plugin Y" or does it send it directly to plugin Y?

What about all the stuff I forgot to mention? :)

Let the comment flood start...

// Erik

-- 
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Erik Johansson
http://ejohansson.se

Attachment: pgpnfP7UQmhi9.pgp
Description: PGP signature

Reply via email to