static int mm_port_receive_internal(struct mm_port *port, uint32_t *start, uint32_t count, bool blocking) { ENTER(); ASSERT(count <= (MM_PORT_SIZE / 2)); ASSERT(port->task == mm_task_selfptr()); int rc = 0; again: mm_regular_lock(&port->lock); if (port->count < count) { mm_regular_unlock(&port->lock); if (blocking) { mm_task_block(); mm_task_testcancel(); goto again; } else { rc = -1; goto leave; } } uint32_t *ring_ptr = &port->ring[port->start]; port->count -= count; if (unlikely((port->start + count) > MM_PORT_SIZE)) { uint32_t top_count = MM_PORT_SIZE - port->start; count -= top_count; while (top_count--) { *start++ = *ring_ptr++; } ring_ptr = &port->ring[0]; port->start = 0; } port->start = (port->start + count) % MM_PORT_SIZE; while (count--) { *start++ = *ring_ptr++; } mm_waitset_broadcast(&port->blocked_senders, &port->lock); leave: LEAVE(); return rc; }
mm_task_combiner_execute(struct mm_task_combiner *combiner, mm_combiner_routine_t routine, uintptr_t data) { ENTER(); // Disable cancellation as the enqueue algorithm cannot be // safely undone if interrupted in the middle. int cancelstate; mm_task_setcancelstate(MM_TASK_CANCEL_DISABLE, &cancelstate); // Get per-core queue of pending requests. mm_core_t core = mm_core_self(); struct mm_list *wait_queue = MM_THREAD_LOCAL_DEREF(core, combiner->wait_queue); // Add the current request to the per-core queue. struct mm_task *task = mm_task_selfptr(); task->flags |= MM_TASK_COMBINING; mm_list_append(wait_queue, &task->wait_queue); // Wait until the current request becomes the head of the // per-core queue. while (mm_list_head(wait_queue) != &task->wait_queue) mm_task_block(); mm_combiner_execute(&combiner->combiner, routine, data); // Remove the request from the per-core queue. mm_list_delete(&task->wait_queue); task->flags &= ~MM_TASK_COMBINING; // If the per-core queue is not empty then let its new head take // the next turn. if (!mm_list_empty(wait_queue)) { struct mm_link *link = mm_list_head(wait_queue); task = containerof(link, struct mm_task, wait_queue); mm_task_run(task); } // Restore cancellation. mm_task_setcancelstate(cancelstate, NULL); LEAVE(); }