472,328 Members | 1,748 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 472,328 software developers and data experts.

(Win32 API) callback to Python, threading hiccups

Hiho,

could somebody please enlighten me about the mechanics of C callbacks to
Python? My domain is more specifically callbacks from the win32 API, but
I'm not sure that's where the problem lies. Here's a description...

I want a callback-based MIDI input/processing, so PortMidi was not an
alternative. I have written a C extension module that links to the mmsys
MIDI API. I separated the win32-dependant code from the Python extension
code, so a) the module interface is system-neutral, b) the
implementation can be tested (re-used) outside of Python. So, that code
is tested OK, but it might be useful to sketch it's design:
- as you may know, the MIDI input on win32 is already managed thru a
callback mechanism; the driver calls back your program with data buffers
- yet, I don't call directly into Python from the callback, since some
restrictions apply on what system calls you can make from there, and I
don't know what Python's interpreter / scripts might call.
- so, on callback, I create a new thread, after checking that the
previous one has returned already (WaitOnSingleObject(mythread)) so we
only have one thread involved.
- this is this thread that calls the user callback, yet this callback
isn't yet a Python callable, we're still in native C code.
- on the side of the extension module now, I merely copied the callback
example from the Python/C API doc, and added GIL management around the call:

static PyObject * my_callback = NULL; //this gets fixed by a
setCallback() func
static void external_callback(const MidiData * const data) {
if (my_callback && (my_callback != Py_None)) {
if (! data) {
PyErr_SetString(PyExc_IndexError, getLastErrorMessage());
} else {
PyObject * arglist = NULL;
PyObject * result = NULL;
arglist = Py_BuildValue("(i,i,s#)", data->deviceIndex,
data->timestamp, data->buffer, data->size);// 0, 0, "test", 4);//

PyGILState_STATE gil = PyGILState_Ensure();
result = PyEval_CallObject(my_callback, arglist);
PyGILState_Release(gil);

Py_DECREF(arglist);
Py_DECREF(result);
}
}
}

- this one above is what is actually passed as callback to the 'native'
C part. So, here's what (I presume) happens:
1. the driver calls into my C code
2. my C code spawns a thread that calls into the extension
3. the extension calls into Python, after acquiring the GIL

Now, here's the hiccup:
inside a Python script, anytime a Python object is accessed by both the
(Python) callback and the main program, I get a GPF :/

You bet I tried to use locks, Queues and whatnot, but nothing will do,
it seems I get protection faults on accessing... thread exclusion objects.

Yet, there is a way where it works seamlessly: it's when the __main__
actually spawns a threading.Thread to access the shared object. Hence I
am lost.

This works:

def input_callback(msg):
midiio.send(msg) ##MIDI thru, a call into my extension that wraps
the implementation call with BEGIN_ALLOW_THREADS and END_...
__main__:
raw_input()

This results in GPF as soon as the callback is fired:

tape = Queue.Queue()
def input_callback(msg):
tape.put(msg)
__main__:
while True:
print tape.get()

This works like a charm:

tape = Queue.Queue()
def input_callback(msg):
tape.put(msg)
def job():
while True:
print tape.get()
__main__:
t = threading.Thread(target = job)
t.start()
raw_input()

Note that I also get a GPF in the later case if I stop the program with
KeyInterrupt, but I guess it's something I'll look into afterwards...

While I can think of hiding away the added threading within a wrapper
module, I won't sleep well untill I figure out what's really happening.
Could someone save my precious and already sparse sleeping time, by
pointing me to what I'm missing here?

WinXP SP1
Python 2.4.1
MinGW GCC 3.4.2

TIA,
Francois De Serres
Jul 21 '05 #1
5 4033
Francois De Serres wrote:
- so, on callback, I create a new thread, after checking that the
previous one has returned already (WaitOnSingleObject(mythread)) so we
only have one thread involved.


Uh... to me, this looks like a frighteningly inefficient way of doing
things. How about using a synchronous queue to post the data to a
processing thread? That way, you don't have to create an entierly new
thread each time you receive data in the callback.
Jul 21 '05 #2
Christopher Subich wrote:
Francois De Serres wrote:

- so, on callback, I create a new thread, after checking that the
previous one has returned already (WaitOnSingleObject(mythread)) so we
only have one thread involved.


Uh... to me, this looks like a frighteningly inefficient way of doing
things. How about using a synchronous queue to post the data to a
processing thread? That way, you don't have to create an entierly new
thread each time you receive data in the callback.

Thanks for the tip, and sorry for frightening you. Maybe you can help as
well with my issue?

Francois

Jul 21 '05 #3
Francois De Serres wrote:
PyGILState_STATE gil = PyGILState_Ensure();
result = PyEval_CallObject(my_callback, arglist);
PyGILState_Release(gil);
Py_DECREF(arglist);
Py_DECREF(result);


