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
pgpnfP7UQmhi9.pgp
Description: PGP signature
