1.1 --- a/imip_store.py Sat Sep 12 19:58:59 2015 +0200
1.2 +++ b/imip_store.py Sat Sep 12 20:16:38 2015 +0200
1.3 @@ -554,16 +554,6 @@
1.4 return map(lambda t: FreeBusyPeriod(*t),
1.5 (get_table or self._get_table_atomic)(user, filename, [(4, None)]))
1.6
1.7 - def get_freebusy_for_update(self, user, name=None):
1.8 -
1.9 - """
1.10 - Get free/busy details for the given 'user', locking the table. Dependent
1.11 - code must release this lock regardless of it completing successfully.
1.12 - """
1.13 -
1.14 - self.acquire_lock(user)
1.15 - return self.get_freebusy(user, name, self._get_table)
1.16 -
1.17 def get_freebusy_for_other(self, user, other, get_table=None):
1.18
1.19 "For the given 'user', get free/busy details for the 'other' user."
1.20 @@ -575,17 +565,6 @@
1.21 return map(lambda t: FreeBusyPeriod(*t),
1.22 (get_table or self._get_table_atomic)(user, filename, [(4, None)]))
1.23
1.24 - def get_freebusy_for_other_for_update(self, user, other):
1.25 -
1.26 - """
1.27 - For the given 'user', get free/busy details for the 'other' user,
1.28 - locking the table. Dependent code must release this lock regardless of
1.29 - it completing successfully.
1.30 - """
1.31 -
1.32 - self.acquire_lock(user)
1.33 - return self.get_freebusy_for_other(user, other, self._get_table)
1.34 -
1.35 def set_freebusy(self, user, freebusy, name=None, set_table=None):
1.36
1.37 "For the given 'user', set 'freebusy' details."
1.38 @@ -598,12 +577,6 @@
1.39 map(lambda fb: fb.as_tuple(strings_only=True), freebusy))
1.40 return True
1.41
1.42 - def set_freebusy_in_update(self, user, freebusy, name=None):
1.43 -
1.44 - "For the given 'user', set 'freebusy' details during a compound update."
1.45 -
1.46 - return self.set_freebusy(user, freebusy, name, self._set_table)
1.47 -
1.48 def set_freebusy_for_other(self, user, freebusy, other, set_table=None):
1.49
1.50 "For the given 'user', set 'freebusy' details for the 'other' user."
1.51 @@ -616,19 +589,6 @@
1.52 map(lambda fb: fb.as_tuple(strings_only=True), freebusy))
1.53 return True
1.54
1.55 - def set_freebusy_for_other_in_update(self, user, freebusy, other):
1.56 -
1.57 - """
1.58 - For the given 'user', set 'freebusy' details for the 'other' user during
1.59 - a compound update.
1.60 - """
1.61 -
1.62 - return self.set_freebusy_for_other(user, freebusy, other, self._set_table)
1.63 -
1.64 - # Release methods.
1.65 -
1.66 - release_freebusy = release_lock
1.67 -
1.68 # Tentative free/busy periods related to countering.
1.69
1.70 def get_freebusy_offers(self, user):
1.71 @@ -641,8 +601,9 @@
1.72
1.73 # Expire old offers and save the collection if modified.
1.74
1.75 - l = self.get_freebusy_for_update(user, "freebusy-offers")
1.76 + self.acquire_lock(user)
1.77 try:
1.78 + l = self.get_freebusy(user, "freebusy-offers")
1.79 for fb in l:
1.80 if fb.expires and get_datetime(fb.expires) <= now:
1.81 expired.append(fb)
1.82 @@ -650,35 +611,18 @@
1.83 offers.append(fb)
1.84
1.85 if expired:
1.86 - self.set_freebusy_offers_in_update(user, offers)
1.87 -
1.88 + self.set_freebusy_offers(user, offers)
1.89 finally:
1.90 - self.release_freebusy(user)
1.91 + self.release_lock(user)
1.92
1.93 return offers
1.94
1.95 - def get_freebusy_offers_for_update(self, user):
1.96 -
1.97 - """
1.98 - Get free/busy offers for the given 'user', locking the table. Dependent
1.99 - code must release this lock regardless of it completing successfully.
1.100 - """
1.101 -
1.102 - self.acquire_lock(user)
1.103 - return self.get_freebusy_offers(user)
1.104 -
1.105 def set_freebusy_offers(self, user, freebusy):
1.106
1.107 "For the given 'user', set 'freebusy' offers."
1.108
1.109 return self.set_freebusy(user, freebusy, "freebusy-offers")
1.110
1.111 - def set_freebusy_offers_in_update(self, user, freebusy):
1.112 -
1.113 - "For the given 'user', set 'freebusy' offers during a compound update."
1.114 -
1.115 - return self.set_freebusy_in_update(user, freebusy, "freebusy-offers")
1.116 -
1.117 # Object status details access.
1.118
1.119 def _get_requests(self, user, queue):
2.1 --- a/imiptools/client.py Sat Sep 12 19:58:59 2015 +0200
2.2 +++ b/imiptools/client.py Sat Sep 12 20:16:38 2015 +0200
2.3 @@ -59,6 +59,16 @@
2.4 self.preferences_dir = preferences_dir
2.5 self.preferences = None
2.6
2.7 + # Store-related methods.
2.8 +
2.9 + def acquire_lock(self):
2.10 + self.store.acquire_lock(self.user)
2.11 +
2.12 + def release_lock(self):
2.13 + self.store.release_lock(self.user)
2.14 +
2.15 + # Preferences-related methods.
2.16 +
2.17 def get_preferences(self):
2.18 if not self.preferences and self.user:
2.19 self.preferences = Preferences(self.user, self.preferences_dir)
2.20 @@ -673,17 +683,18 @@
2.21 if user == self.user:
2.22 return
2.23
2.24 - freebusy = self.store.get_freebusy_for_other_for_update(self.user, user)
2.25 + self.acquire_lock()
2.26 try:
2.27 + freebusy = self.store.get_freebusy_for_other(self.user, user)
2.28 fn(freebusy, user, for_organiser, True)
2.29
2.30 # Tidy up any obsolete recurrences.
2.31
2.32 self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid))
2.33 - self.store.set_freebusy_for_other_in_update(self.user, freebusy, user)
2.34 + self.store.set_freebusy_for_other(self.user, freebusy, user)
2.35
2.36 finally:
2.37 - self.store.release_freebusy(self.user)
2.38 + self.release_lock()
2.39
2.40 def update_freebusy_from_organiser(self, organiser):
2.41
3.1 --- a/imiptools/content.py Sat Sep 12 19:58:59 2015 +0200
3.2 +++ b/imiptools/content.py Sat Sep 12 20:16:38 2015 +0200
3.3 @@ -72,7 +72,14 @@
3.4 handler.set_identity(method)
3.5
3.6 if handler.is_usable(method):
3.7 - methods[method](handler)()
3.8 +
3.9 + # Perform the method in a critical section.
3.10 +
3.11 + handler.acquire_lock()
3.12 + try:
3.13 + methods[method](handler)()
3.14 + finally:
3.15 + handler.release_lock()
3.16
3.17 # Handler registry.
3.18
4.1 --- a/imiptools/handlers/common.py Sat Sep 12 19:58:59 2015 +0200
4.2 +++ b/imiptools/handlers/common.py Sat Sep 12 20:16:38 2015 +0200
4.3 @@ -86,29 +86,26 @@
4.4 organiser of an event if 'for_organiser' is set to a true value.
4.5 """
4.6
4.7 - freebusy = self.store.get_freebusy_for_update(self.user)
4.8 - try:
4.9 - # Obtain the attendance attributes for this user, if available.
4.10 + freebusy = self.store.get_freebusy(self.user)
4.11
4.12 - self.update_freebusy_for_participant(freebusy, self.user, for_organiser)
4.13 + # Obtain the attendance attributes for this user, if available.
4.14
4.15 - # Remove original recurrence details replaced by additional
4.16 - # recurrences, as well as obsolete additional recurrences.
4.17 + self.update_freebusy_for_participant(freebusy, self.user, for_organiser)
4.18
4.19 - self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid))
4.20 - self.store.set_freebusy_in_update(self.user, freebusy)
4.21 + # Remove original recurrence details replaced by additional
4.22 + # recurrences, as well as obsolete additional recurrences.
4.23
4.24 - if self.publisher and self.is_sharing():
4.25 - self.publisher.set_freebusy(self.user, freebusy)
4.26 + self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid))
4.27 + self.store.set_freebusy(self.user, freebusy)
4.28
4.29 - # Update free/busy provider information if the event may recur
4.30 - # indefinitely.
4.31 + if self.publisher and self.is_sharing():
4.32 + self.publisher.set_freebusy(self.user, freebusy)
4.33
4.34 - if self.possibly_recurring_indefinitely():
4.35 - self.store.append_freebusy_provider(self.user, self.obj)
4.36 + # Update free/busy provider information if the event may recur
4.37 + # indefinitely.
4.38
4.39 - finally:
4.40 - self.store.release_freebusy(self.user)
4.41 + if self.possibly_recurring_indefinitely():
4.42 + self.store.append_freebusy_provider(self.user, self.obj)
4.43
4.44 return True
4.45
4.46 @@ -116,42 +113,36 @@
4.47
4.48 "Remove free/busy information when handling an object."
4.49
4.50 - freebusy = self.store.get_freebusy_for_update(self.user)
4.51 - try:
4.52 - self.remove_from_freebusy(freebusy)
4.53 - self.remove_freebusy_for_recurrences(freebusy)
4.54 - self.store.set_freebusy_in_update(self.user, freebusy)
4.55 + freebusy = self.store.get_freebusy(self.user)
4.56
4.57 - if self.publisher and self.is_sharing():
4.58 - self.publisher.set_freebusy(self.user, freebusy)
4.59 + self.remove_from_freebusy(freebusy)
4.60 + self.remove_freebusy_for_recurrences(freebusy)
4.61 + self.store.set_freebusy(self.user, freebusy)
4.62
4.63 - # Update free/busy provider information if the event may recur
4.64 - # indefinitely.
4.65 + if self.publisher and self.is_sharing():
4.66 + self.publisher.set_freebusy(self.user, freebusy)
4.67
4.68 - if self.possibly_recurring_indefinitely():
4.69 - self.store.remove_freebusy_provider(self.user, self.obj)
4.70 + # Update free/busy provider information if the event may recur
4.71 + # indefinitely.
4.72
4.73 - finally:
4.74 - self.store.release_freebusy(self.user)
4.75 + if self.possibly_recurring_indefinitely():
4.76 + self.store.remove_freebusy_provider(self.user, self.obj)
4.77
4.78 def update_event_in_freebusy_offers(self):
4.79
4.80 "Update free/busy offers when handling an object."
4.81
4.82 - freebusy = self.store.get_freebusy_offers_for_update(self.user)
4.83 - try:
4.84 - # Obtain the attendance attributes for this user, if available.
4.85 + freebusy = self.store.get_freebusy_offers(self.user)
4.86
4.87 - self.update_freebusy_for_participant(freebusy, self.user)
4.88 + # Obtain the attendance attributes for this user, if available.
4.89 +
4.90 + self.update_freebusy_for_participant(freebusy, self.user)
4.91
4.92 - # Remove original recurrence details replaced by additional
4.93 - # recurrences, as well as obsolete additional recurrences.
4.94 + # Remove original recurrence details replaced by additional
4.95 + # recurrences, as well as obsolete additional recurrences.
4.96
4.97 - self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid))
4.98 - self.store.set_freebusy_offers_in_update(self.user, freebusy)
4.99 -
4.100 - finally:
4.101 - self.store.release_freebusy(self.user)
4.102 + self.remove_freebusy_for_recurrences(freebusy, self.store.get_recurrences(self.user, self.uid))
4.103 + self.store.set_freebusy_offers(self.user, freebusy)
4.104
4.105 return True
4.106
4.107 @@ -159,14 +150,11 @@
4.108
4.109 "Remove free/busy offers when handling an object."
4.110
4.111 - freebusy = self.store.get_freebusy_offers_for_update(self.user)
4.112 - try:
4.113 - self.remove_from_freebusy(freebusy)
4.114 - self.remove_freebusy_for_recurrences(freebusy)
4.115 - self.store.set_freebusy_offers_in_update(self.user, freebusy)
4.116 + freebusy = self.store.get_freebusy_offers(self.user)
4.117
4.118 - finally:
4.119 - self.store.release_freebusy(self.user)
4.120 + self.remove_from_freebusy(freebusy)
4.121 + self.remove_freebusy_for_recurrences(freebusy)
4.122 + self.store.set_freebusy_offers(self.user, freebusy)
4.123
4.124 return True
4.125
5.1 --- a/imiptools/handlers/person.py Sat Sep 12 19:58:59 2015 +0200
5.2 +++ b/imiptools/handlers/person.py Sat Sep 12 20:16:38 2015 +0200
5.3 @@ -291,12 +291,9 @@
5.4 period = Period(dtstart, dtend, self.get_tzid())
5.5
5.6 for sender, sender_attr in senders:
5.7 - stored_freebusy = self.store.get_freebusy_for_other_for_update(self.user, sender)
5.8 - try:
5.9 - replace_overlapping(stored_freebusy, period, freebusy)
5.10 - self.store.set_freebusy_for_other_in_update(self.user, stored_freebusy, sender)
5.11 - finally:
5.12 - self.store.release_freebusy(self.user)
5.13 + stored_freebusy = self.store.get_freebusy_for_other(self.user, sender)
5.14 + replace_overlapping(stored_freebusy, period, freebusy)
5.15 + self.store.set_freebusy_for_other(self.user, stored_freebusy, sender)
5.16
5.17 class Freebusy(PersonFreebusy):
5.18
6.1 --- a/imiptools/handlers/resource.py Sat Sep 12 19:58:59 2015 +0200
6.2 +++ b/imiptools/handlers/resource.py Sat Sep 12 20:16:38 2015 +0200
6.3 @@ -103,58 +103,54 @@
6.4
6.5 periods = self.obj.get_periods(tzid, self.get_window_end())
6.6
6.7 - freebusy = self.store.get_freebusy_for_update(self.user)
6.8 - try:
6.9 - offers = self.store.get_freebusy_offers(self.user)
6.10 + freebusy = self.store.get_freebusy(self.user)
6.11 + offers = self.store.get_freebusy_offers(self.user)
6.12 +
6.13 + # Check the periods against any scheduled events and against
6.14 + # any outstanding offers.
6.15
6.16 - # Check the periods against any scheduled events and against
6.17 - # any outstanding offers.
6.18 + scheduled = self.can_schedule(freebusy, periods)
6.19 + scheduled = scheduled and self.can_schedule(offers, periods)
6.20
6.21 - scheduled = self.can_schedule(freebusy, periods)
6.22 - scheduled = scheduled and self.can_schedule(offers, periods)
6.23 + # Where the corrected object can be scheduled, issue a counter
6.24 + # request.
6.25
6.26 - # Where the corrected object can be scheduled, issue a counter
6.27 - # request.
6.28 + if scheduled and corrected:
6.29 + method = "COUNTER"
6.30
6.31 - if scheduled and corrected:
6.32 - method = "COUNTER"
6.33 + # Find the next available slot if the event cannot be scheduled.
6.34
6.35 - # Find the next available slot if the event cannot be scheduled.
6.36 -
6.37 - #elif not scheduled and len(periods) == 1:
6.38 + #elif not scheduled and len(periods) == 1:
6.39
6.40 - # # Find a free period, update the object with the details.
6.41 + # # Find a free period, update the object with the details.
6.42
6.43 - # duration = periods[0].get_duration()
6.44 - # free = invert_freebusy(freebusy)
6.45 + # duration = periods[0].get_duration()
6.46 + # free = invert_freebusy(freebusy)
6.47
6.48 - # for found in periods_from(free, periods[0]):
6.49 - # # NOTE: Correct the found period first.
6.50 - # if found.get_duration() >= duration
6.51 - # scheduled = True
6.52 - # method = "COUNTER"
6.53 - # # NOTE: Set the period using the original duration.
6.54 - # break
6.55 + # for found in periods_from(free, periods[0]):
6.56 + # # NOTE: Correct the found period first.
6.57 + # if found.get_duration() >= duration
6.58 + # scheduled = True
6.59 + # method = "COUNTER"
6.60 + # # NOTE: Set the period using the original duration.
6.61 + # break
6.62
6.63 - # Update the participation of the resource in the object.
6.64 -
6.65 - attendee_attr = self.update_participation(self.obj,
6.66 - scheduled and "ACCEPTED" or "DECLINED")
6.67 + # Update the participation of the resource in the object.
6.68
6.69 - # Update free/busy information.
6.70 + attendee_attr = self.update_participation(self.obj,
6.71 + scheduled and "ACCEPTED" or "DECLINED")
6.72
6.73 - if method == "REPLY":
6.74 - self.update_event_in_freebusy(for_organiser=False)
6.75 - self.remove_event_from_freebusy_offers()
6.76 + # Update free/busy information.
6.77
6.78 - # For countered proposals, record the offer in the resource's
6.79 - # free/busy collection.
6.80 + if method == "REPLY":
6.81 + self.update_event_in_freebusy(for_organiser=False)
6.82 + self.remove_event_from_freebusy_offers()
6.83
6.84 - elif method == "COUNTER":
6.85 - self.update_event_in_freebusy_offers()
6.86 + # For countered proposals, record the offer in the resource's
6.87 + # free/busy collection.
6.88
6.89 - finally:
6.90 - self.store.release_freebusy(self.user)
6.91 + elif method == "COUNTER":
6.92 + self.update_event_in_freebusy_offers()
6.93
6.94 # Set the complete event or an additional occurrence.
6.95