def is_gte_than_lock_holding_events(self,waiting_event_priority): ''' @param {priority} waiting_event_priority --- @returns {bool} --- Returns True if waiting_event_uuid is greater than or equal to all other events that are currently holding read or read/write locks on data. ''' # check write lock if ((self.write_lock_holder is not None) and (not gte_priority(waiting_event_priority,self.write_lock_holder.cached_priority))): return False # check read locks for read_lock_uuid in self.read_lock_holders: read_lock_holder_event_cached_priority = self.read_lock_holders[read_lock_uuid] if not gte_priority( waiting_event_priority,read_lock_holder_event_cached_priority.cached_priority): return False return True
def try_schedule_read_waiting_event(self,waiting_event): ''' CALLED FROM WITHIN LOCK Check if can schedule the read event waiting_event Should be able to schedule if: 1) There is not a write lock holder or 2) There is a write lock holder that is not currently in two phase commit, and has a uuid that is less than our uuid. a) check if write lock holder is younger (ie, it should be preempted). b) If it is younger and it's not in two phase commit, then go ahead and revoke it. @returns {bool} --- Returns True if could schedule read waiting event. False if could not. ''' # CASE 1 if self.write_lock_holder is None: self.read_lock_holders[waiting_event.event.uuid] = ( EventCachedPriorityObj(waiting_event.event,waiting_event.cached_priority)) waiting_event.unwait(self) return True # CASE 2 # b) If it is younger and it's not in two phase commit, # then go ahead and revoke it. # 2 a --- check if write lock holder is younger (ie, it should be # preempted). if gte_priority( self.write_lock_holder.cached_priority,waiting_event.cached_priority): # do not preempt write lock: it has been around longer return False # 2 b --- If it is younger and it's not in two phase commit, # then go ahead and revoke it. if not self.write_lock_holder.event.can_backout_and_hold_lock(): # cannot backout write lock holder return False # Can backout write lock holder: # 1) Actually perform backout of writing event # 2) Clean up write lock holder and read lock holder state # associated with write lock # 3) Waiting event gets included in read lock holder # 4) Unjam waiting event's read queue, which returns value. # 1 self.obj_request_backout_and_release_lock(write_lock_holder.event) # following code will remove all write lock holders and read # lock holders. # 2 self.write_lock_holder = None self.read_lock_holders = {} # 3 self.read_lock_holders[waiting_event.event.uuid] = ( EventCachedPriorityObj(waiting_event.event,waiting_event.cached_priority)) # 4 waiting_event.unwait(self) return True
def acquire_read_lock(self,active_event): ''' DOES NOT ASSUME ALREADY WITHIN LOCK @returns {DataWrapper object} Algorithms: 0) If already holds a write lock on the variable, then return the dirty value associated with event. 1) If already holds a read lock on variable, returns the value immediately. 2) If does not hold a read lock on variable, then attempts to acquire one. If worked, then return the variable. When attempting to acquire read lock: a) Checks if there is any event holding a write lock. If there is not, then adds itself to read lock holder dict. b) If there is another event holding a write lock, then check if uuid of the read lock requester is >= uuid of the write lock. If it is, then try to backout the holder of write lock. c) If cannot backout or have a lesser uuid, then create a waiting event and block while listening to queue. (same as #3) 3) If did not work, then create a waiting event and a queue and block while listening on that queue. Blocks until has acquired. ''' self._lock() # Each event has a priority associated with it. This priority # can change when an event gets promoted to be boosted. To # avoid the read/write conflicts this might cause, at the # beginning of acquring read lock, get priority and use that # for majority of time trying to acquire read lock. If cached # priority ends up in WaitingElement, another thread can later # update it. cached_priority = active_event.get_priority() # must be careful to add obj to active_event's touched_objs. # That way, if active_event needs to backout, we're guaranteed # that the state we've allocated for accounting the # active_event is cleaned up here. if not self.insert_in_touched_objs(active_event): self._unlock() raise util.BackoutException() # check 0 above if ((self.write_lock_holder is not None) and (active_event.uuid == self.write_lock_holder.event.uuid)): to_return = self.dirty_val self._unlock() return to_return # also check 1 above if active_event.uuid in self.read_lock_holders: # already allowed to read the variable to_return = self.val self._unlock() return to_return # must be careful to add obj to active_event's touched_objs. # That way, if active_event needs to backout, we're guaranteed # that the state we've allocated for accounting the # active_event is cleaned up here. if not self.insert_in_touched_objs(active_event): self._unlock() raise util.BackoutException() # Check 2 from above # check 2a if self.write_lock_holder is None: to_return = self.val self.read_lock_holders[active_event.uuid] = ( EventCachedPriorityObj(active_event,cached_priority)) self._unlock() return to_return # check 2b if gte_priority(cached_priority, self.write_lock_holder.cached_priority): # backout write lock if can if self.write_lock_holder.event.can_backout_and_hold_lock(): # actually back out the event self.obj_request_backout_and_release_lock(self.write_lock_holder.event) # add active event as read lock holder and return self.dirty_val = None self.write_lock_holder = None self.read_lock_holders = {} self.read_lock_holders[active_event.uuid] = ( EventCachedPriorityObj(active_event,cached_priority)) to_return = self.val self._unlock() return to_return # Condition 2c + 3 # create a waiting read element waiting_element = WaitingElement( active_event,cached_priority,True,self.data_wrapper_constructor,self.peered) self.waiting_events[active_event.uuid] = waiting_element self._unlock() return waiting_element.queue.get()