diff options
author | Mike Pall <mike> | 2011-11-14 14:15:57 +0100 |
---|---|---|
committer | Mike Pall <mike> | 2011-11-14 14:18:25 +0100 |
commit | 71d00a56dbab6c29c0346093dbe530d7b7608be4 (patch) | |
tree | 5e28e19b4d2f20168d5ee0e4fe500b1e2b233c1c /doc/ext_ffi_semantics.html | |
parent | e9eb4fdb4a08baaa2d9190187a6c38d5b3f8b091 (diff) | |
download | luajit2-71d00a56dbab6c29c0346093dbe530d7b7608be4.tar.gz |
FFI: Add callback support (for x86/x64).
Diffstat (limited to 'doc/ext_ffi_semantics.html')
-rw-r--r-- | doc/ext_ffi_semantics.html | 128 |
1 files changed, 125 insertions, 3 deletions
diff --git a/doc/ext_ffi_semantics.html b/doc/ext_ffi_semantics.html index 79f25510..7e140e27 100644 --- a/doc/ext_ffi_semantics.html +++ b/doc/ext_ffi_semantics.html @@ -297,10 +297,12 @@ arguments to C calls: <tr class="even"> <td class="convin">string</td><td class="convop">string data →</td><td class="convout"><tt>const char[]</tt></td></tr> <tr class="odd separate"> +<td class="convin">function</td><td class="convop"><a href="#callback">create callback</a> →</td><td class="convout">C function type</td></tr> +<tr class="even separate"> <td class="convin">table</td><td class="convop"><a href="#init_table">table initializer</a></td><td class="convout">Array</td></tr> -<tr class="even"> +<tr class="odd"> <td class="convin">table</td><td class="convop"><a href="#init_table">table initializer</a></td><td class="convout"><tt>struct</tt>/<tt>union</tt></td></tr> -<tr class="odd separate"> +<tr class="even separate"> <td class="convin">cdata</td><td class="convop">cdata payload →</td><td class="convout">C type</td></tr> </table> <p> @@ -821,6 +823,127 @@ cdata objects are indistinguishable from pointers returned by C functions (which is one of the reasons why the GC cannot follow them). </p> +<h2 id="callback">Callbacks</h2> +<p> +The LuaJIT FFI automatically generates special callback functions +whenever a Lua function is converted to a C function pointer. This +associates the generated callback function pointer with the C type +of the function pointer and the Lua function object (closure). +</p> +<p> +This can happen implicitly due to the usual conversions, e.g. when +passing a Lua function to a function pointer argument. Or you can use +<tt>ffi.cast()</tt> to explicitly cast a Lua function to a +C function pointer. +</p> +<p> +Currently only certain C function types can be used as callback +functions. Neither C vararg functions nor functions with +pass-by-value aggregate argument or result types are supported. There +are no restrictions for the kind of Lua functions that can be called +from the callback — no checks for the proper number of arguments +are made. The return value of the Lua function will be converted to the +result type and an error will be thrown for invalid conversions. +</p> +<p> +It's allowed to throw errors across a callback invocation, but it's not +advisable in general. Do this only if you know the C function, that +called the callback, copes with the forced stack unwinding and doesn't +leak resources. +</p> + +<h3 id="callback_resources">Callback resource handling</h3> +<p> +Callbacks take up resources — you can only have a limited number +of them at the same time (500 - 1000, depending on the +architecture). The associated Lua functions are anchored to prevent +garbage collection, too. +</p> +<p> +<b>Callbacks due to implicit conversions are permanent!</b> There is no +way to guess their lifetime, since the C side might store the +function pointer for later use (typical for GUI toolkits). The associated +resources cannot be reclaimed until termination: +</p> +<pre class="code"> +ffi.cdef[[ +typedef int (__stdcall *WNDENUMPROC)(void *hwnd, intptr_t l); +int EnumWindows(WNDENUMPROC func, intptr_t l); +]] + +-- Implicit conversion to a callback via function pointer argument. +local count = 0 +ffi.C.EnumWindows(function(hwnd, l) + count = count + 1 +end, 0) +-- The callback is permanent and its resources cannot be reclaimed! +-- Ok, so this may not be a problem, if you do this only once. +</pre> +<p> +Note: this example shows that you <em>must</em> properly declare +<tt>__stdcall</tt> callbacks on Windows/x86 systems. The calling +convention cannot be automatically detected, unlike for +<tt>__stdcall</tt> calls <em>to</em> Windows functions. +</p> +<p> +For some use cases it's necessary to free up the resources or to +dynamically redirect callbacks. Use an explicit cast to a +C function pointer and keep the resulting cdata object. Then use +the <a href="ext_ffi_api.html#callback_free"><tt>cb:free()</tt></a> +or <a href="ext_ffi_api.html#callback_set"><tt>cb:set()</tt></a> methods +on the cdata object: +</p> +<pre class="code"> +-- Explicitly convert to a callback via cast. +local count = 0 +local cb = ffi.cast("WNDENUMPROC", function(hwnd, l) + count = count + 1 +end) + +-- Pass it to a C function. +ffi.C.EnumWindows(cb, 0) +-- EnumWindows doesn't need the callback after it returns, so free it. + +cb:free() +-- The callback function pointer is no longer valid and its resources +-- will be reclaimed. The created Lua closure will be garbage collected. +</pre> + +<h3 id="callback_performance">Callback performance</h3> +<p> +<b>Callbacks are slow!</b> First, the C to Lua transition itself +has an unavoidable cost, similar to a <tt>lua_call()</tt> or +<tt>lua_pcall()</tt>. Argument and result marshalling add to that cost. +And finally, neither the C compiler nor LuaJIT can inline or +optimize across the language barrier and hoist repeated computations out +of a callback function. +</p> +<p> +Do not use callbacks for performance-sensitive work: e.g. consider a +numerical integration routine which takes a user-defined function to +integrate over. It's a bad idea to call a user-defined Lua function from +C code millions of times. The callback overhead will be absolutely +detrimental for performance. +</p> +<p> +It's considerably faster to write the numerical integration routine +itself in Lua — the JIT compiler will be able to inline the +user-defined function and optimize it together with its calling context, +with very competitive performance. +</p> +<p> +As a general guideline: <b>use callbacks only when you must</b>, because +of existing C APIs. E.g. callback performance is irrelevant for a +GUI application, which waits for user input most of the time, anyway. +</p> +<p> +For new designs <b>avoid push-style APIs</b> (C function repeatedly +calling a callback for each result). Instead <b>use pull-style APIs</b> +(call a C function repeatedly to get a new result). Calls from Lua +to C via the FFI are much faster than the other way round. Most well +designed libraries already use pull-style APIs (read/write, get/put). +</p> + <h2 id="clib">C Library Namespaces</h2> <p> A C library namespace is a special kind of object which allows @@ -1002,7 +1125,6 @@ Other missing features: <ul> <li>Bit operations for 64 bit types.</li> <li>Arithmetic for <tt>complex</tt> numbers.</li> -<li>Callbacks from C code to Lua functions.</li> <li>Passing structs by value to vararg C functions.</li> <li><a href="extensions.html#exceptions">C++ exception interoperability</a> does not extend to C functions called via the FFI, if the call is |