Extending Functionality: gen_server_ext

Jay Nelson <>
Thu Mar 20 15:05:11 CET 2003


Chris and Vlad discussed:

 > Extensions to gen_server to support "behavers" and pseudo
 > inheritance call chains ...

This seems an interesting approach to reusability or extension
of functionality.  I haven't thought about a specific problem that
it would apply to but I have some general thoughts about a
structural architecture.  I guess these features could be useful
in a situation where you have two machines, one having more
functionality than another (but the lesser functionality reused)
or when users can login with differing levels of privileged access.
I'm sure there are many other applications, the main issue being
whether you want dynamic modification of behaviour or a static
application structure.

Chained processes - deep inheritance

This seems to be the thrust of the initial thinking.  It mirrors the
deeper inheritance structures used in OO.  The bottom of a
chain is the base behaviour in a gen_server, and refinements
are placed in front of the base like popcorn strung on a thread.

Issues:

1) Explicit interface vs. implicit interface: use explicit delegation
and fail if message unhandled, or pass unhandled requests down
the chain until they are handled and then back up the chain

2) Allow installation of handlers the way gen_event does to get
situationally adaptive behaviour?

3) Multiple inheritance: what if two base chains are joined by
a single gen_server?  Not allowed or deterministic searching for
unhandled messages to be passed to base chain.

4) If the chain is deep, there could be a lot of wasted message
passing searching for the right handler.  You may want to use
memoization to cache the found handler in an ets table the first
time it is used.  Even in a dynamic scenario, you could de-momoize
if a new handler is installed.

5) Death of a single process can interrupt much computation.

6) Single process can only participate in one chain.  A separate
instance is needed to participate in a different chain.


Interface amalgam - shallow inheritance

Create a single Amalgam process (similar to supervisor) that combines
the desired interfaces from several gen_servers.  gen_servers
are all on equal footing, supervisor created using an ordering
of the gen_servers which may or may not be modified after
creation (Amalgam interface specification).

1) Explicit interface vs. implicit interface:  Explicit works the same
way as before -- unhandled messages fail.  Implicit relies on the
ordering Amalgam interface specification when searching
for message handler.

2) Installation of handlers should not occur on gen_servers, but by
modification of Amalgam interface specification.

3) Inheritance is explicitly defined -- no ambiguity or restrictions.

4) Message passing only one level deep:  Amalgam <--> gen_server

5) Death of a single process only eliminates some features; it may
be anticipated and a less capable process answers, dynamic relaunching
(ala supervisor behaviour) reinstalls.

6) A single process can participate in any number of Amalgams.


The first approach cannot model arbitrary computations as easily.
The second affords more control over inheritance, and allows
simultaneous reuse.  Here is an example of an Amalgam:

amalgam:start_link( Server, Workers )

Server -> name, pid
Workers -> Ordered list of tuples

amalgam:start_link( Window,
     [ gen_window, {File_menu, Gen_menu}, {Edit_menu, Gen_menu},
                           {My_address_form, Gen_address_form, Gen_form},
                           {Ok_cancel_buttons}]

The amalgam:init() function would start all the processes and then build
a call table using left to right ordering of list elements.  If a process in a
tuple goes down, the others in that tuple may be used as backup handlers.
If all processes representing one element of the list go down, that bundle
of functions is not available until at least one process is restarted.

You may still want an explicit chain, or may be forced to use an explicit
chain if the network topology does not allow the Amalgam to directly access
a process.  A generalized approach would combine the two techniques,
but my programming style would favor single level Amalgams because it
is a clearer expression of the computation desired.  The same processes
could participate in two Amalgams, but with a different ordering to get a
different behaviour.  A person logging in could be handled by collecting
a set of privileges from the database, then dynamically constructing an
Amalgam that presents only the interfaces that they are allowed to access.
Refusing to propagate a missing handler can be a feature.

The Amalgam could be combined with a gen_fsm to dynamically add or
subtract processes from the call table based on the current state of the
FSM.  This is a reasonably easy model to understand that gives rise to
a complex network of process behaviour that is manageable and fault-
tolerant.

jay

                                               




More information about the erlang-questions mailing list