I think this should be:
PyGILState_STATE gil = PyGILState_Ensure();
result = PyEval_CallObject(my_callback, arglist);
Py_DECREF(arglist);
Py_DECREF(result);
PyGILState_Release(gil);

The DECREFs need to be protected, that is where storage is
recycled and such, and you still need Python's data structures
to do that kind of work.

--Scott David Daniels
Jul 21 '05 #4
Scott David Daniels <Sc***********@Acm.Org> wrote:
Francois De Serres wrote:
PyGILState_STATE gil = PyGILState_Ensure();
result = PyEval_CallObject(my_callback, arglist);
PyGILState_Release(gil);
Py_DECREF(arglist);
Py_DECREF(result);


I think this should be:
PyGILState_STATE gil = PyGILState_Ensure();
result = PyEval_CallObject(my_callback, arglist);
Py_DECREF(arglist);
Py_DECREF(result);
PyGILState_Release(gil);

The DECREFs need to be protected, that is where storage is
recycled and such, and you still need Python's data structures
to do that kind of work.


I freely admit to being woefully underinformed about the GIL, but I'm
wondering if your statement is really true. If the purpose of the GIL is
simply to make things thread-safe, then I would have guessed that the first
one was correct. If someone else holds a reference to "arglist", then the
DECREF is just a nice, atomic decrement. If no one else holds a reference
to "arglist", then it's quite safe to delete it.

Is there more to the GIL than I'm assuming?
--
- Tim Roberts, ti**@probo.com
Providenza & Boekelheide, Inc.
Jul 21 '05 #5
Tim Roberts wrote:
PyGILState_STATE gil = PyGILState_Ensure();
result = PyEval_CallObject(my_callback, arglist);
PyGILState_Release(gil);
Py_DECREF(arglist);
Py_DECREF(result);


If someone else holds a reference to "arglist", then the
DECREF is just a nice, atomic decrement. If no one else holds a reference
to "arglist", then it's quite safe to delete it.


What if anothere thread takes the GIL as soon as you release it, and is
attempting to allcate a new opbject, and (at the same time - you might
be on a multiprocessor!) your PyDECREF removes the last reference and
starts freeing objects?
Jul 21 '05 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: Markus von Ehr | last post by:
Hi, I tried to build the swig: callback example, but when I run the runme.py file I get a message box: abnormal program termination The error...
7
by: Steve Menard | last post by:
Here is my problem. I have this library thats hosts another language within python, and allows that language to call back INTO python. All is...
4
by: Wai Yip Tung | last post by:
I'm attempting to turn some process than uses callback to return result into a more user friendly generator. I'm hitting some road block so any...
4
by: Carl | last post by:
Using COM and ADO on Win32, it is very easy to access databases (eg MySql, MS SQL Server, etc) from Python. Does anybody know if it is possible...
15
by: Bryan | last post by:
I have a multi-threaded C# console application that uses WMI (System.Management namespace) to make RPC calls to several servers (600+ ) and returns...
2
by: | last post by:
Hello all, I need to call a win32 api and I also need to have a worker thread in my program. Is there something I should be carefull of, as in...
5
by: Lonewolf | last post by:
Hi all, I am not sure if it is even possible to do it from .NET itself. Is there something like a waitable timer which can resume the system from...
0
by: Tim Spens | last post by:
The following is a simple complete example using the c python api to generate callbacks from c to python. But when I run the c code I get a segfault...
0
by: Tim Spens | last post by:
--- On Fri, 6/27/08, Tim Spens <t_spens@yahoo.comwrote: I think I know where the problem is but I'm unsure how to fix it. When I call...
0
by: concettolabs | last post by:
In today's business world, businesses are increasingly turning to PowerApps to develop custom business applications. PowerApps is a powerful tool...
0
better678
by: better678 | last post by:
Question: Discuss your understanding of the Java platform. Is the statement "Java is interpreted" correct? Answer: Java is an object-oriented...
0
by: Kemmylinns12 | last post by:
Blockchain technology has emerged as a transformative force in the business world, offering unprecedented opportunities for innovation and...
0
by: CD Tom | last post by:
This happens in runtime 2013 and 2016. When a report is run and then closed a toolbar shows up and the only way to get it to go away is to right...
0
by: CD Tom | last post by:
This only shows up in access runtime. When a user select a report from my report menu when they close the report they get a menu I've called Add-ins...
0
by: Naresh1 | last post by:
What is WebLogic Admin Training? WebLogic Admin Training is a specialized program designed to equip individuals with the skills and knowledge...
0
jalbright99669
by: jalbright99669 | last post by:
Am having a bit of a time with URL Rewrite. I need to incorporate http to https redirect with a reverse proxy. I have the URL Rewrite rules made...
0
by: antdb | last post by:
Ⅰ. Advantage of AntDB: hyper-convergence + streaming processing engine In the overall architecture, a new "hyper-convergence" concept was...
1
by: Matthew3360 | last post by:
Hi, I have a python app that i want to be able to get variables from a php page on my webserver. My python app is on my computer. How would I make it...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.