# HG changeset patch # User Charly COSTE # Date 1250797972 -7200 # Node ID 4e1f27ea527b3204618bd1d60fa2646d5a19dee1 # Parent abdb7a2b6c6d30cca65d0919cb63966c6cff7bc9 First hack at locks for thread safety. Some other minor changes. Signed-off-by: Charly COSTE diff --git a/bot.py b/bot.py --- a/bot.py +++ b/bot.py @@ -24,7 +24,7 @@ version = 0, 1 import irclib import xmppony as xmpp -from threading import Thread +import threading from bridge import * from time import sleep import re @@ -73,16 +73,20 @@ class bot(Thread): """[Internal] XMPP infinite loop.""" while True: try: + self.xmpp_c.lock.acquire() self.xmpp_c.Process(0.5) + self.xmpp_c.lock.release() try: for c in self.xmpp_connections.itervalues(): if hasattr(c, 'Process'): + c.lock.acquire() c.Process(0.5) + c.lock.release() else: sleep(0.5) except RuntimeError: pass - except xml.parsers.expat.ExpatError: + except (xml.parsers.expat.ExpatError, xmpp.protocol.XMLNotWellFormed): self.error('=> Debug: received invalid stanza', debug=True) continue @@ -243,8 +247,9 @@ class bot(Thread): nickname = None - if '!' in event.source(): - nickname = event.source().split('!')[0] + if event.source() != None: + if '!' in event.source(): + nickname = event.source().split('!')[0] # Events that we want to ignore only in some cases @@ -415,6 +420,8 @@ class bot(Thread): return c self.error('===> Debug: opening new XMPP connection for "'+nickname+'"', debug=True) c = xmpp.client.Client(self.bare_jid.getDomain(), debug=[]) + c.lock = threading.Lock() + c.lock.acquire() self.xmpp_connections[nickname] = c c.used_by = 1 c.nickname = nickname @@ -424,6 +431,7 @@ class bot(Thread): c.RegisterHandler('iq', self._xmpp_iq_handler) c.RegisterHandler('message', self._xmpp_message_handler) c.sendInitPresence() + c.lock.release() return c diff --git a/bridge.py b/bridge.py --- a/bridge.py +++ b/bridge.py @@ -22,6 +22,7 @@ from participant import * from encoding import * import traceback import re +import threading class NoSuchParticipantException(Exception): pass @@ -35,6 +36,7 @@ class bridge: _warning = 3 _error = 4 _nothing = 5 + _modes = ['normal', 'limited', 'minimal'] def __init__(self, owner_bot, xmpp_room_jid, irc_room, irc_server, mode, say_level, irc_port=6667): @@ -48,10 +50,12 @@ class bridge: else: raise Exception('[Error] "'+say_level+'" is not a correct value for a bridge\'s "say_level" attribute') self.participants = [] - if mode not in ['normal', 'limited', 'minimal']: + if mode not in self.__class__._modes: raise Exception('[Error] "'+mode+'" is not a correct value for a bridge\'s "mode" attribute') self.mode = mode + self.lock = threading.Lock() + # Join XMPP room try: self.xmpp_room = xmpp.muc(xmpp_room_jid) @@ -67,7 +71,8 @@ class bridge: except: self.bot.error('[Error] joining IRC room failed') raise - self.bot.error('[Notice] bridge "'+str(self)+'" is running in '+self.mode+' mode') + + self.bot.error('[Notice] bridge "'+str(self)+'" is running in '+self.mode+' mode and a say_level of "'+say_level+'"') def _irc_nick_callback(self, error, arguments=[]): @@ -135,18 +140,23 @@ class bridge: def getParticipant(self, nickname): """Returns a participant object if there is a participant using nickname in the bridge. Raises a NoSuchParticipantException otherwise.""" + self.lock.acquire() for participant_ in self.participants: if participant_.nickname == nickname: + self.lock.release() return participant_ + self.lock.release() raise NoSuchParticipantException('there is no participant using the nickname "'+nickname+'" in this bridge') def get_participants_nicknames_list(self, protocols=['irc', 'xmpp']): """Returns a list of the nicknames of the bridge's participants that are connected on the XMPP side.""" + self.lock.acquire() participants_nicknames = [] for p in self.participants: if p.protocol in protocols: participants_nicknames.append('"'+p.nickname+'"') + self.lock.release() return participants_nicknames @@ -184,6 +194,7 @@ class bridge: self.bot.error('===> Debug: "'+nickname+'" was on both sides of bridge "'+str(self)+'" but left '+left_protocol, debug=True) elif was_on_both == False: + self.lock.acquire() self.bot.error('===> Debug: removing participant "'+nickname+'" from bridge "'+str(self)+'"', debug=True) self.participants.remove(p) p.leave(leave_message) @@ -192,14 +203,15 @@ class bridge: for p in self.participants: if p.protocol == 'xmpp': i += 1 + self.lock.release() if left_protocol == 'xmpp': if self.irc_connections_limit != -1 and self.irc_connections_limit > i: self.switchFromLimitedToNormalMode() - if self.mode != 'normal' and self.say_participants_list == True: + if self.mode != 'normal': xmpp_participants_nicknames = self.get_participants_nicknames_list(protocols=['xmpp']) self.say('[Info] Participants on XMPP: '+' '.join(xmpp_participants_nicknames), on_xmpp=False) elif left_protocol == 'irc': - if self.mode == 'minimal' and self.say_participants_list == True: + if self.mode == 'minimal': irc_participants_nicknames = self.get_participants_nicknames_list(protocols=['irc']) self.say('[Info] Participants on IRC: '+' '.join(irc_participants_nicknames), on_irc=False) diff --git a/irclib.py b/irclib.py --- a/irclib.py +++ b/irclib.py @@ -69,6 +69,7 @@ import string import sys import time import types +import threading VERSION = 0, 4, 8 DEBUG = 0 @@ -189,7 +190,9 @@ class IRC: for s in sockets: for c in self.connections: if s == c._get_socket(): + c.lock.acquire() c.process_data() + c.lock.release() def process_timeout(self): """Called when a timeout notification is due. @@ -336,7 +339,8 @@ class IRC: def _remove_connection(self, connection): """[Internal]""" - self.connections.remove(connection) + if connection in self.connections: + self.connections.remove(connection) if self.fn_to_remove_socket: self.fn_to_remove_socket(connection._get_socket()) @@ -421,13 +425,19 @@ class ServerConnection(Connection): Returns the ServerConnection object. """ + + if self.connected == True: + self.lock.acquire() self.used_by += 1 self.irclibobj.bot.error('===> Debug: using existing IRC connection for '+str(self)+', this connection is now used by '+str(self.used_by)+' bridges', debug=True) self.nick(self.real_nickname, callback=nick_callback) - return + self.lock.release() + return self + self.lock = threading.Lock() + self.lock.acquire() self.nick_callbacks = [] self.previous_buffer = "" self.handlers = {} @@ -464,6 +474,7 @@ class ServerConnection(Connection): self.pass_(self.password) self.nick(self.nickname, callback=nick_callback) self.user(self.username, self.ircname) + self.lock.release() return self