[Haskell-cafe] FFI woes!

Sebastian Sylvan sebastian.sylvan at gmail.com
Thu Dec 16 10:20:51 EST 2004

On Thu, 16 Dec 2004 12:38:26 +0000, Jules Bean <jules at jellybean.co.uk> wrote:
> On 16 Dec 2004, at 06:57, Sebastian Sylvan wrote:
> > Here's the problem though. If the SamplePlaybackRaw is currently
> > playing sound when it gets garbage collected, I want to keep the sound
> > resource (SoundSample) alive until the playback has finished.
> >
> I probably haven't understood the details correctly but why not just
> spawn a thread in your 'start playback' function. This thread (a) keeps
> a reference to the soundsample (which guarantees it will never be
> GC'ed) and (b) checks every second or so to see if the playing has
> finished.  If the playing has finished, it arranges to no longer hold
> the reference, and in due course GC will take it out.

This is my "if-all-else-fails" backup.

I really don't want to poll the sound sample all the time since it
uses up resources needlessly. If it comes to that, though, it's pretty
much guaranteed that it will get the job done.

The plan was to try to initiate the polling loop only when the GC
wants to delete the sound channel.

Here's what I imagine a typical execution could look like

1. Sound resource, s, is loaded
2. Calling "play" on s, returns a "channel" in which the sound is being played.
3. Repeat 2 any number of times.
4. The GC is being invoked. s may no longer be in scope, but the
"channels" are still referencing the sound resource so it won't get
collected. If the channel(s) are out of scope and the GC tries to
collect them, they will each spawn a "keep-alive-loop" in their
finalizer which has no other purpose but to keep the sound resource
alive for however long it takes to finish playing.

This is what I tried to do. I've now tried to do it with weak pointers
as well. I get the finalizer to run, but for some reason the finalizer
for the sound resource gets invoked before the finalizer for the
channel. This is extremply strange since the channel contains a
reference to the "ForeignPtr CSoundSample" so I wouldn't expect the
sound resource finalizer to EVER get invoked while there are still
channels around that are referencing it.

Here's the latest attempt:

-- the sound resource.
-- The finalizer will simply call the C-library's "free"-function
sampleLoad :: String -> IO SoundSample
sampleLoad s = do cs <- newCString s
                  samplePtr <- fsound_SampleLoad (-1) cs 0 0 0
                  finalizer <- mkSampleFinalizer sampleFinalizer
                  newForeignPtr finalizer samplePtr

-- The playback
-- contains the channel AND a reference to the SoundSample
-- so the soundsample should NEVER get finalized before the 
-- SamplePlaybackRaw
data SamplePlaybackRaw = SP !CInt !SoundSample
type SamplePlayback = Weak SamplePlaybackRaw

-- play a sample
samplePlay :: SoundSample -> IO SamplePlayback
samplePlay sample = do ch <- withForeignPtr sample (fsound_PlaySound (-1))
		       let spb = SP ch sample -- SamplePlaybackRaw
                       mkWeakPtr spb (Just (samplePlaybackFinalizer spb))

samplePlaybackFinalizer :: SamplePlaybackRaw -> IO ()
samplePlaybackFinalizer (SP ch sample) = 
	do putStrLn "Starting keep-alive loop for sample playback"
           hFlush stdout                                     
           touchForeignPtr sample
           forkIO (keepAlive ch sample)
           return ()

keepAlive :: CInt -> SoundSample -> IO ()
keepAlive ch sample = do putStrLn "Keeping channel alive!"
                         hFlush stdout
			 touchForeignPtr sample			 
			 threadDelay 500000 -- wait a while
                         b <- fsound_IsPlaying ch
                         case cscharToBool b of
                         	True -> keepAlive ch sample
                                False -> putStrLn "Finished keeping
sample alive"


Sebastian Sylvan
UIN: 44640862

More information about the Haskell-Cafe mailing list