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