[opensource-dev] Client Plugin System Design

Morgaine morgaine.dinova at googlemail.com
Mon Mar 8 00:22:23 PST 2010


Ricky, I'm not sure even where to begin, because your proposed solution is
simply direct embedding of one or more scripting language systems into the
address space of the viewer.  This ignores every single advantage of
process-separated plugins and replaces them with a catalog of problems that
could fill a book.

Direct embedding is what everyone does when they want a simplistic
sequential scripting facility for configuring or testing a host
application.  We haven't proposed it ourselves not because we've had a brain
lapse and can't see the simplest solution, but because the simplest solution
is a complete disaster when misapplied as a general scripting facility for
user-written extensions.  I regularly embed Lua into host programs your way,
but using that approach here would have so many disadvantages and so few
advantages that I have avoided it intentionally.

I'll try to summarize some key issues for you, although this will leave much
unsaid.  Let's call the conventional approach Direct Language Embedding, or
*DLE* for brevity.


   - Unlike process-based plugins, DLE normally provides no concurrent
   execution.  This means that if you want your plugins to execute concurrently
   you will have to implement either multitasking or threading in the viewer,
   both of which involve shared-state multiprogramming.


   - Shared-state multiprogramming is hard and error-prone because of race
   conditions, non-determinacy, atomicity and locking, transactional integrity,
   as well as deadlock, livelock, and various other highly unfriendly
   critters.  I'm not going to justify this further here as it would take too
   long, but I did my research in this area, and anyone who thinks it's not
   hard needs to study the topic another 10 years.  A reliable solution
   requires operating system quality design, so this job really needs to be
   left to the operating system,


   - Because shared-state multiprogramming is hard, the execution engine for
   client-side scripts would be large, complex, and very hard to get right.  It
   would be literally a multi-year effort to make it work correctly, and it
   would always be hanging by a thread because full-coverage testing is very
   difficult owing to state complexity and timing dependencies.


   - In contrast to DLE, process-based plugins would not require any
   multiprogramming in the viewer at all.  The C++ host side can be completely
   sequential in its processing of messages arriving from plugins, while the
   plugins themselves would run with full concurrency courtesy of the operating
   system.  This makes for simple, reliable, and determinist viewer code.


   - DLE would add the entire code and data space of each embedded language
   runtime into the viewer's address space.  This is bloat with a capital 'B',
   and because virtually every language has a different embedding mechanism
   into the hosting C++ code, there will be very little code reuse.
   Application stability is inversely proportional to application size, so
   bloating the viewer is asking for trouble, not to mention large maintenance
   costs.


   - Because of the preceding issue, in practice DLE will never allow more
   than one or two different language runtimes to be integrated into the
   viewer, and hence a language-agnostic approach to client-side scripting is
   effectively barred.


   - Without language agnosticism, a small proportion of users will be
   favored by the chosen language or runtime while a larger proportion will be
   disadvantaged and their primary skills will not be harnessed.  This is a
   waste of people's skills, time and productivity.


   - Without language agnosticism, you are stuck with the facilities
   provided by the chosen languages or runtime.  If another language has a
   great feature that is highly appropriate for a particular plugin task, too
   bad, you cannot use it.  This is not very empowering, and it's an
   unnecessary restriction.


   - With DLE, the language runtimes would be directly *linked* with the
   viewer code, and hence licensing restrictions would come into play.  This
   would add a whole new rat's nest of problems and effectively make embedding
   of many useful languages impossible, whereas the license barrier provided by
   sockets avoids this issue entirely.


   - Because all plugins run in the address space of the viewer in DLE, the
   opportunity for programs to interfere with each other or with the viewer is
   large.  The viewer's stability drops to that of its least stable plugin.


   - With DLE, security is a nightmare.  Every 3rd party plugin can
   potentially alter something in the viewer unless it runs sandboxed, but if
   it runs sandboxed then it cannot provide much (or any) local interfacing, so
   the power of such plugins is weak.  Fast C/C++ plugins are almost out of the
   question for this reason.


   - Even if low inherent power is accepted so that sandboxing can provide
   security, this sandboxing can only ever be relatively weak *software
   sandboxing* because the plugin code and data lie within the address space
   of the host application.  This contrasts markedly with plugins implemented
   as system processes, which are backed by the much stronger guarantees of
   isolation provided by the system's hardware MMU, even if plugins are written
   in bare-metal C/C++.


   - Because DLE runs plugins as part of the host application, the standard
   system tools for working with processes cannot easily be used.  Even basic
   facilities such as killing a script would have to be implemented from
   scratch within the multiprogramming environment, instead of using the
   existing operating system kill command.  The same applies to profiling,
   performance monitoring, debugging, optimizing, and so on --- all are much
   more difficult within the host application, or not even viable.  (An example
   of this problem is provided by SL's Mono subsystem, which *still* has no
   means of unloading sim-side Mono assemblies after script termination, simply
   because the unload facility was never written.)


   - DLE requires that user programming of plugins be done in an environment
   of special calls to the host application so that plugins can cooperate
   properly with the host's code, and this has to be done very carefully to
   avoid damaging the host.  It is very distant from the simplicity and safety
   of programming external processes, which can use stock textbook styles and
   the normal libraries for the language.  DLE really needs client-side plugin
   programmers to be relative experts, while process-based plugins are safe
   enough for a much larger and less expert audience to program.


