[telepathy-butterfly/master] Implemented a lock so only one operation at the time can be performed on a contact

Louis-Francis Ratté-Boulianne louis-francis.ratte-boulianne at collabora.co.uk
Tue Aug 4 14:04:05 PDT 2009


We can not add or remove a contact from the publish and subscribe while one of these operation is already executing
---
 butterfly/channel/contact_list.py |  153 +++++++++++++++++++++++++++++--------
 1 files changed, 121 insertions(+), 32 deletions(-)

diff --git a/butterfly/channel/contact_list.py b/butterfly/channel/contact_list.py
index c7d3be6..f537565 100644
--- a/butterfly/channel/contact_list.py
+++ b/butterfly/channel/contact_list.py
@@ -29,6 +29,71 @@ from butterfly.handle import ButterflyHandleFactory
 __all__ = ['ButterflyContactListChannelFactory']
 
 
+class HandleMutex(object):
+    def __init__(self):
+        self._handles = set()
+        self._keys = {}
+        self._callbacks = {}
+
+    def is_locked(self, handle):
+        return (handle in self._handles)
+
+    def is_owned(self, key, handle):
+        return (handle in self._handles and self._keys[handle] == key)
+
+    def lock(self, key, handle):
+        if self.is_locked(handle):
+            return False
+        print "Locking", handle, key
+        self._handles.add(handle)
+        self._keys[handle] = key
+        return True
+
+    def unlock(self, key, handle):
+        if not self.is_owned(key, handle):
+            return
+        print "Unlocking", handle, key
+        self._handles.remove(handle)
+        del self._keys[handle]
+        callbacks = self._callbacks
+        self._callbacks[handle] = []
+        for callback in callbacks.get(handle, []):
+            callback[0](*callback[1:])
+
+    def add_callback(self, key, handle, callback):
+        if self.is_owned(key, handle):
+            print "Mutex owned", key, handle
+            return
+        if not self.is_locked(handle):
+            callback[0](*callback[1:])
+        else:
+            print "Adding callback", handle
+            self._callbacks.setdefault(handle, []).append(callback)
+
+class Lockable(object):
+    def __init__(self, mutex, key, cb_name):
+        self._mutex = mutex
+        self._key = key
+        self._cb_name = cb_name
+
+    def __call__(self, func):
+        def method(object, handle, *args, **kwargs):
+            def finished_cb(*user_data):
+                self._mutex.unlock(self._key, handle)
+
+            def unlocked_cb():
+                self._mutex.lock(self._key, handle)
+                kwargs[self._cb_name] = finished_cb
+                if func(object, handle, *args, **kwargs):
+                    finished_cb()
+
+            self._mutex.add_callback(self._key, handle, (unlocked_cb,))
+
+        return method
+
+mutex = HandleMutex()
+
+
 def ButterflyContactListChannelFactory(connection, manager, handle, props):
     handle = connection.handle(
         props[telepathy.CHANNEL_INTERFACE + '.TargetHandleType'],
@@ -139,34 +204,46 @@ class ButterflySubscribeListChannel(ButterflyListChannel,
                 telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE, 0)
 
     def AddMembers(self, contacts, message):
-        ab = self._conn.msn_client.address_book
         for h in contacts:
-            handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, h)
-            contact = handle.contact
-            if contact is None:
-                account = handle.account
-            elif contact.is_member(papyon.Membership.FORWARD):
-                continue
-            else:
-                account = contact.account
-            groups = list(handle.pending_groups)
-            handle.pending_groups = set()
-            ab.add_messenger_contact(account,
-                    invite_message=message.encode('utf-8'),
-                    groups=groups)
+            self._add(h, message)
 
     def RemoveMembers(self, contacts, message):
-        ab = self._conn.msn_client.address_book
         for h in contacts:
-            handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, h)
-            contact = handle.contact
-            if contact.is_member(papyon.Membership.FORWARD):
-                ab.delete_contact(contact)
+            self._remove(h)
 
     def _filter_contact(self, contact):
         return (contact.is_member(papyon.Membership.FORWARD) and not
                 contact.is_member(papyon.Membership.PENDING), False, False)
 
+    @Lockable(mutex, 'add_subscribe', 'finished_cb')
+    def _add(self, handle_id, message, finished_cb):
+        handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, handle_id)
+        if handle.contact is not None and \
+           handle.contact.is_member(papyon.Membership.FORWARD):
+            return True
+
+        account = handle.account
+        network = handle.network
+        groups = list(handle.pending_groups)
+        handle.pending_groups = set()
+        ab = self._conn.msn_client.address_book
+        ab.add_messenger_contact(account, network,
+                auto_allow=False,
+                invite_message=message.encode('utf-8'),
+                groups=groups,
+                done_cb=(finished_cb,),
+                failed_cb=(finished_cb,))
+
+    @Lockable(mutex, 'rem_subscribe', 'finished_cb')
+    def _remove(self, handle_id, finished_cb):
+        handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, handle_id)
+        contact = handle.contact
+        if contact is None or not contact.is_member(papyon.Membership.FORWARD):
+            return True
+        ab = self._conn.msn_client.address_book
+        ab.delete_contact(contact, done_cb=(finished_cb,),
+                failed_cb=(finished_cb,))
+
     # papyon.event.ContactEventInterface
     def on_contact_memberships_changed(self, contact):
         handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
@@ -190,21 +267,12 @@ class ButterflyPublishListChannel(ButterflyListChannel,
         self.GroupFlagsChanged(0, 0)
 
     def AddMembers(self, contacts, message):
-        ab = self._conn.msn_client.address_book
-        for contact_handle_id in contacts:
-            contact_handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT,
-                        contact_handle_id)
-            contact = contact_handle.contact
-            ab.accept_contact_invitation(contact, False)
+        for handle_id in contacts:
+            self._add(handle_id, message)
 
     def RemoveMembers(self, contacts, message):
-        ab = self._conn.msn_client.address_book
-        for contact_handle_id in contacts:
-            contact_handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT,
-                        contact_handle_id)
-            contact = contact_handle.contact
-            if contact.is_member(papyon.Membership.PENDING):
-                ab.decline_contact_invitation(contact)
+        for handle_id in contacts:
+            self._remove(handle_id)
 
     def GetLocalPendingMembersWithInfo(self):
         result = []
@@ -223,6 +291,27 @@ class ButterflyPublishListChannel(ButterflyListChannel,
                 contact.is_member(papyon.Membership.PENDING),
                 False)
 
+    @Lockable(mutex, 'add_publish', 'finished_cb')
+    def _add(self, handle_id, message, finished_cb):
+        handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, handle_id)
+        contact = handle.contact
+        account = handle.account
+        network = handle.network
+        ab = self._conn.msn_client.address_book
+        ab.accept_contact_invitation(contact, False,
+                done_cb=(finished_cb,), failed_cb=(finished_cb,))
+
+    @Lockable(mutex, 'rem_publish', 'finished_cb')
+    def _remove(self, handle_id, finished_cb):
+        handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, handle_id)
+        contact = handle.contact
+        ab = self._conn.msn_client.address_book
+        if contact.is_member(papyon.Membership.PENDING):
+            ab.decline_contact_invitation(contact, False, done_cb=finished_cb,
+                    failed_cb=finished_cb)
+        else:
+            return True
+
     # papyon.event.ContactEventInterface
     def on_contact_memberships_changed(self, contact):
         handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
-- 
1.5.6.5




More information about the telepathy-commits mailing list