source: mainline/uspace/srv/sysman/sysman.c@ c2d50c8

Last change on this file since c2d50c8 was 73f7c4e, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

sysman: Create tests for job control

  • units are mocked by patching virtual methods table
  • sysman_process_queue to enforce emptying event queue

Conflicts:

.bzrignore
boot/Makefile.common

  • Property mode set to 100644
File size: 9.1 KB
Line 
1/*
2 * Copyright (c) 2015 Michal Koutny
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <adt/hash_table.h>
30#include <adt/list.h>
31#include <errno.h>
32#include <fibril_synch.h>
33#include <stdlib.h>
34
35#include "log.h"
36#include "sysman.h"
37#include "unit.h"
38
39
40/* Do not expose this generally named type */
41typedef struct {
42 link_t event_queue;
43
44 event_handler_t handler;
45 void *data;
46} event_t;
47
48typedef struct {
49 link_t callbacks;
50
51 callback_handler_t handler;
52 void *data;
53} obj_callback_t;
54
55typedef struct {
56 ht_link_t ht_link;
57
58 void *object;
59 list_t callbacks;
60} observed_object_t;
61
62static LIST_INITIALIZE(event_queue);
63static fibril_mutex_t event_queue_mtx;
64static fibril_condvar_t event_queue_nonempty_cv;
65static fibril_condvar_t event_queue_empty_cv;
66
67static hash_table_t observed_objects;
68static fibril_mutex_t observed_objects_mtx;
69static fibril_condvar_t observed_objects_cv;
70
71/* Hash table functions */
72static size_t observed_objects_ht_hash(const ht_link_t *item)
73{
74 observed_object_t *callbacks =
75 hash_table_get_inst(item, observed_object_t, ht_link);
76
77 return (size_t) callbacks->object;
78}
79
80static size_t observed_objects_ht_key_hash(void *key)
81{
82 void *object = *(void **) key;
83 return (size_t) object;
84}
85
86static bool observed_objects_ht_key_equal(void *key, const ht_link_t *item)
87{
88 void *object = *(void **)key;
89 return (
90 hash_table_get_inst(item, observed_object_t, ht_link)->object ==
91 object);
92}
93
94static hash_table_ops_t observed_objects_ht_ops = {
95 .hash = &observed_objects_ht_hash,
96 .key_hash = &observed_objects_ht_key_hash,
97 .equal = NULL,
98 .key_equal = &observed_objects_ht_key_equal,
99 .remove_callback = NULL
100};
101
102static void notify_observers(void *object)
103{
104 ht_link_t *item = hash_table_find(&observed_objects, &object);
105 if (item == NULL) {
106 return;
107 }
108 observed_object_t *observed_object =
109 hash_table_get_inst(item, observed_object_t, ht_link);
110
111 list_foreach_safe(observed_object->callbacks, cur_link, next_link) {
112 obj_callback_t *callback =
113 list_get_instance(cur_link, obj_callback_t, callbacks);
114 callback->handler(object, callback->data);
115 list_remove(cur_link);
116 free(callback);
117 }
118}
119
120/*
121 * Non-static functions
122 */
123void sysman_events_init(void)
124{
125 fibril_mutex_initialize(&event_queue_mtx);
126 fibril_condvar_initialize(&event_queue_nonempty_cv);
127 fibril_condvar_initialize(&event_queue_empty_cv);
128
129 bool table =
130 hash_table_create(&observed_objects, 0, 0, &observed_objects_ht_ops);
131 if (!table) {
132 sysman_log(LVL_FATAL, "%s: Failed initialization", __func__);
133 abort();
134 }
135 fibril_mutex_initialize(&observed_objects_mtx);
136 fibril_condvar_initialize(&observed_objects_cv);
137}
138
139int sysman_events_loop(void *unused)
140{
141 while (1) {
142 /* Pop event */
143 fibril_mutex_lock(&event_queue_mtx);
144 while (list_empty(&event_queue)) {
145 fibril_condvar_signal(&event_queue_empty_cv);
146 fibril_condvar_wait(&event_queue_nonempty_cv,
147 &event_queue_mtx);
148 }
149
150 link_t *li_event = list_first(&event_queue);
151 list_remove(li_event);
152 event_t *event =
153 list_get_instance(li_event, event_t, event_queue);
154 fibril_mutex_unlock(&event_queue_mtx);
155
156 /* Process event */
157 //sysman_log(LVL_DEBUG2, "process_event(%p, %p)",
158 // event->handler, event->data);
159 event->handler(event->data);
160 free(event);
161 }
162}
163
164/** Create and queue job for unit
165 *
166 * If unit already has the same job assigned callback is set to it.
167 *
168 * @param[in] callback (optional) callback must explicitly delete reference
169 * to job, void callback(void *job, void *callback_arg)
170 *
171 * return EBUSY unit already has a job assigned of different type
172 */
173int sysman_run_job(unit_t *unit, unit_state_t target_state,
174 callback_handler_t callback, void *callback_arg)
175{
176 job_t *job;
177
178 if (unit->job != NULL) {
179 assert(unit->job->state != JOB_UNQUEUED);
180
181 if (unit->job->target_state != target_state) {
182 return EBUSY;
183 }
184 job = unit->job;
185 } else {
186 job = job_create(unit, target_state);
187 if (job == NULL) {
188 return ENOMEM;
189 }
190 /* Reference in unit is enough */
191 job_del_ref(&job);
192 }
193
194 if (callback != NULL) {
195 job_add_ref(job);
196 sysman_object_observer(job, callback, callback_arg);
197 }
198
199 if (job->state == JOB_UNQUEUED) {
200 job_add_ref(job);
201 sysman_raise_event(&sysman_event_job_process, job);
202 }
203
204 return EOK;
205}
206
207void sysman_raise_event(event_handler_t handler, void *data)
208{
209 //sysman_log(LVL_DEBUG2, "%s(%p, %p)", __func__, handler, data);
210 event_t *event = malloc(sizeof(event_t));
211 if (event == NULL) {
212 sysman_log(LVL_FATAL, "%s: cannot allocate event", __func__);
213 // TODO think about aborting system critical task
214 abort();
215 }
216 link_initialize(&event->event_queue);
217 event->handler = handler;
218 event->data = data;
219
220 fibril_mutex_lock(&event_queue_mtx);
221 list_append(&event->event_queue, &event_queue);
222 /* There's only single event loop, broadcast is unnecessary */
223 fibril_condvar_signal(&event_queue_nonempty_cv);
224 fibril_mutex_unlock(&event_queue_mtx);
225}
226
227/** Empty current content of event queue
228 *
229 * This is potentially blocking call and as long as fibrils are cooperatively
230 * scheduled, queue will be empty upon return from this function.
231 */
232void sysman_process_queue(void)
233{
234 fibril_mutex_lock(&event_queue_mtx);
235 while (!list_empty(&event_queue)) {
236 fibril_condvar_wait(&event_queue_empty_cv, &event_queue_mtx);
237 }
238 fibril_mutex_unlock(&event_queue_mtx);
239}
240
241/** Register single-use object observer callback
242 *
243 * TODO no one handles return value, it's quite fatal to lack memory for
244 * callbacks... @return EOK on success
245 * @return ENOMEM
246 */
247int sysman_object_observer(void *object, callback_handler_t handler, void *data)
248{
249 int rc;
250 observed_object_t *observed_object = NULL;
251 observed_object_t *new_observed_object = NULL;
252 ht_link_t *ht_link = hash_table_find(&observed_objects, &object);
253
254 if (ht_link == NULL) {
255 observed_object = malloc(sizeof(observed_object_t));
256 if (observed_object == NULL) {
257 rc = ENOMEM;
258 goto fail;
259 }
260 new_observed_object = observed_object;
261
262 observed_object->object = object;
263 list_initialize(&observed_object->callbacks);
264 hash_table_insert(&observed_objects, &observed_object->ht_link);
265 } else {
266 observed_object =
267 hash_table_get_inst(ht_link, observed_object_t, ht_link);
268 }
269
270 obj_callback_t *obj_callback = malloc(sizeof(obj_callback_t));
271 if (obj_callback == NULL) {
272 rc = ENOMEM;
273 goto fail;
274 }
275
276 obj_callback->handler = handler;
277 obj_callback->data = data;
278 list_append(&obj_callback->callbacks, &observed_object->callbacks);
279 return EOK;
280
281fail:
282 free(new_observed_object);
283 return rc;
284}
285
286/*
287 * Event handlers
288 */
289
290// NOTE must run in main event loop fibril
291void sysman_event_job_process(void *data)
292{
293 job_t *job = data;
294 dyn_array_t job_closure;
295 dyn_array_initialize(&job_closure, job_ptr_t);
296
297 int rc = job_create_closure(job, &job_closure);
298 if (rc != EOK) {
299 sysman_log(LVL_ERROR, "Cannot create closure for job %p (%i)",
300 job, rc);
301 goto fail;
302 }
303
304 /*
305 * If jobs are queued, reference is passed from closure to the queue,
306 * otherwise, we still have the reference.
307 */
308 rc = job_queue_add_jobs(&job_closure);
309 if (rc != EOK) {
310 goto fail;
311 }
312 /* We don't need job anymore */
313 job_del_ref(&job);
314
315 job_queue_process();
316 return;
317
318fail:
319 job->retval = JOB_FAILED;
320 job_finish(job);
321 job_del_ref(&job);
322
323 dyn_array_foreach(job_closure, job_ptr_t, closure_job) {
324 job_del_ref(&(*closure_job));
325 }
326 dyn_array_destroy(&job_closure);
327}
328
329void sysman_event_job_finished(void *data)
330{
331 notify_observers(data);
332 /* Unreference the event data */
333 job_t *job = data;
334 job_del_ref(&job);
335
336 /* The finished job, might have been blocking */
337 job_queue_process();
338}
339
340void sysman_event_unit_exposee_created(void *data)
341{
342 unit_t *unit = data;
343 unit_exposee_created(unit);
344}
345
346void sysman_event_unit_failed(void *data)
347{
348 unit_t *unit = data;
349 unit_fail(unit);
350}
351
352void sysman_event_unit_state_changed(void *data)
353{
354 notify_observers(data);
355}
Note: See TracBrowser for help on using the repository browser.