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 5 4151
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.
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
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
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.
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? This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
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 appears in already in the first line of the...
|
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 good as long as the other languages calls back on...
|
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
pointer would be appriciated.
Let say there is an...
|
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 to access databases from Zope (which is
written in...
|
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 ScheduledJobs. The section of my code that...
|
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 the documentation for 'thread' it reads
...
|
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
its hibernate or standby (S3: suspend to ram)...
|
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 in PyInt_FromLong () (see below). Most of this...
|
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 Register_Handler(...) from python via...
|
by: nemocccc |
last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
|
by: Sonnysonu |
last post by:
This is the data of csv file
1 2 3
1 2 3
1 2 3
1 2 3
2 3
2 3
3
the lengths should be different i have to store the data by column-wise with in the specific length.
suppose the i have to...
|
by: Hystou |
last post by:
There are some requirements for setting up RAID:
1. The motherboard and BIOS support RAID configuration.
2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
|
by: Hystou |
last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
|
by: Oralloy |
last post by:
Hello folks,
I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>".
The problem is that using the GNU compilers,...
|
by: jinu1996 |
last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
|
by: tracyyun |
last post by:
Dear forum friends,
With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
|
by: agi2029 |
last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
|
by: conductexam |
last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
| | |