btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / src / add-ons / kernel / generic / dpc / dpc.c
blob4557972c186ed7d21181615cd89b32e131e40114
1 /*
2 * Copyright 2007-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Philippe Houdoin, philippe.houdoin@free.fr
7 */
10 //! Deferred Procedure Call support kernel module
13 #include <KernelExport.h>
15 #include <stdio.h>
16 #include <stdlib.h>
18 #include <dpc.h>
21 // Private DPC queue structures
22 typedef struct {
23 dpc_func function;
24 void *arg;
25 } dpc_slot;
28 typedef struct {
29 thread_id thread;
30 sem_id wakeup_sem;
31 spinlock lock;
32 int size;
33 int count;
34 int head;
35 int tail;
36 dpc_slot slots[0];
37 // size * slots follow
38 } dpc_queue;
40 #define DPC_QUEUE_SIZE 64
42 static int32
43 dpc_thread(void *arg)
45 dpc_queue *queue = arg;
46 dpc_slot dpc;
48 // Let's wait forever/until semaphore death for new DPC slot to show up
49 while (acquire_sem(queue->wakeup_sem) == B_OK) {
50 cpu_status former;
52 // grab the next dpc slot
53 former = disable_interrupts();
54 acquire_spinlock(&queue->lock);
56 dpc = queue->slots[queue->head];
57 queue->head = (queue->head + 1) % queue->size;
58 queue->count--;
60 release_spinlock(&queue->lock);
61 restore_interrupts(former);
63 dpc.function(dpc.arg);
66 // Let's finish the pending DPCs, if any.
67 // Otherwise, resource could leak...
68 while (queue->count--) {
69 dpc = queue->slots[queue->head];
70 queue->head = (queue->head + 1) % queue->size;
71 dpc.function(dpc.arg);
74 // Now, let's die quietly, ignored by all... sigh.
75 return 0;
79 // #pragma mark - public API
82 static status_t
83 new_dpc_queue(void **handle, const char *name, int32 priority)
85 char str[64];
86 dpc_queue *queue;
88 if (!handle)
89 return B_BAD_VALUE;
91 queue = malloc(sizeof(dpc_queue) + DPC_QUEUE_SIZE * sizeof(dpc_slot));
92 if (!queue)
93 return B_NO_MEMORY;
95 queue->head = queue->tail = 0;
96 queue->size = DPC_QUEUE_SIZE;
97 queue->count = 0;
98 B_INITIALIZE_SPINLOCK(&queue->lock); // Init the spinlock
100 snprintf(str, sizeof(str), "%.*s_wakeup_sem",
101 (int) sizeof(str) - 11, name);
103 queue->wakeup_sem = create_sem(0, str);
104 if (queue->wakeup_sem < B_OK) {
105 status_t status = queue->wakeup_sem;
106 free(queue);
107 return status;
110 // Fire a kernel thread to actually handle (aka call them!)
111 // the queued/deferred procedure calls
112 queue->thread = spawn_kernel_thread(dpc_thread, name, priority, queue);
113 if (queue->thread < 0) {
114 status_t status = queue->thread;
115 delete_sem(queue->wakeup_sem);
116 free(queue);
117 return status;
119 resume_thread(queue->thread);
121 *handle = queue;
123 return B_OK;
127 static status_t
128 delete_dpc_queue(void *handle)
130 dpc_queue *queue = handle;
131 thread_id thread;
132 status_t exit_value;
133 cpu_status former;
135 if (!queue)
136 return B_BAD_VALUE;
138 // Close the queue: queue_dpc() should know we're closing:
139 former = disable_interrupts();
140 acquire_spinlock(&queue->lock);
142 thread = queue->thread;
143 queue->thread = -1;
145 release_spinlock(&queue->lock);
146 restore_interrupts(former);
148 // Wakeup the thread by murdering its favorite semaphore
149 delete_sem(queue->wakeup_sem);
150 wait_for_thread(thread, &exit_value);
152 free(queue);
154 return B_OK;
158 static status_t
159 queue_dpc(void *handle, dpc_func function, void *arg)
161 dpc_queue *queue = handle;
162 cpu_status former;
163 status_t status = B_OK;
165 if (!queue || !function)
166 return B_BAD_VALUE;
168 // Try to be safe being called from interrupt handlers:
169 former = disable_interrupts();
170 acquire_spinlock(&queue->lock);
172 if (queue->thread < 0) {
173 // Queue thread is dying...
174 status = B_CANCELED;
175 } else if (queue->count == queue->size)
176 // This DPC queue is full, sorry
177 status = B_NO_MEMORY;
178 else {
179 queue->slots[queue->tail].function = function;
180 queue->slots[queue->tail].arg = arg;
181 queue->tail = (queue->tail + 1) % queue->size;
182 queue->count++;
185 release_spinlock(&queue->lock);
186 restore_interrupts(former);
188 if (status == B_OK)
189 // Wake up the corresponding dpc thread
190 // Notice that interrupt handlers should return B_INVOKE_SCHEDULER to
191 // shorten DPC latency as much as possible...
192 status = release_sem_etc(queue->wakeup_sem, 1, B_DO_NOT_RESCHEDULE);
194 return status;
198 static status_t
199 std_ops(int32 op, ...)
201 switch (op) {
202 case B_MODULE_INIT:
203 return B_OK;
204 case B_MODULE_UNINIT:
205 return B_OK;
207 default:
208 return B_ERROR;
213 static dpc_module_info sDPCModule = {
215 B_DPC_MODULE_NAME,
217 std_ops
220 new_dpc_queue,
221 delete_dpc_queue,
222 queue_dpc
226 module_info *modules[] = {
227 (module_info *) &sDPCModule,
228 NULL