core/interfaces/IForwardSys.h
changeset 183 78488edd8873
parent 180 fe406c5f2aeb
child 185 5e0987d38ee0
     1.1 --- a/core/interfaces/IForwardSys.h	Sun Nov 12 02:27:27 2006 +0000
     1.2 +++ b/core/interfaces/IForwardSys.h	Sun Nov 12 09:51:39 2006 +0000
     1.3 @@ -142,7 +142,7 @@
     1.4  		virtual int PushArray(cell_t *inarray, 
     1.5  								unsigned int cells, 
     1.6  								cell_t **phys_addr, 
     1.7 -								int flags=SMFUNC_COPYBACK_NONE) =0;
     1.8 +								int flags=0) =0;
     1.9  	};
    1.10  
    1.11  
    1.12 @@ -307,48 +307,33 @@
    1.13   *
    1.14   *  Note that while #2,3,4 could be added to AMX Mod X, the real binding property is #1, which makes the system
    1.15   * object oriented, rather than AMX Mod X, which hides the objects behind static functions.  It is entirely a design
    1.16 - * issue, rather than a usability one.  The interesting part is when it gets to implementation, which is when the 
    1.17 - * problems for SourceMod arise.  Specifically, the implementation is easier in GENERAL -- the tough part is the oddball
    1.18 - * cases.
    1.19 + * issue, rather than a usability one.  The interesting part is when it gets to implementation, which has to cache
    1.20 + * parameter pushing until execution.  Without this, multiple function calls can be started across one plugin, which
    1.21 + * will result in heap corruption given SourcePawn's implementation.
    1.22   *
    1.23 - * Observe the calling process:
    1.24 - * - Each parameter is pushed using the ICallable interface.  For each parameter:
    1.25 - *  - For each function in the collection, the parameter is processed and pushed.
    1.26 + * Observe the new calling process:
    1.27 + * - Each parameter is pushed into a local cache using the ICallable interface.
    1.28   * - For each function in the collection:
    1.29 + *  - Each parameter is decoded and -pushed into the function.
    1.30   *  - The call is made.
    1.31 - *  - Copy backs are performed.
    1.32   * - Return
    1.33   *
    1.34 - *  Astute readers will note the problems - the parameters are processed individually for each push,
    1.35 - * rather than for each call.  This means:
    1.36 + *  Astute readers will note the (minor) problems:
    1.37   * 1) More memory is used.  Specifically, rather than N params of memory, you now have N params * M plugins.
    1.38 - *    This is because, again, parameters are processed per function object, per-push, before the call.
    1.39 + *    This is because, again, parameters are cached both per-function and per-forward.
    1.40   * 2) There are slightly more calls going around: one extra call for each parameter, since each push is manual.
    1.41 - * 3) Copybacks won't work as expected.  
    1.42   *
    1.43 - *  Number 3 is hard to see.  For example, say a forward has two functions, and an array is pushed with copyback.
    1.44 - * The array gets pushed and copied into each plugin.  When the call is made, each plugin now has separate copies of 
    1.45 - * the array.  When the copyback is performed, it gets mirrored to the originall address, but not to the next plugin!
    1.46 -
    1.47 - *  Implementing this is "difficult."  To be re-entrant, an IPluginFunction can't tell you anything
    1.48 - * about its copybacks after it has returned, because its internal states were reset long ago.
    1.49 - * 
    1.50 - *  In order to implement this feature, IPluginFunction::Execute function now lets you pass a listener in.
    1.51 - * This listener is notified each time an array parameter is about to be copied back.  Specifically, the forward 
    1.52 - * uses this to detect when it needs to push a new parameter out to the next plugin.  When the forward gets the 
    1.53 - * call back, it detects whether there is another plugin in the queue.  If there is, it grabs the address it will
    1.54 - * be using for the same arrays, and specifies it as the new copy-back point.  If no plugins are left, it allows
    1.55 - * the copyback to chain up to the original address.
    1.56 - * 
    1.57 - *  This wonderful little hack is somewhat reminiscent of how SourceHook parameter rewrite chains work.  It seems 
    1.58 - * ugly at first, but it is actually the correct design pattern to apply to an otherwise awful problem.
    1.59 - *
    1.60 - *  Note that there are other solutions to this that aren't based on a visitor-like pattern.  For example, the Function
    1.61 - * class could expose a "set new original address" function.  Then after arrays are pushed, the arrays are re-browsed,
    1.62 - * and function #1 gets function #2's original address, function #2 gets function #3's original address, et cetera.
    1.63 - * This extra browse step is a bit less efficient though, since the "visitor" method implies only taking action when
    1.64 - * necessary.  Furthermore, it would require exposing such a "set address" function, which should fire a red flag 
    1.65 - * that the API is doing something it shouldn't (namely, exposing the direct setting of very internal properties).
    1.66 + * HISTORICAL NOTES:
    1.67 + *  There used to be a # about copy backs.  
    1.68 + *  Note that originally, the Forward implementation was a thin wrapper around IForwards.  It did not cache pushes,
    1.69 + * and instead immediately fired them to each internal plugin.  This was to allow users to know that pointers would 
    1.70 + * be immediately resolved.  Unfortunately, this became extremely burdensome on the API and exposed many problems,
    1.71 + * the major (and breaking) one was that two separate Function objects cannot be in a calling process on the same 
    1.72 + * plugin at once. (:TODO: perhaps prevent that in the IPlugin object?) This is because heap functions lose their order
    1.73 + * and become impossible to re-arrange without some global heap tracking mechanis.  It also made iterative copy backs 
    1.74 + * for arrays/references overwhelmingly complex, since each plugin had to have its memory back-patched for each copy.
    1.75 + *  Therefore, this was scrapped for cached parameters (current implementation), which is the implementation AMX Mod X 
    1.76 + * uses.  It is both faster and works better.
    1.77   */
    1.78  
    1.79  #endif //_INCLUDE_SOURCEMOD_FORWARDINTERFACE_H_