I wrote a test program in Haskell on the Raspberry Pi that plays a delightful tune on a buzzer connected to a GPIO pin.
Here are the imports I used:
import qualified Control.Concurrent as C
import qualified Control.Monad as M
import System.IO
import qualified System.Posix.Unistd as P
Here are the functions that toggle the pin by writing to the /sys/class/gpio/gpio16/value file:
changePin2 :: Handle -> String -> Int -> IO ()
changePin2 handle onOff delay = do
pos <- hGetPosn handle
hPutStr handle (onOff ++ "\n")
hFlush handle
hSetPosn pos
P.usleep delay
--C.threadDelay delay
blinkOn2 :: Handle -> Int -> IO ()
blinkOn2 handle delay = do
changePin2 handle "1" delay
changePin2 handle "0" delay
finally, here is an example of playing one note with a pause before the next one:
mapM_ (blinkOn2 h) (replicate 26 1908)
P.usleep 50000
-- C.threadDelay 50000
When I first tried it, I used threadDelay and it sounded terrible. It was low pitched, suggesting the delay was longer than expected and all notes sounded more or less the same. Using the usleep function improved things considerably. Finally, adding the -threaded option when compiling with ghc made the sound even cleaner.
ghc -threaded buzzer1t.hs
I do not understand why either of these improved it and if anyone knows it would help greatly.
googling seems to reveal that usleep and friends are delays at the OS level whereas threadDelay only pertains to the thread in the Haskell program itself. threadDelay also seems like the more recommended one and considered better practice even though in this case usleep is clearly superior.
I think the documentation is a good start here:
GHC Note: threadDelay is a better choice. Without the -threaded option, usleep will block all other user threads. Even with the -threaded option, usleep requires a full OS thread to itself. threadDelay has neither of these shortcomings.
To expand a bit further: The GHC runtime multiplexes user threads over system threads. The default runtime uses only a single OS thread, regardless of how many user threads there are. Most blocking calls to external code are written such that they deschedule the current Haskell user thread while they're in external code, which is allowed to execute concurrently with Haskell code. This means that even the default runtime with a single OS thread can handle multiple user threads doing IO simultaneously, for instance.
In this world, actually blocking the OS thread is considered a somewhat hostile activity. threadDelay
just marks the current thread as not runnable until the specified amount of time has expired. This is much friendlier with the runtime system, as it releases the underlying OS thread.
When you use the threaded runtime, you get multiple OS threads to execute user threads, but it's still somewhat hostile to grab one and not release it. Among other things, it prevents the garbage collector from running (it waits until it can pause all user threads at known safe points, so it doesn't corrupt memory in use concurrently), and OS threads are significantly more memory-heavy than user threads if you add extras to make up for lost concurrency.
So for most software, threadDelay
is a much better citizen. But it has downsides. The thread doesn't necessarily resume immediately. It becomes available to be scheduled at the given time, but that doesn't mean it actually runs. That still depends on other threads yielding. That's almost certainly the cause of the trouble you were having - the additional delay waiting to go from runnable to actually running. usleep
is around specifically for the cases when that gets in the way. Seems like a fine reason to use it when needed.