'SWF timeouts can happen on different objects (workflow executions,
activity tasks, decision tasks) and should be processed in order.
A specific timeout can change the workflow execution state and have an
impact on other timeouts: for instance, if the workflow execution
timeouts, subsequent timeouts on activity or decision tasks are
irrelevant ; if an activity task timeouts, other timeouts on this task
are irrelevant, and a new decision is fired, which could well timeout
before the end of the workflow.
So the idea here is to find the earliest timeout that would have been
triggered, process it, then make the workflow state progress and repeat
the whole process.'

def _process_timeouts(self):

    timeout_candidates = []
    timeout_candidates.append(self.first_timeout())
    for task in self.decision_tasks:
        timeout_candidates.append(task.first_timeout())
    for task in self.activity_tasks:
        timeout_candidates.append(task.first_timeout())
    timeout_candidates = list(filter(None, timeout_candidates))
    first_timeout = None
    if timeout_candidates:
        first_timeout = min(timeout_candidates, key=(lambda t: t.timestamp))
    if first_timeout:
        should_schedule_decision_next = False
        if isinstance(first_timeout.obj, WorkflowExecution):
            self.timeout(first_timeout)
        elif isinstance(first_timeout.obj, DecisionTask):
            self.timeout_decision_task(first_timeout)
            should_schedule_decision_next = True
        elif isinstance(first_timeout.obj, ActivityTask):
            self.timeout_activity_task(first_timeout)
            should_schedule_decision_next = True
        else:
            raise NotImplementedError(u'Unhandled    timeout    object')
        if should_schedule_decision_next:
            self.schedule_decision_task()
        self._process_timeouts()
