📄 mainloop.py
字号:
# # ***** BEGIN LICENSE BLOCK *****# Source last modified: $Id: mainloop.py,v 1.2 2004/07/07 22:00:04 hubbe Exp $# # Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.# # The contents of this file, and the files included with this file,# are subject to the current version of the RealNetworks Public# Source License (the "RPSL") available at# http://www.helixcommunity.org/content/rpsl unless you have licensed# the file under the current version of the RealNetworks Community# Source License (the "RCSL") available at# http://www.helixcommunity.org/content/rcsl, in which case the RCSL# will apply. You may also obtain the license terms directly from# RealNetworks. You may not use this file except in compliance with# the RPSL or, if you have a valid RCSL with RealNetworks applicable# to this file, the RCSL. Please see the applicable RPSL or RCSL for# the rights, obligations and limitations governing use of the# contents of the file.# # Alternatively, the contents of this file may be used under the# terms of the GNU General Public License Version 2 or later (the# "GPL") in which case the provisions of the GPL are applicable# instead of those above. If you wish to allow use of your version of# this file only under the terms of the GPL, and not to allow others# to use your version of this file under the terms of either the RPSL# or RCSL, indicate your decision by deleting the provisions above# and replace them with the notice and other provisions required by# the GPL. If you do not delete the provisions above, a recipient may# use your version of this file under the terms of any one of the# RPSL, the RCSL or the GPL.# # This file is part of the Helix DNA Technology. RealNetworks is the# developer of the Original Code and owns the copyrights in the# portions it created.# # This file, and the files included with this file, is distributed# and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY# KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS# ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET# ENJOYMENT OR NON-INFRINGEMENT.# # Technology Compatibility Kit Test Suite(s) Location:# http://www.helixcommunity.org/content/tck# # Contributor(s):# # ***** END LICENSE BLOCK *****# """Implements a async, thread-safe scheduling engine. This file is used inbuild/lib, and in mserver/. It should be kept syncronized to the sameversion."""import osimport stringimport socketimport errnoimport selectimport timeimport thread## callback classesclass Callback: def __init__(self, callback, data): self.enabled_lock = thread.allocate_lock() self.enabled = 1 self.recursion_count = 0 self.callback = callback self.data = data def enable(self): self.enabled_lock.acquire(1) self.enabled = 1 self.enabled_lock.release() def disable(self): self.enabled_lock.acquire(1) self.enabled = 0 self.enabled_lock.release() def dispatch(self): self.enabled_lock.acquire(1) enabled = self.enabled self.enabled_lock.release() if not enabled: return self.recursion_count = self.recursion_count + 1 self.execute() self.recursion_count = self.recursion_count - 1 def execute(self): if self.data: self.callback(self.data) else: self.callback()class TimeoutCallback(Callback): def __init__(self, seconds, callback, data): Callback.__init__(self, callback, data) self.expiration = time.time() + float(seconds) def is_expired(self, time): return time > self.expiration def expires(self, time): return self.expiration - timeclass IOCallback(Callback): def __init__(self, sock, callback, data): Callback.__init__(self, callback, data) self.sock = sock def execute(self): if self.data: self.callback(self.sock, self.data) else: self.callback(self.sock)class MainLoop: error = 'MainLoop.error' def __init__(self): self.done_flag = 0 ## list of timeout callbacks self.timeout_list = [] ## list of idle callbacks self.idle_list = [] ## sockets for select() reading/writing self.read_hash = {} self.read_list = [] self.write_hash = {} self.write_list = [] def main(self): ## go until done while not self.done_flag: self.main_loop_iteration() ## give re-birth to the network engine, thereby de-referencing ## and freeing everything that the current network engine references delete_engine() def done(self): self.done_flag = 1 def add_timeout_cb(self, seconds, callback, data): ## add the timeout timeout = TimeoutCallback(seconds, callback, data) ## add to the timeout_list index = 0 for index in range(len(self.timeout_list)): if self.timeout_list[index].expiration > timeout.expiration: break self.timeout_list.insert(index, timeout) return id(timeout) def remove_timeout_cb(self, id): for timeout in self.timeout_list: if id(timeout) == id: self.timeout_list.remove(timeout) break def add_read_cb(self, sock, callback, data): try: sock.fileno() except AttributeError: raise MainLoop.error, 'bad type %s' % (str(type(sock))) if not self.read_hash.has_key(sock): self.read_list.append(sock) self.read_hash[sock] = IOCallback(sock, callback, data) def remove_read_cb(self, sock): try: self.read_hash[sock].disable() del self.read_hash[sock] except KeyError: pass else: self.read_list.remove(sock) def add_write_cb(self, sock, callback, data): try: sock.fileno() except AttributeError: raise MainLoop.error, 'bad type %s' % (str(type(sock))) if not self.write_hash.has_key(sock): self.write_list.append(sock) self.write_hash[sock] = IOCallback(sock, callback, data) def remove_write_cb(self, sock): try: self.write_hash[sock].disable() del self.write_hash[sock] except KeyError: pass else: self.write_list.remove(sock) def calculate_next_timeout(self): ## no timeouts if not len(self.timeout_list): return None ## compute timeout from soonest timeout callback seconds = self.timeout_list[0].expires(time.time()) if seconds < 0: seconds = 0 return seconds def select(self, seconds): ## only run select() if there are valid sockets if len(self.read_list) or len(self.write_list): return select.select(self.read_list, self.write_list, [], seconds) if seconds: time.sleep(seconds) return [], [], [] def main_loop_iteration(self): callback_list = self.main_loop_poll() self.main_loop_dispatch(callback_list) def main_loop_poll(self): callback_list = [] seconds = self.calculate_next_timeout() ## make the select() call; catch a lot of potential errors ## and prune the select lists if needed try: read_list, write_list, exception_list = self.select(seconds) except select.error, e: sys.stderr.write('select.error: %s' % (str(e))) return self.find_bad_sockets() except ValueError: sys.stderr.write('ValueError') return self.find_bad_sockets() except TypeError: sys.stderr.write('TypeError') return self.find_bad_sockets() else: for sock in read_list: try: callback_list.append(self.read_hash[sock]) except KeyError: pass for sock in write_list: try: callback_list.append(self.write_hash[sock]) except KeyError: pass ## add in expired timeouts callback_list = callback_list + self.get_expired_timeouts() return callback_list def main_loop_dispatch(self, callback_list): for callback in callback_list: callback.dispatch() def find_bad_sockets(self): callback_list = [] ## go through the list of all sockets in the networking engine ## and preform a getsockname() on them; if this throws an ## exception, then the socket is invalid and should be removed for sock in self.read_list[:]: try: sock.getsockname() except socket.error: try: callback_list.append(self.read_hash[sock]) except KeyError: continue except AttributeError: continue for sock in self.write_list[:]: try: sock.getsockname() except socket.error: try: callback_list.append(self.write_hash[sock]) except KeyError: continue except AttributeError: continue return callback_list def get_expired_timeouts(self): ## if there are no timeouts
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -