473,394 Members | 1,840 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

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

how do you implement a reactor without a select?

I have always been curious about how people implement mainloops (or,
in Twisted terminology, reactors). So I sit down and I wrote the
following simple implementation:

import itertools

class SimpleReactor(object):

DELAY = 0.001 # seconds

def __init__(self):
self._event = {} # action id -(scheduled time, action, args)
self._counter = itertools.count(1) # action id generator
self.running = False

def callLater(self, deltat, action, *args):
"""Schedule an action with arguments args in deltat seconds.
Return the action id"""
now = time.time()
i = self._counter.next()
self._event[i] = now + deltat, action, args
return i

def cancelCallLater(self, action_id):
"Cancel the action identified by action_id"
del self._event[action_id]

def default_action(self): # to be overridden
"Invoked at each lap in the mainloop"
time.sleep(self.DELAY) # don't run too fast, rest a bit

def cleanup_action(self): # to be overridden
"Invoked at the end of the mainloop"

def manage_exc(self, e):
"Invoked at each call"
raise e

def dooneevent(self):
"Perfom scheduled actions"
now = time.time()
for i, (start_time, action, args) in self._event.items():
if now >= start_time: # it's time to start the action
self.cancelCallLater(i) # don't run it again
try:
action(*args)
except Exception, e:
self.manage_exc(e)

def run(self):
"Run the main loop"
self.running = True
try:
while self.running:
self.default_action()
self.dooneevent()
except KeyboardInterrupt:
print 'Stopped via CTRL-C'
finally:
self.cleanup_action()

def stop(self):
self.running = False

Notice that I copied the Twisted terminology, but
I did not look at Twisted implementation because I did not want to
use a select (I assume that the GUI mainloops do not use it either).
The trick I use is to store the actions to perform (which are
callables identified by an integer) in an event dictionary and
to run them in the mainlooop if the current time is greater than
the scheduled time.
I had to add a time.sleep(.001) call in the default_action to avoid
consuming 100%
of the CPU in the loop.
I wonder if real mainloops are done in this way and how bad/good is
this implementation compared to a serious one. Any suggestion/hint/
advice
is well appreciated. Thanks,

Michele Simionato

May 7 '07 #1
10 1935
Notice that I copied the Twisted terminology, but
I did not look at Twisted implementation because I did not want to
use a select (I assume that the GUI mainloops do not use it either).
Why do you assume that? It's a wrong assumption. Yielding a thread/process
until the OS wakes it up because of IO to be performed is the proper way to
go. And at least in unix, IO is _everything_, also mouse-movements and
keyboard events. Most probably the OS will have specialized APIs (or some
wrapper lib has) that allow for reactor registration for events of
different kinds including timers. But basically, it's select - I mean you
could easily offer a timer as a file-object as well. Not sure if that's
done though.
The trick I use is to store the actions to perform (which are
callables identified by an integer) in an event dictionary and
to run them in the mainlooop if the current time is greater than
the scheduled time.
I had to add a time.sleep(.001) call in the default_action to avoid
consuming 100%
of the CPU in the loop.
I wonder if real mainloops are done in this way and how bad/good is
this implementation compared to a serious one. Any suggestion/hint/
advice
is well appreciated. Thanks,
It's ok, but of course more wasteful than it needs to be - better would be
full delegation to the OS.

Diez
May 7 '07 #2
Michele Simionato <mi***************@gmail.comwrote:
I wonder if real mainloops are done in this way and how bad/good is
this implementation compared to a serious one. Any suggestion/hint/
advice is well appreciated. Thanks,
Module sched in Python's standard library may suggest one clearly-better
approach: when you know in advance when future events are scheduled for,
sleep accordingly (rather than polling every millisecond). sched's
sources are simple enough to study, and its architecture clean and
strong enough that it's easy to extend to other cases, e.g. where
previously-unscheduled events may be delivered from other threads,
without necessarily hacking the sources.

Specifically, sched implements the Dependency Injection DP: rather than
just calling time.time and time.sleep, it accepts those two callables
upon initialization. This makes it easy, among many other
customizations, to pass instead of time.sleep a user-coded callable
(typically a bound method) that "sleeps" by a wait-with-timeout on a
Queue (so that other threads, by putting an event on the Queue in
question, immediately wake up the scheduler, etc, etc).
Alex
May 7 '07 #3
On May 7, 4:39 pm, a...@mac.com (Alex Martelli) wrote:
Michele Simionato <michele.simion...@gmail.comwrote:
I wonder if real mainloops are done in this way and how bad/good is
this implementation compared to a serious one. Any suggestion/hint/
advice is well appreciated. Thanks,

