Protecting against callbacks queuing up?

H

Hendrik van Rooyen

OK, now things starts to make sense.

You tell me to do something like this?


def doCallback(self):
if self.process_busy==False:
self.process_busy=True
self.at.after(0.01,self.data_callback)

This bit is allmost right - try to make the time a millisecond or less, if you
can. (however that is specified - I do not know) The spelling is probably:

self.at.after(1,self.data_callback, (other_stuff))

where "other stuff" comes from the event and is what is needed to do the
calculation. - the event should be an argument to the doCallback thinghy too,
somehow, so that you can get at what you need to do the calculation.
def doneCallback(self):
self.process_busy=False
you do not need this separate.
just do this:

def data_callback(self,other_stuff):
calculate what must be calculated
self.process_busy=False
And the after command will cause python to spawn a new thread, which
runs self.data_callback, which measn that the doCallback will return
immediately, so the next callback from accelerometer can be processed?

Yes and skipped if the calculation is not done yet. Obviously you will have
to do whatever it takes to make sure the events keep coming (if anything), so
it may end up a little more complex, but you basically have the idea now.

The thing that happens as a result of the after is not really a new thread, it
is a piece of the main loop that gets diverted for a while to do the
calculation.

if, in that routine, you go:

while True:
pass

You will completely freeze up the whole bang shoot..
And when the self.data_callback() finishes, i'd make it call the
doneCallback() to release my busy flag, right?

see above - just clear it from the called routine.
I'm gonna try this tomorrow :)

Good luck!

- Hendrik
 
D

Dennis Lee Bieber

But it'll only make a noticable delay EVERY time the user moves, and not
prevent the build up of calls if it doesn't finish within the 35 callbacks.

The point is that I don't know in advance, how long the call will take.
It depends on the amount of data i load and process during the call.
I only know when the calculations have finished, and when they are
called, and think there might be some way to block further callbacks
until the first one returns?

From what I could read in the documentation, the ONLY action that
stops the system from queuing callbacks is the one you've already said
locked up the system: the stop listening call.

Otherwise, the accelerometer hardware itself will insert an event
onto the queue at whatever rate it generates data at (documentation
suggested 35/sec for one device). That means you WILL get all those
events delivered unless you somehow filter them -- cf: the set filter
option, which will basically have to throw away events until whatever
criteria is used to determine when an event may be passed.

Now... It may be that those filters run as the event is generated --
whereas all the solutions currently being proposed run as part of the
callback routine; and the callback routine is the /output/ of an event
queue. If so, a filter would minimize the stuff that actually gets
queued for callback processing...

#init routine
...
self.filter.pass = True

#filter itself
...
if self.pass:
self.pass = False
callback() #this is the reference to the callback to be invoked

#callback processing
...
#do real callback work
self.filter.pass = True

(filters are their own class, so the instance wouldn't be
"self.filter.attribute", just "self.attribute")

NOTE: this only applies IF the accelerometer event filter is
asynchronous from the callback itself; it may not be, and that would
mean that you'd still get all the pending callbacks...

Pity the callback invocation doesn't pass the data WITH a
timestamp... Then you'd save "current time" at the end of the callback
processing, and...

if event.time > saved_time:
#do real callback work

Whatever method is used, it has to somehow determine which events to
ignore (ie, return without invoking the callback worker). At present,
the only method that can be done synchronously (not reliant on having
the filter run in parallel with a callback, nor on non-exist ant
timestamps) is the counter modulo method, which basically says process
only every n events, throwing the other n-1 events away.

In most GUIs, and similar, even an "after" trigger is still
synchronous -- the master event dispatching loop just sees that a
function should be called after so much time has passed -- but that call
is still synchronous with the dispatcher and nothing else gets processed
until it finishes.
 
E

Esben von Buchwald

It seems to solve the problem.

What I did:

def contextDataHandler(self):
self.contextdata.process_busy=True
self.services.findServices()
self.drawDisplay()
self.contextdata.process_busy=False

def doCallback(self):
self.at.cancel()
if self.process_busy==False:
self.at.after(0.01,self.data_callback)


The app works awesomely stable now

data_callback refers to contextDataHandler in parent class

Thanks guys, good job
 
D

Dennis Lee Bieber

def contextDataHandler(self):
self.contextdata.process_busy=True
self.services.findServices()
self.drawDisplay()
self.contextdata.process_busy=False

def doCallback(self):
self.at.cancel()
if self.process_busy==False:
self.at.after(0.01,self.data_callback)


The app works awesomely stable now

That seem rather convoluted and unstable to me... Each time an
accelerometer event triggers and results in a call to doCallback(). And
the first thing you do is cancel any "after" timer -- then, if NOT busy
(which, BTW, is cleaner coded as "if not self.process_busy:") you create
a NEW after timer -- thereby shifting the next real work callback by
whatever the event delivery rate was.

What would happen if, in the master __init__() you set:

self.needAfter = True

Then

def contextDataHandler(self):
self.services.findServices()
self.drawDisplay()
self.needAfter = True

And

def doCallback(self):
if self.needAfter:
self.needAfter = False
self.at.after(0.001, self.data_callback)

(Note I made the timeout much smaller -- since there will only ever be
ONE after timer active, and when it is active you WANT to process the
data, so why wait more than needed).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,159
Latest member
SweetCalmCBDGummies
Top