| 162 | | == Loose Ends == |
| | 162 | == Adjustor pools == |
| | 163 | |
| | 164 | A declaration for a "wrapper" callback looks like this: |
| | 165 | |
| | 166 | {{{ |
| | 167 | foreign import ccall safe "wrapper" |
| | 168 | mkDelegate :: IO () -> IO (FunPtr (IO ())) |
| | 169 | }}} |
| | 170 | |
| | 171 | To implement these, GHC normally generates a small piece of executable code at runtime, called an "adjustor". The purpose of these "wrapper" declarations is to generate a C-callable function pointer that executes Haskell code. |
| | 172 | |
| | 173 | Due to Apple's requirements for code-signing, the iOS kernel enforces a ban on self-modifying code. |
| | 174 | |
| | 175 | We solve this conundrum by pre-compiling a pool of functions, and allocating from it. Because the pool size for each wrapper is fixed, this creates the problem that the pool can run out. If this happens, the application will die with this message: |
| | 176 | |
| | 177 | {{{ |
| | 178 | HaskellDraw: internal error: createPooledAdjustor - adjustor pool 'Main_d1tU' is empty (capacity 32) |
| | 179 | }}} |
| | 180 | |
| | 181 | (The name of the module where the "wrapper" was declared appears before the underscore character.) |
| | 182 | |
| | 183 | Each "wrapper" declaration has its own pool, whose size defaults to 32. This means that at any one time, there can exist no more than 32 adjustors created by the defined wrapper constructor function (in this example, mkDelegate). '''Foreign.Ptr.freeHaskellFunPtr''' is the IO action to free an adjustor, and in this implementation this returns it to the pool. |
| | 184 | |
| | 185 | If the pool is too small for a given application, you can increase it by using a {-# POOLSIZE x #-} pragma, which must appear after the "wrapper" token. e.g. |
| | 186 | |
| | 187 | {{{ |
| | 188 | foreign import ccall safe "wrapper" {-# POOLSIZE 100 #-} |
| | 189 | mkDelegate :: IO () -> IO (FunPtr (IO ())) |
| | 190 | }}} |
| | 191 | |
| | 192 | Because pool sizes are limited, it should be considered unsafe to call freeHaskellFunPtr in a finalizer, because garbage collection is not predictable. |
| | 193 | |
| | 194 | The {{{POOLSIZE}}} pragma generates a compiler warning only on GHC versions where it isn't supported, so in practice it's portable. |
| | 195 | |
| | 196 | == Loose ends == |