Module sched in Python's standard library may suggest one clearly-better
approach: when you know in advance when future events are scheduled for,
sleep accordingly (rather than polling every millisecond). sched's
sources are simple enough to study, and its architecture clean and
strong enough that it's easy to extend to other cases, e.g. where
previously-unscheduled events may be delivered from other threads,
without necessarily hacking the sources.

Specifically, sched implements the Dependency Injection DP: rather than
just calling time.time and time.sleep, it accepts those two callables
upon initialization. This makes it easy, among many other
customizations, to pass instead of time.sleep a user-coded callable
(typically a bound method) that "sleeps" by a wait-with-timeout on a
Queue (so that other threads, by putting an event on the Queue in
question, immediately wake up the scheduler, etc, etc).

Alex
I know about sched (it was the first thing I looked at): the problem
is that sched
adopt a blocking approach and it basically requires threads, whereas I
wanted to
avoid them. Diez B. Roggisch's reply is closer to my expectations:
>Most probably the OS will have specialized APIs (or some
wrapper lib has) that allow for reactor registration for events of different
kinds including timers.
But what kind of specialized API do I have at my disposition for
timers on Linux?
It looks like this is the question I should have asked the first
time ;)
Michele Simionato

May 7 '07 #4
Michele Simionato wrote:
Notice that I copied the Twisted terminology, but
I did not look at Twisted implementation because I did not want to
use a select (I assume that the GUI mainloops do not use it either).
The trick I use is to store the actions to perform (which are
callables identified by an integer) in an event dictionary and
to run them in the mainlooop if the current time is greater than
the scheduled time.
I had to add a time.sleep(.001) call in the default_action to avoid
consuming 100%
of the CPU in the loop.
Busy-looping like that is ugly and inefficient, even with the sleep
thrown in.

Most GUI main loops _do_ use either select() or poll(). When Xt/GTK/
Qt/etc have function like "gtk_add_input" which takes an fd that
you'll get notified about if it's written to while you're in the main
loop, that's just adding another fd to the select() loop.

There are other ways to wait for events on an fd, but they tend to be
less portable. Depending on your Unix flavor, epoll, /dev/poll,
kqueues, kevent, queued realtime signals, or something else might be
available from the OS (but probably not from Python without futzing
with ctypes or writing an extension). If you want details, check out
http://www.kegel.com/c10k.html

The alternatives usually aren't really faster unless you have hundreds
of connections, though--select/poll have major portability advantages,
so go with them unless you have a compelling reason.

May 7 '07 #5
Michele Simionato <mi***************@gmail.comwrote:
...
I know about sched (it was the first thing I looked at): the problem
is that sched
adopt a blocking approach and it basically requires threads, whereas I
As the "sleep for time N" callable, you can pass any callable you wish;
I suggested one based on Queue.wait with timeout, which would indeed
require some other thread to wake it, but any kind of system call which
allows timeouts, such as select or poll, would work just as well.
wanted to
avoid them. Diez B. Roggisch's reply is closer to my expectations:
Most probably the OS will have specialized APIs (or some wrapper lib
has) that allow for reactor registration for events of different kinds
including timers.

But what kind of specialized API do I have at my disposition for
timers on Linux?
It looks like this is the question I should have asked the first
time ;)
What do you expect from "timers on Linux" that you could not get with a
simple "sleep for the next N milliseconds"? A timer (on Linux or
elsewhere) can jog your process N milliseconds from now, e.g. with a
SIGALRM or SIGPROF, and you can set one with the setitimer syscall
(presumably accessible via ctypes, worst case -- I've never used it from
Python, yet), but how would that help you (compared to plain sleep,
select, poll, or whatever else best fits your need)?
Alex
May 8 '07 #6
On May 8, 4:53 am, a...@mac.com (Alex Martelli) wrote:
What do you expect from "timers on Linux" that you could not get with a
simple "sleep for the next N milliseconds"? A timer (on Linux or
elsewhere) can jog your process N milliseconds from now, e.g. with a
SIGALRM or SIGPROF, and you can set one with the setitimer syscall
(presumably accessible via ctypes, worst case -- I've never used it from
Python, yet), but how would that help you (compared to plain sleep,
select, poll, or whatever else best fits your need)?
I hoped there was a library such thay I could register a Python
callable (say
a thunk) and having it called by the linux timer at time t without
blocking
my process. But if a Linux timer will just send to my process an
alarm, I would need to code myself a mechanism waiting for the alarm
and doing the function call. In that case as you say, I would be
better off with a select+timeout or a even with a queue+timeout, which
already do most of the job.

