changeset 24:4e1f27ea527b

First hack at locks for thread safety. Some other minor changes. Signed-off-by: Charly COSTE <changaco@changaco.net>
author Charly COSTE <changaco@changaco.net>
date Thu, 20 Aug 2009 21:52:52 +0200
parents abdb7a2b6c6d
children a9066c416533
files bot.py bridge.py irclib.py
diffstat 3 files changed, 41 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- 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
 	
 	
--- 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)
 		
--- 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