Completed an UNTESTED implementation. Did I say it was untested already?!

Until, it's obvioulsy not ready for prime time: using it to queue AcpiOsExecute() event/method callbacks... 


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19761 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Philippe Houdoin
2007-01-10 01:08:44 +00:00
parent b795f04c46
commit 7d2635cbc0
+60 -19
View File
@@ -32,52 +32,79 @@ typedef struct {
typedef struct { typedef struct {
dpc_func function; dpc_func function;
void *arg; void *arg;
} dpc; } dpc_slot;
typedef struct { typedef struct {
thread_id thread; thread_id thread;
sem_id wakeup_sem; sem_id wakeup_sem;
int max_dpc;
spinlock lock; spinlock lock;
dpc list[1]; int size;
// max_dpc - 1 follow int head;
int tail;
dpc_slot slots[0];
// size * slots follow
} dpc_queue; } dpc_queue;
static int32 static int32
dpc_thread(void *arg) dpc_thread(void *arg)
{ {
// TODO: dpc_queue *queue = arg;
// loop on acquire_sem(queue->wakeup_sem), exit on bad sem
// inner loop: disable interrupt, find first slot not empty, empty it, restore interrupts if (!queue)
// and call the dpc return -1;
// Let's wait forever/until sem death for DPC to show up
while (acquire_sem(queue->wakeup_sem) == B_OK) {
cpu_status former;
int i;
dpc_slot call;
// grab the next dpc slot
former = disable_interrupts();
acquire_spinlock(&queue->lock);
call = queue->slots[queue->head];
queue->head = (queue->head++) % queue->size;
release_spinlock(&queue->lock);
restore_interrupts(former);
if (call.function)
call.function(call.arg);
}
// Let's die quietly, ignored by all...
return 0; return 0;
} }
// ---- // ----
static void * static void *
new_dpc_thread(const char *name, long priority, int max_dpc) new_dpc_thread(const char *name, long priority, int queue_size)
{ {
char str[64]; char str[64];
dpc_queue *queue; dpc_queue *queue;
int i;
queue = malloc(sizeof(dpc_queue) + (max_dpc - 1) * sizeof(dpc)); queue = malloc(sizeof(dpc_queue) + queue_size * sizeof(dpc_slot));
if (!queue) if (!queue)
return NULL; return NULL;
queue->max_dpc = max_dpc; queue->head = queue->tail = 0;
queue->size = queue_size;
queue->lock = 0; queue->lock = 0;
for (i = 0; i < max_dpc; i++) {
queue->list[i].function = NULL;
queue->list[i].arg = NULL;
}
strncpy(str, name, sizeof(str)); strncpy(str, name, sizeof(str));
strncat(str, "_wakeup_sem", sizeof(str)); strncat(str, "_wakeup_sem", sizeof(str));
queue->wakeup_sem = create_sem(-1, str); queue->wakeup_sem = create_sem(-1, str);
if (queue->wakeup_sem < B_OK) {
free(queue);
return NULL;
}
set_sem_owner(queue->wakeup_sem, B_SYSTEM_TEAM); set_sem_owner(queue->wakeup_sem, B_SYSTEM_TEAM);
// Fire a kernel thread to do actually do
// the deferred procedure calls
queue->thread = spawn_kernel_thread(dpc_thread, name, priority, queue); queue->thread = spawn_kernel_thread(dpc_thread, name, priority, queue);
if (queue->thread < 0) { if (queue->thread < 0) {
delete_sem(queue->wakeup_sem); delete_sem(queue->wakeup_sem);
@@ -99,6 +126,7 @@ delete_dpc_thread(void *thread)
if (!queue) if (!queue)
return B_BAD_VALUE; return B_BAD_VALUE;
// Wakeup the thread by murdering its favorite semaphore
delete_sem(queue->wakeup_sem); delete_sem(queue->wakeup_sem);
wait_for_thread(queue->thread, &exit_value); wait_for_thread(queue->thread, &exit_value);
free(queue); free(queue);
@@ -111,13 +139,26 @@ static status_t
queue_dpc(void *thread, dpc_func function, void *arg) queue_dpc(void *thread, dpc_func function, void *arg)
{ {
dpc_queue *queue = thread; dpc_queue *queue = thread;
cpu_status former;
if (!queue) if (!queue)
return B_BAD_VALUE; return B_BAD_VALUE;
// TODO: acquire queue (spin)lock, find an empty slot, release lock and // Take measure to be safe to be called from interrupt handlers:
// wakup the thread (but be interrupt handler safe here: B_DO_NOT_RESCHEDULE flag! former = disable_interrupts();
return B_ERROR; acquire_spinlock(&queue->lock);
queue->slots[queue->tail].function = function;
queue->slots[queue->tail].arg = arg;
queue->tail = (queue->tail++) % queue->size;
release_spinlock(&queue->lock);
restore_interrupts(former);
// wake up the corresponding dpc thead
// Notice that the interrupt handler should returns B_INVOKE_SCHEDULER for
// shortest DPC latency...
return release_sem_etc(queue->wakeup_sem, 1, B_DO_NOT_RESCHEDULE);
} }