Michele Simionato

May 8 '07 #7
On May 7, 7:36 pm, "sjdevn...@yahoo.com" <sjdevn...@yahoo.comwrote:
>
Busy-looping like that is ugly and inefficient, even with the sleep
thrown in.

Most GUI main loops _do_ use either select() or poll(). When Xt/GTK/
Qt/etc have function like "gtk_add_input" which takes an fd that
you'll get notified about if it's written to while you're in the main
loop, that's just adding another fd to the select() loop.

There are other ways to wait for events on an fd, but they tend to be
less portable. Depending on your Unix flavor, epoll, /dev/poll,
kqueues, kevent, queued realtime signals, or something else might be
available from the OS (but probably not from Python without futzing
with ctypes or writing an extension). If you want details, check outhttp://www.kegel.com/c10k.html

The alternatives usually aren't really faster unless you have hundreds
of connections, though--select/poll have major portability advantages,
so go with them unless you have a compelling reason.
I see where you are coming from. In a GUI or in a Web server most
of the time is spent waiting from input, so a busy loop design would
be a terrible
design indeed. But I had another use case in mind. The code I posted
is
extracted from a batch script I have, called 'dbexplorer'. The script
performs lots of
queries in a database and find out "bad" data according to some
criterium.
So at each iteration there is a default action which is nontrivial; it
becomes
a time.sleep only at the end, when the batch has finished its job and
it
is waiting for input.In normal conditions most of the time is spent
doing
something, not waiting, so the busy loop design here is not so bad.
Still I wanted to know what my alternatives were. And from this thread
I gather the impression that actually the only portable alternative is
using some
kind of select, unless I want to use threads, and in that case the
scheduler approach could be viable.
Anyway, that C10K page is really an interesting resource, thanks for
pointing it out!

Michele Simionato

May 8 '07 #8
Michele Simionato <mi***************@gmail.comwrote:
On May 8, 4:53 am, a...@mac.com (Alex Martelli) wrote:
What do you expect from "timers on Linux" that you could not get with a
simple "sleep for the next N milliseconds"? A timer (on Linux or
elsewhere) can jog your process N milliseconds from now, e.g. with a
SIGALRM or SIGPROF, and you can set one with the setitimer syscall
(presumably accessible via ctypes, worst case -- I've never used it from
Python, yet), but how would that help you (compared to plain sleep,
select, poll, or whatever else best fits your need)?

I hoped there was a library such thay I could register a Python
callable (say
a thunk) and having it called by the linux timer at time t without
blocking
my process. But if a Linux timer will just send to my process an
alarm, I would need to code myself a mechanism waiting for the alarm
and doing the function call. In that case as you say, I would be
better off with a select+timeout or a even with a queue+timeout, which
already do most of the job.
Python or not, I don't know of a way to "register a callback" from the
OS without using threads. Considering that your callback WOULD be
executing on a different thread no matter what (your "only" thread being
blocked on some blocking syscall, or executing other code -- having
other code in your process suddenly start executing at that point is
pre-emptive threading, by whatever name you choose to call it), it's not
clear to me why the "avoiding threads" issue should matter to you.
Alex
May 8 '07 #9
On 2007-05-08, Michele Simionato <mi***************@gmail.comwrote:
On May 8, 4:53 am, a...@mac.com (Alex Martelli) wrote:
>What do you expect from "timers on Linux" that you could not get with a
simple "sleep for the next N milliseconds"? A timer (on Linux or
elsewhere) can jog your process N milliseconds from now, e.g. with a
SIGALRM or SIGPROF, and you can set one with the setitimer syscall
(presumably accessible via ctypes, worst case -- I've never used it from
Python, yet), but how would that help you (compared to plain sleep,
select, poll, or whatever else best fits your need)?

I hoped there was a library such thay I could register a Python
callable (say
a thunk) and having it called by the linux timer at time t without
blocking
I once played with the following module to do something similar.
Maybe it is usefull to you as is, or can give you an idea on how
to proceed. I only tested it on linux.

---------------------------- alarm.py --------------------
m signal import signal, SIG_IGN, SIGALRM
from time import time
from thread import allocate_lock
from heapq import heappush, heappop
from os import kill, getpid
import errno

from select import select, error as SelectException

from ctypes import *
libc = cdll.LoadLibrary("/lib/libc.so.6")

class _timeval(Structure):
_fields_ = [("tv_sec" , c_long), ("tv_usec", c_long)]

def timeval(tm):
sec = int(tm)
usec = int(1000000 * (tm - sec))
return _timeval(sec, usec)

