Changes between Version 2 and Version 3 of BlockObjects/FakingIt


Ignore:
Timestamp:
Aug 26, 2011 6:33:25 AM (4 years ago)
Author:
chak
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • BlockObjects/FakingIt

    v2 v3  
    11= Block Objects: Marshalling Them Explicitly =
    22
     3In the following, we use the standard Haskell 2010 FFI to explicitly marshal Haskell functions to C blocks and vice versa.
    34
     5== The qsort_b example using a foreign wrapper ==
     6
     7The most straight-forward approach is to use the existing FFI support for turning Haskell functions into C function pointers by way of a foreign import wrapper declaration.  We can then embed these C function pointers in block literals without the need for a explicit environment on the C side.
     8
     9=== Block literals ===
     10
     11Blocks are by default allocated on the stack in C and only promoted to the heap using an explicity `Block_copy()` function.  In Haskell, we always allocate them on the heap, which is indicated by the pointer being placed in a block literals `isa` field:
     12{{{
     13foreign import ccall "& _NSConcreteGlobalBlock" nsConcreteGlobalBlock :: Ptr ()
     14}}}
     15
     16A block literal with an empty environment has the following layout:
     17{{{
     18-- Layout of the block literal (64-bit runtime)
     19--
     20-- .quad        __NSConcreteGlobalBlock           # void *isa;
     21-- .long        1342177280                        # int  flags = 0x50000000;
     22-- .long        0                                 # int  reserved;
     23-- .quad        ___block_invoke                   # void (*invoke)(void *, ...);
     24-- .quad        ___block_descriptor               # struct Block_descriptor *descriptor;
     25
     26long, quad :: Int
     27long = 4  -- long word = 32 bit
     28quad = 8  -- quad word = 64 bit
     29
     30isaOffset, flagsOffset, invokeOffset, descriptorOffset, blockLiteralSize :: Int
     31isaOffset        = 0
     32flagsOffset      = isaOffset        + quad
     33invokeOffset     = flagsOffset      + long + long
     34descriptorOffset = invokeOffset     + quad
     35blockLiteralSize = descriptorOffset + quad
     36}}}
     37
     38In Haskell, we represent block literals as opaque pointers:
     39{{{
     40newtype Block a = Block (Ptr (Block a))
     41}}}
     42
     43
     44When turning a Haskell function into a C function pointer to be included in a block literal as the `invoke` function, we need to take care to add a pointer to the block literal itself as a new first argument:
     45{{{
     46mkBlock :: ((Block f -> f) -> IO (FunPtr (Block f -> f))) -> f -> IO (Block f)
     47mkBlock mkWrapper f
     48  = do { fPtr     <- mkWrapper (const f)
     49       ; blockPtr <- mallocBytes blockLiteralSize
     50       ; poke (blockPtr `plusPtr` isaOffset)        nsConcreteGlobalBlock
     51       ; poke (blockPtr `plusPtr` flagsOffset)      (0x50000000 :: Word32)
     52       ; poke (blockPtr `plusPtr` invokeOffset)     fPtr
     53       ; poke (blockPtr `plusPtr` descriptorOffset) descriptorPtr
     54       ; return $ Block blockPtr
     55       }
     56}}}
     57
     58The block descriptor is static, except for the signature that we omit for the moment.
     59{{{
     60-- Block descriptor structure shared between all blocks.
     61--
     62-- .quad        0                                 # unsigned long int reserved;
     63-- .quad        32                                # unsigned long int size = blockLiteralSize;
     64-- .quad        signature_str                     # const char *signature;
     65-- .quad        0                                 # <undocumented>
     66
     67descriptorPtr :: Ptr ()
     68descriptorPtr
     69  = unsafePerformIO $
     70    do { descPtr <- mallocBytes (4 * quad)
     71       ; poke (descPtr `plusPtr` (0 * quad)) (0 :: Word64)
     72       ; poke (descPtr `plusPtr` (1 * quad)) blockLiteralSizeWord64
     73       ; poke (descPtr `plusPtr` (2 * quad)) nullPtr    -- gcc puts a NULL in; should be ok for now
     74       ; poke (descPtr `plusPtr` (3 * quad)) (0 :: Word64)
     75       ; return descPtr
     76       }
     77  where
     78    blockLiteralSizeWord64 :: Word64
     79    blockLiteralSizeWord64 = fromIntegral blockLiteralSize
     80}}}
    481
    582