This is very helpful, thanks for the tips. I especially like the idea of 
waiting on a file descriptor controlled by a C thread; that should avoid the 
need for any Julia code to be called by the callback.

You may be interested in some experiments I ran (see below), trying to 
reproduce my problem in a simpler context. "Unfortunately," all of these 
experiments work. So either there is something specific to this particular 
library, or "working" is fragile in some fashion and I just haven't succeeded 
in triggering a failure.

--Tim

c = Condition()

function c_notify(p::Ptr{Void})
    cond = unsafe_pointer_to_objref(p)
    notify(cond)
    C_NULL
end

const cb = cfunction(c_notify, Ptr{Void}, (Ptr{Void},))

@sync begin
    @async begin
        println("About to wait from 1")
        wait(c)
        println("Done waiting from 1")
    end
    @async begin
        println("About to notify from 2")
#         notify(c)
        ccall(cb, Void, (Ptr{Void},), pointer_from_objref(c))
        println("Done notifying from 2")
    end
end

threads = Uint64[0]

@sync begin
    @async begin
        println("About to wait from 1")
        wait(c)
        println("Done waiting from 1")
    end
    @async begin
        println("About to notify from 2")
#         notify(c)
        ccall(:pthread_create, Cint, (Ptr{Void}, Ptr{Void}, Ptr{Void}, 
Ptr{Void}), threads, C_NULL, cb, pointer_from_objref(c))
        ccall(:pthread_join, Cint, (Uint64, Ptr{Void}), threads[1], C_NULL)
        println("Done notifying from 2")
    end
end


On Friday, January 31, 2014 01:50:53 PM Spencer Russell wrote:
> Hi Tim,
> 
> I spent some time playing around with interfacing to multi-threaded C
> libraries for AudioIO, namely portaudio. In Portaudio you register a
> callback which gets called periodically whenever the audio card needs a new
> frame of data. It gets called from a separate thread, so I was getting
> segfaults if I executed julia code directly from the callback.
> 
> In the end I wrote a small C module that implemented a callback which I
> kept synchronized with a Julia Task. For the C callback to wake up the Task
> I used a file descriptor (that way in Julia land I could wait on it and
> only block that task instead of the whole julia process). For the Julia
> Task to wake up the C callback I used a semaphore.
> 
> You can see my C code (as well as the BinDeps stuff to compile it) in the
> AudioIO repo at
> 
> https://github.com/ssfrr/AudioIO.jl
> 
> I'm not sure if this approach would work in your situation, but if your C
> library is spawning a separate thread I think this is one way to handle it.
> 
> -s
> 
> On Fri, Jan 31, 2014 at 7:58 AM, Tim Holy <[email protected]> wrote:
> > I'm interfacing with a C library that provides the ability to set a
> > callback
> > function so you can be notified when some job is done. I'm hoping to
> > exploit
> > this with the Task interface so I can feed multiple jobs with a single
> > Julia
> > process. I'm declaring my notify function like this:
> > 
> > function callback_notify(hnd, status, data)
> > 
> >     println("In notify")
> >     s = unsafe_pointer_to_objref(data)::Stream
> >     @show s
> >     s.status = status
> >     notify(s.c)
> >     println("Done notifying")
> >     nothing
> > 
> > end
> > 
> > Stream is a type that has a Condition member,
> > type Stream
> > 
> >     handle::StreamHandle
> >     status::Cuint
> >     c::Condition
> > 
> > end
> > 
> > I've successfully set the callback function, issued wait(s.c), and from
> > the
> > println statements I can see that the notify callback executes fully.
> > However,
> > my task never wakes up. My suspicion is that the C library is spawning a
> > new
> > thread; does that pose problems for the Task interface? Or is something
> > else
> > happening? Any good workarounds?
> > 
> > --Tim

Reply via email to