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_