Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Importing 'C' Delay function into Haskell using FFI

Tags:

haskell

ffi

There is a function in the wiringPi 'C' library called delay with type

void delay(unsigned int howLong);

This function delays execution of code for howLong milliseconds. I wrote the binding code in haskell to be able to call this function. The haskell code is as follows,

foreign import ccall "wiringPi.h delay" c_delay :: CUInt -> IO ()
hdelay :: Int -> IO ()
hdelay howlong = c_delay (fromIntegral howlong)

After this, I wrote a simple haskell program to call this function. The simply haskell code is as follows..

--After importing relavent libraries I did

main = wiringPiSetup
    >> delay 5000

But the delay does not happen or rather the executable generated by the ghc compiler exits right away.

Could someone tell me what could possibly go wrong here? A small nudge in the right direction would help.

Cheers and Regards.

like image 332
Jay Avatar asked Nov 04 '22 03:11

Jay


1 Answers

Please ignore the part in block quote, and see update below - I am preserving the original non-solution because of comments associated with it.

You should mark the import as unsafe since you want the main thread to block while the function is executing (see comment below by @carl). By default, import is safe, not unsafe. So, changing the function signature to this should make the main thread block:

foreign import ccall unsafe "wiring.h delay" c_delay :: CUInt -> IO ()

Also, if you plan to write multi-threaded code, GHC docs for multi-threaded FFI is >very useful. This also seems a good starter.

Update

The behavior seems to be due to signal interrupt handling (if I recall correctly, this was added in GHC 7.4+ to fix some bugs). More details here: http://hackage.haskell.org/trac/ghc/wiki/Commentary/Rts/Signals

Please note the comment on the above page: Signal handling differs between the threaded version of the runtime and the non-threaded version.

Approach 1 - Handle signal interrupt in FFI code: A toy code is below which handles the interrupt in sleep. I tested it on Linux 2.6.18 with ghc 7.6.1.

C code:

/** ctest.c **/
#include <unistd.h>
#include <stdio.h>
#include <time.h>

unsigned delay(unsigned sec)
{
  struct timespec req={0};
  req.tv_sec = sec;
  req.tv_nsec = 0;

  while (nanosleep(&req, &req) == -1) {
    printf("Got interrupt, continuing\n");
    continue;
    }
  return 1;
}

Haskell code:

{-# LANGUAGE ForeignFunctionInterface #-}
-- Filename Test.hs
module Main (main) where
import Foreign.C.Types

foreign import ccall safe "delay" delay :: CUInt -> IO CUInt

main =  do
    putStrLn "Sleeping"
    n <- delay 2000
    putStrLn $ "Got return code from sleep: " ++ show n

Now, after compiling with ghc 7.6.1 (command: ghc Test.hs ctest.c), it waits until sleep finishes, and prints a message every time it gets an interrupt signal during sleep:

./Test
Sleeping
Got interrupt, continuing
Got interrupt, continuing
Got interrupt, continuing
Got interrupt, continuing
....
....
Got return code from sleep: 1

Approach 2 - Disable SIGVTALRM before calling FFI code, and re-enable:

I am not sure what the implications are for disabling SIGVTALRM. This is alternative approach which disables SIGVTALRM during FFI call, if you can't alter FFI code. So, FFI code is not interrupted during sleep (assuming it is SIGVTALRM that is causing the interrupt).

{-# LANGUAGE ForeignFunctionInterface #-}
-- Test.hs
module Main (main) where
import Foreign.C.Types
import System.Posix.Signals

foreign import ccall safe "delay" delay :: CUInt -> IO CUInt

main =  do
    putStrLn "Sleeping"
    -- Block SIGVTALRM temporarily to avoid interrupts while sleeping
    blockSignals $ addSignal sigVTALRM emptySignalSet
    n <- delay 2
    putStrLn $ "Got return code from sleep: " ++ show n
    -- Unblock SIGVTALRM
    unblockSignals $ addSignal sigVTALRM emptySignalSet
    return ()
like image 196
Sal Avatar answered Nov 15 '22 08:11

Sal