class itimerval(Structure):
_fields_ = [("it_interval", _timeval), ("it_value", _timeval)]
def alarm(tm):
tv = timeval(tm)
ti = timeval(0.0)
ntv = itimerval(ti, tv)
otv = itimerval(timeval(0.0), timeval(0.0))
rt = libc.setitimer(0, byref(ntv), byref(otv))
#print otv.it_value.tv_sec , otv.it_value.tv_usec
if rt:
raise ValueError
else:
return otv.it_value.tv_sec + otv.it_value.tv_usec / 1000000.0

def sleep(tm):
wakeup = time() + tm
while tm >= 0:
try:
select([],[],[],tm)
except SelectException , Err_Info:
#print dir(Err_Info)
if Err_Info[0] != errno.EINTR:
raise
tm = wakeup - time()

alarms = []
alarm_lock = allocate_lock()

def AlarmHandler(sgnr, frame):
alarm_lock.acquire()
now = time()
while alarms and alarms[0].moment <= now:
current = heappop(alarms)
if not current.canceled:
current.func(*current.args, **current.kwds)
current.executed = True
now = time()
alarm_lock.release()
if alarms:
#print alarms[0].moment - now, alarms
alarm(alarms[0].moment - now)

signal(SIGALRM, AlarmHandler)

class Alarm(object):
def __init__(self, tp, func, *args, **kwds):
alarm(0)
try:
alarm_lock.acquire()
self.canceled = False
self.executed = False
self.func = func
self.args = args
self.kwds = kwds
self.moment = tp
heappush(alarms, self)
now = time()
delta = alarms[0].moment - now
#print alarms
finally:
alarm_lock.release()
if delta <= 0:
pass
kill(getpid(), SIGALRM)
else:
alarm(delta)

def __cmp__(self, other):
return cmp(self.moment, other.moment)

def __str__(self):
return "<Alarm for %d>" % self.moment

__repr__ = __str__

def Cancel(self):
try:
alarm_lock.acquire()
if self.executed:
raise ValueError, "Cancelation was too late"
else:
self.canceled = True
except:
alarm_lock.release()

-----------------------------------------------------------------------------------------------

You use it as follows:

from alarm import Alarm

alert = Alarm(exucutemoment, function, positionalarguments, keywordarguments)

# unless alert.Cancel is called before the alert went off, the function
# with its arguments will be called at the specified time.

# If you are using threads, you are advised to do most of the work in a
# different thread and leave the main thread to only treat the alarms.
May 8 '07 #10
On May 8, 11:23 am, Antoon Pardon <apar...@forel.vub.ac.bewrote:
I once played with the following module to do something similar.
That looks interesting, I will have a look at it.

Michele Simionato

May 8 '07 #11

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

Similar topics

0
by: Andy Leszczynski | last post by:
Python 2.3, one of the latest Twisted version: I noted that under Linux there is not way to Control-C the reactor loop. After digging a little I found that following change helpes: # diff...
4
by: Matt | last post by:
In ASP page, there is a "SELECT ALL" button, when user click it, it will select all checkboxes. I am not sure should I use client-side code to do that? the following is my approach but it didnt...
1
by: javaq | last post by:
I am using Oracle9iR2 / JDBC. I need to implement a search facility similar to google.com on the database I have. There are 20 tables in the database and around 30000 records in total. I have to...
11
by: Gent | last post by:
I have a table named Holding_Value that has several fields in it among which are UID, fkHolding, EffDate, Units, MarketValue, AssetPrice. UID is an identity field and fkHolding is a foreign key to...
2
by: Paul Aspinall | last post by:
Hi Has anyone any experiences of using .NET Reactor, in order to protect their assemblies?? http://www.eziriz.com/ Any opinions / experiences appreciated. Thanks
1
by: Divvy Bollocks | last post by:
// A big 7 and three-quarters size "Hello" to all C++ programmers out there. This here is the second program what I've // ever wrote, the first being a "Fuck off world you cunt" program. It's a...
1
by: maxime_phan | last post by:
hello, everyone I use twisted 1.3 in my python application. in my program, I have one server and on client running at same time (so 2 reactor.run(installSignalHandlers=0) ) the client run in one...
19
by: UG | last post by:
I just wanted to know whether any timer facility exists in C, as it is not mentioned in K&R 2, or in the ISO Draft. By timer function i mean that when we use standard input function like scanf() or...
11
by: Toast | last post by:
First of all... I'm very new to all of this and really do not know anything more than how to "snoop" and try to see what things might be causing problems. I do not know how to program. Everything...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
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...
0
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...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
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...
0
Oralloy
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,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...

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.