Tuesday, August 7, 2012

OFlux Plugin Away

Plugin architectures allow for optional functionality to be added to a simple core. The core or kernel of the program provides the bones on which the rest of the program is built. It is not necessary for the kernel to rely on the parts added ontop (the plugins) -- in fact it would be bad if that happened. Plugins can depend on more primitive plugins to accomplish their jobs.

Unlike modules which are re-usable as multiple instances within the program, plugins are intended to only either be there (once) or not there at all.  Most web servers accept plugins (e.g. for dynamic scripting language execution or CGIs) which extend the functionality of the core web server program (which parses HTTP headers etc).  This can be very a very powerful way of organizing server software generally.

When shipping software to customers with differing needs, plugins allow the the end user to customize the code that they are running in a controlled way.  A plugin which is not running is one which does not adversely affect performance, and cannot cause the program to crash.  More critically, turning off functionality which is only suitable in non-production environments (so it never runs on a live system with customers using it) is great safety feature.


My First Plugin



In order to prepare the way for a plugin Plug, the kernel.flux program you write first needs to have made available some abstract nodes for the plugin to hook into:

 
 node S () => (int a);
 node A (int a) => ...;
 node N (int a) => ();

 source S -> A;
 A = N;


By default all of the outputs of node S are consumed by concrete node (meaning it has a C++ implementation function) N via abstract node A (its only purpose is to provide a place for the new plugin to hook-in). Now we can write a new plugin which routes away some cases from the kernel flow to handle them in using their own special code:

 
 include kernel.flux

 plugin Plug
  begin
  external node A (int a) => ...;

  condition isZero (int a) => bool;
  node NForZero (int a) => ();

  A : [isZero] = NForZero;
  end


On the C++ side there is a Plug namespace which holds all of the symbols for the plugin, and it is compiled into a libPlug.so dynamic shared object (loaded dynamically at run time).  The decision to load a particular plugin is based on configuration (by default symbolic links to XML files such as Plug.xml in a particular directory), so it is easy to turn them on and off.  The content of a plugin XML file describes the list of required plugins that need to be loaded first, and how it is that the program flow is patched/modified by the new plugin code.

The effect of the Plug plugin is to divert the flow to node NForZero when the output a of S has isZero(a) evaluate to true. This is really a conditional augmentation of the existing kernel flow:


The dot output from compiling the plugin (using the -p compiler option), shows what the plugin added to the flow it is built on top of.  Had Plug depended on other plugins, those would also be highlighted in red colored boxes (and each .flux file would need to have include statements at the top of Plug.flux).

Had the conditional isZero been replaced by a *, the new route NForZero would become the new default (N being unreachable after that).  This is a way for the plugin to over-ride existing functionality in the kernel program.

Another Possibility is to add a concurrent successor node to the flow using the special &= operator which causes a second node (in addition to N) to run on every output from S:


 Node M (int a) => ();
 A &= M;

Summary


Plugins provide a method of extending a program with optional functionality.  In the case of OFlux plugins the functionality can be new parts of the flow which augment a pre-existing flow.  Plugins can use (and therefore depend on) the functionality of other plugins.  This way of coding away from the core with ever more specialized code with finer grain concerns is very useful.  It has many benefits such as reducing compile time (of the plugin component), enforcing dependencies, reducing exposure to bugs and increasing performance (by not running code you do not require).

No comments:

Post a Comment


Follow Mark on GitHub