def bisect(a, too_high): """Return the first index i in a such that too_high(a[i]) is true; or len(a) if too_high(x) is true for all elements x in a. The usual use for this function is deciding where to insert element; e.g. my_list.insert(bisect(my_list, lambda elem,x=x: x < elem), x) Requires: a is monotonic non-decreasing in the truth of too_high; i.e. exists[pos] ((all[j in [0, pos)] not too_high(a[j])) and all[j in [pos, len(a))] too_high(a[j])). Put another way: all[(i,j) s.t. i <= j] too_high(a[i]) implies too_high(a[j]). Note that this more or less requires that neither a nor too_high change during execution (say due to writes from another thread). Ensures: The return value equals pos above. I.e.: (all[j in [0, ret)] not too_high(a[j])) and (all[j in [ret, len(a))] too_high(a[j])). """ check.check_has_type(a, types.ListType) check.check_is_callable(too_high) lo = 0 hi = len(a) while lo < hi: mid = (lo+hi) >> 1 if too_high(a[mid]): hi = mid else: lo = mid+1 check.check_assertion((0 <= lo) and (lo <= len(a))) ## Check sortedness: #for elem in a[:lo]: # if too_high(elem): # check.show_bug('utility.bisect: unsorted input list (or bug in bisect code)') #for elem in a[lo:]: # if not too_high(elem): # check.show_bug('utility.bisect: unsorted input list (or bug in bisect code)') return lo
def schedule_mainthread(t_plus, func, *parameters): """ Schedule an action: after t_plus milliseconds, func will be called in the main thread with the parameters given. If func returns true, it will be called again after another t_plus milliseconds""" check.check_has_type(t_plus, types.FloatType) check.check_is_callable(func) daemon_action_lock.acquire() try: new_time = time.time() + t_plus/1000.0 action_item = (new_time, func, parameters, t_plus) pos = bisect(daemon_action_list, lambda elem, new_time=new_time: new_time < elem[0]) daemon_action_list.insert(pos, action_item) finally: daemon_action_lock.release()