fenfire-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Fenfire-dev] PEG: functional futureproof api


From: Tuomas Lukka
Subject: [Fenfire-dev] PEG: functional futureproof api
Date: Fri, 5 Sep 2003 12:48:38 +0300
User-agent: Mutt/1.5.4i

=============================================================
PEG functional_futureproof_api--tjl: 
=============================================================

:Author:   Tuomas J. Lukka
:Last-Modified: $Date: 2003/09/03 11:19:15 $
:Revision: $Revision: 1.1 $
:Status:   Current

Functions and caching are here to stay with us. 
However, the caching is currently pretty nasty for the programmer
and requires active thinking, especially in the case of super-lazy
functions.

This PEG provides a future-proof API for handling functions and caching
cleanly.

Issues
======

- How do we specify which functions need to be run in the OpenGL thread?
  How general should we make this ability?

  One important point is that this is a property of the **function**, not
  its instantiation.

  The reasonable alternatives, given this, are 
  
  1) to have separate interfaces
     for the per-function properties and for the ``Function`` 
     to implement them.

     Pros:
        - simple
     Cons:
        - profusion of interfaces

  2) to add, in the ``Functional`` api a Hints object and "getHints"
     or something

     Pros:
        - flexible
     Cons:
        - slightly klunky

  RESOLUTION: 2). this is pretty much as good as we can get. If the Hints
  object is specified flexibly, can be done without loss of generality
  (i.e. can give Id to specific sets of Background objects).



- How should we give hints to the Functional API about which functions
  are slow?

  This may be a property of the function, or a property of the instance.

  RESOLUTION: For now, allow only as part of Hints.


- Do we want/need to specify that a function uses its parameter functions once,
  maybe once, several times?

  RESOLUTION: Not yet. So far, no use for such information has been given.

- Do we want/need to specify that a function uses its parameter functions 
  always with the same parameter that it gets?

  RESOLUTION: Not yet. So far, no use for such information has been given.

- What about slow tasks that want to use OpenGL, such as Mipzip generation?
  Paper generation is much faster.

  RESOLUTION: Nothing for now - they should be run in a thread, and start
  new processes. This is even slower, but OTOH will crash less likely
  (there has been some stability problems when generating lots of mipzips
  that seem like a problem in zlib or NVIDIA drivers ((yeah, right ;))).

- Should we try to do more through reflection, reading parameters &c
  from finished objects? We might be able to map::

    Function node1 = new G("X");
    Function node2 = new F("X", node1);

  to the Functional objects?

  RESOLUTION: Not yet. We can always create a new tag interface for ``Function``
  objects that obey the special restrictions associated with that,
  if any are needed.

- How should we specify placeholder objects for super-lazy caches?
  Placeholders can be on two levels, either the function level or instance 
level.

  RESOLUTION: For now, only function level. Hints object will have a 
setPlaceholder
  call.



Changes
=======

GL- and nonGLfunctions
----------------------

In the following, we shall refer to functions that have to be calculated
in the OpenGL thread (i.e. ones that create Vobs) as GLfunctions,
and others as nonGLfunctions.

Loosening purity
----------------

The biggest API change is not an API change *per se* but a semantic change.
So far, ``PureFunction`` and ``PureNodeFunction`` have been required to 
be pure, interface-wise, i.e. their f() *method* may not return values
depending on anything other than the parameter.

To make handling functions convenient, we shall relax this requirement:
a ``PureFunction`` must be pure **iff** all its constructor parameters
that implement ``Function`` are pure, and likewise for ``PureNodeFunction``s.

Allowing exceptions
-------------------

Any object implementing a ``Function`` interface (except for the special 
Functional
API classes) shall not leak memory or cause crashes or improper results if any 
of
its constructor parameters that implement the ``Function`` API throw an 
``Error``.
The error must pass through the ``Function`` invocation to the caller.

This change is important for the case where a fast GLfunction of a slow 
nonGLfunction
needs to be calculated: if the slow value has not been cached, it's better
to schedule its calculation in a background thread and throw an exception. That 
way,
the OpenGL thread is free for other operations during the calculation.

This allows us to emulate some sort of continuation mechanism for Java.

Creating functions
------------------

Functions shall no more be created directly (it's allowed but will not be 
cacheable &c).
Instead, a reflective Function creation API shall be used:

The Functional API
------------------

    interface Functional {
        /** Hints about a Function class.
         * Created using HintsMaker.
         * An empty interface in order to be unmodifiable.
         */
        interface Hints {
        }
        /** An interface for creating Hints objects.
         */
        interface HintsMaker {
            /** This function must be run in a background object
             * of the given group if it's not run directly.
             * This is useful for using Libvob OpenGL, since 
             * OpenGL objects should only be handled in one thread.
             * Default: null.
             */
            void setBackgroundGroup(Object id);

            /** Whether this function usually consumes considerable time
             * to generate its output, given all its inputs.
             * Evaluations of functions given as parameters to this function
             * are not counted.
             * Default: not slow.
             */
            void setSlow(boolean isSlow);

            /** Set the placeholder object to be returned if the function value
             * is not ready yet.
             * Default: null.
             */
            void setPlaceholder(Object o);

            /** Create the Hints object.
             */
            Hints make();
        }

        /** A node in the functional calculation DAG.
         */
        interface Node {
            /** Get a function entry point for this node.
             */
            Function getCallableFunction();
        }
        /** Create a new node in the DAG.
         * @param id An identifier for the node. Used for determining caching 
&c.
         *           Should be stable between invocations.
         * @param functionClass The class of which the Function (or 
NodeFunction)
         *              object should 
         *              be created.
         * @param parameters The parameters for the constructor of the class.
         *              These may contain Node objects, which will be converted
         *              to functions or nodefunctions as appropriate.
         */
        Node createNode(
            Object id,
            Class functionClass,
            Object[] parameters
            );
    }

Example about using the API
---------------------------

First, define two functions::

    public class G implements PureFunction {
    
        public G(Object bla) {
        }
        public Object f(Object o) {
            ...
        }
    }

    public class F implements PureFunction {
        static public Functional.Hints functionalHints;
        static {
            // F needs to be run in the OpenGL thread
            Functional.HintsMaker maker = 
                new Functional.HintsMaker();
            maker.setBackgroundGroup("OPENGL");
            functionalHints = maker.make();
        }
    
        public F(Object bla, Function g) {
        }
        public Object f(Object o) {
            ...
        }
    }

And construction the functions::

    Functional.Node node1 = Functional.createNode(
        "N1",
        G.class, 
        new Object[] {"X"});
    Functional.Node node2 = Functional.createNode(
        "N2",
        F.class,
        new Object[] {"Y", node1});

This constructor is "equivalent" to::

    Function node1 = new G("X");
    Function node2 = new F("X", node1);

except that the result may be cached.








reply via email to

[Prev in Thread] Current Thread [Next in Thread]