This list of negatives could easily grow a lot longer, but to bring it to a
close I'll just mention that DLE has some advantages too, but they are
remarkably few in number.

The most important one is that the interface to viewer facilities can be
very thin and hence fast.  For example, if the purpose of a plugin is to
render fast GL graphics then DLE is a good approach, albeit for single
plugins only.  In addition, as long as no concurrent execution is required,
implementing DLE can be done in under a day (at least for Lua), plus a few
weeks for defining API functions.  It's really that simple.

However, the vast majority of plugin applications do not require bare metal
speed, and what's more, concurrent execution of plugins is a mandatory
requirement.  This makes the tradeoff of pros versus cons weigh massively
against the DLE approach for our application.


Morgaine.


PS. With regards to "Networking code in every plugin just to connect to the
client", networking is made available by the operating system to every
process through system calls or system subroutines, ie. the thinnest
interface possible.  There is no bloat or overhead involved.  Particular
languages sometimes pretty up the system interface a little, but these
bindings do not normally introduce any significant overhead. Throughput and
latency of socket communications is not a significant issue either --- I've
measured them in an environment which emulated this design pattern, and the
level of performance might even suit some rendering tasks.






======================================

On Mon, Mar 8, 2010 at 2:19 AM, Ricky <kf6kjg at gmail.com> wrote:

> So far, barring any LL concepts, we have (as far as I know so far!) two
> designs of plugin system:
> 1: Socket-based plugins - as suggested by Morgaine.
> 2: D-Bus or similar existing IPC tool.
> 3: C++ Dynamically Shared Objects - my suggestion.
>
> Morgaine's design has a couple advantages that I can think of: Namely, a
> distinct lawyer-approved separation of code allowing the plugins to be
> released under different licenses, and a fairly stable target to design
> around, meaning the socket API wouldn't typically change a lot across
> releases, plugins also could be built into other external packages that
> interface with the socket API to provide massive amounts of integration.
> However, it's disadvantages come along: Networking code in every plugin
> just to connect to the client, separate processes that have to be launched
> by something - presumably the client code, but not always...
>
> I'm not too sure about the D-Bus design, there hasn't been much discussion,
> other than it seems a lot like Morgaine's suggestion to me, just an
> available existent re-usable tool.  That can be a good thing, as we wouldn't
> have to worry about the protocol, it would be already done.
>
> The last option is one I've seen references to across the 'net.  All that
> would be written in the client software is one or more Interface Objects
> (those familiar with polymorphic design are very familiar with these,) and
> then a list or two of these for different tasks.  For instance, one list
> could be for those that have registered themselves as Dynamic Script
> interpreters.  These Interfaces would then be implemented by all plugins,
> depending on what tasks the plugin was spec'd to handle.  Plugins would then
> be located in the plugins directory in the main install, and/or in a plugins
> directory under each profile.  They would be loaded automatically upon
> startup of the client, and their functions would be called upon event.  The
> event could be as simple as the last step in the render/update loops, or as
> detailed as when the user clicks or touches a keyboard button.  They also
> are given access to various commands they can call in the client.  The more
> the merrier.
>   The advantages are that these marry right up to the client giving
> full-speed access, along with the ability to get into the nitty-gritty of
> the client's underpinnings.  Also an easier way to code for a lot of
> programmers (like myself) who, while having a passing familiarity with
> networking code, are not terribly interested in writing plugins as clients
> to the client....
> Disadvantages are a potential lock-in to the client's license, although I'm
> not sure about that, and the API could be easily changed while patching some
> other part of the client code.
>
> Each has it's pro's and con's.  And my listing of such is by no means
> comprehensive!  I want to see this hashed out and the best over-all design
> get implemented: Client capabilities would soar fast, and be done in a very
> modular way improving SL massively.
>
> Ricky
> Cron Stardust
>
> _______________________________________________
> Policies and (un)subscribe information available here:
> http://wiki.secondlife.com/wiki/OpenSource-Dev
> Please read the policies before posting to keep unmoderated posting
> privileges
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.secondlife.com/pipermail/opensource-dev/attachments/20100308/e9ef401b/attachment.htm 


More information about the opensource-dev mailing list