source: mainline/uspace/srv/sysman/sysman.c@ 5353f50

Last change on this file since 5353f50 was 25a9fec, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

sysman: Refactor job.c into job_queue.c and job_closure.c

  • Property mode set to 100644
File size: 10.6 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 "job_closure.h"
36#include "job_queue.h"
37#include "log.h"
38#include "sysman.h"
39#include "task.h"
40#include "unit.h"
41
42
43/* Do not expose this generally named type */
44typedef struct {
45 link_t event_queue;
46
47 event_handler_t handler;
48 void *data;
49} event_t;
50
51typedef struct {
52 link_t callbacks;
53
54 callback_handler_t handler;
55 void *data;
56} obj_callback_t;
57
58typedef struct {
59 ht_link_t ht_link;
60
61 void *object;
62 list_t callbacks;
63} observed_object_t;
64
65static LIST_INITIALIZE(event_queue);
66static fibril_mutex_t event_queue_mtx;
67static fibril_condvar_t event_queue_nonempty_cv;
68static fibril_condvar_t event_queue_empty_cv;
69
70static hash_table_t observed_objects;
71static fibril_mutex_t observed_objects_mtx;
72static fibril_condvar_t observed_objects_cv;
73
74/*
75 * Hash table functions
76 */
77static size_t observed_objects_ht_hash(const ht_link_t *item)
78{
79 observed_object_t *callbacks =
80 hash_table_get_inst(item, observed_object_t, ht_link);
81
82 return (size_t) callbacks->object;
83}
84
85static size_t observed_objects_ht_key_hash(void *key)
86{
87 void *object = *(void **) key;
88 return (size_t) object;
89}
90
91static bool observed_objects_ht_key_equal(void *key, const ht_link_t *item)
92{
93 void *object = *(void **)key;
94 return (
95 hash_table_get_inst(item, observed_object_t, ht_link)->object ==
96 object);
97}
98
99static hash_table_ops_t observed_objects_ht_ops = {
100 .hash = &observed_objects_ht_hash,
101 .key_hash = &observed_objects_ht_key_hash,
102 .equal = NULL,
103 .key_equal = &observed_objects_ht_key_equal,
104 .remove_callback = NULL
105};
106
107/*
108 * Static functions
109 */
110
111static observed_object_t *observed_object_create(void *object)
112{
113 observed_object_t *result = malloc(sizeof(observed_object_t));
114 if (result) {
115 result->object = object;
116 list_initialize(&result->callbacks);
117 hash_table_insert(&observed_objects, &result->ht_link);
118 }
119 return result;
120}
121
122static void observed_object_destroy(observed_object_t **ptr_observed_object)
123{
124 observed_object_t *observed_object = *ptr_observed_object;
125 if (observed_object == NULL) {
126 return;
127 }
128
129 ht_link_t *item = &observed_object->ht_link;
130 hash_table_remove_item(&observed_objects, item);
131 free(observed_object);
132 *ptr_observed_object = NULL;
133}
134
135static void notify_observers(void *object)
136{
137 ht_link_t *item = hash_table_find(&observed_objects, &object);
138 if (item == NULL) {
139 return;
140 }
141 observed_object_t *observed_object =
142 hash_table_get_inst(item, observed_object_t, ht_link);
143
144 list_foreach_safe(observed_object->callbacks, cur_link, next_link) {
145 obj_callback_t *callback =
146 list_get_instance(cur_link, obj_callback_t, callbacks);
147 callback->handler(object, callback->data);
148 list_remove(cur_link);
149 free(callback);
150 }
151
152 observed_object_destroy(&observed_object);
153}
154
155/*
156 * Non-static functions
157 */
158void sysman_events_init(void)
159{
160 fibril_mutex_initialize(&event_queue_mtx);
161 fibril_condvar_initialize(&event_queue_nonempty_cv);
162 fibril_condvar_initialize(&event_queue_empty_cv);
163
164 bool table =
165 hash_table_create(&observed_objects, 0, 0, &observed_objects_ht_ops);
166 if (!table) {
167 sysman_log(LVL_FATAL, "%s: Failed initialization", __func__);
168 abort();
169 }
170 fibril_mutex_initialize(&observed_objects_mtx);
171 fibril_condvar_initialize(&observed_objects_cv);
172}
173
174int sysman_events_loop(void *unused)
175{
176 while (1) {
177 /* Pop event */
178 fibril_mutex_lock(&event_queue_mtx);
179 while (list_empty(&event_queue)) {
180 fibril_condvar_signal(&event_queue_empty_cv);
181 fibril_condvar_wait(&event_queue_nonempty_cv,
182 &event_queue_mtx);
183 }
184
185 link_t *li_event = list_first(&event_queue);
186 list_remove(li_event);
187 event_t *event =
188 list_get_instance(li_event, event_t, event_queue);
189 fibril_mutex_unlock(&event_queue_mtx);
190
191 /* Process event */
192 //sysman_log(LVL_DEBUG2, "process_event(%p, %p)",
193 // event->handler, event->data);
194 event->handler(event->data);
195 free(event);
196 }
197}
198
199/** Create and queue job for unit
200 *
201 * If unit already has the same job assigned callback is moved to it.
202 *
203 * @param[in] callback (optional) callback must explicitly delete reference
204 * to job, void callback(void *job, void *callback_arg)
205 *
206 * return EBUSY unit already has a job assigned of different type
207 */
208int sysman_run_job(unit_t *unit, unit_state_t target_state,
209 callback_handler_t callback, void *callback_arg)
210{
211 job_t *job = job_create(unit, target_state);
212 if (job == NULL) {
213 return ENOMEM;
214 }
215
216 if (callback != NULL) {
217 job_add_ref(job);
218 //TODO sysman_object_observer is not fibril-safe CANNOT BE CALLED HERE!
219 sysman_object_observer(job, callback, callback_arg);
220 }
221
222 /* Pass reference to event */
223 sysman_raise_event(&sysman_event_job_process, job);
224
225 return EOK;
226}
227
228void sysman_raise_event(event_handler_t handler, void *data)
229{
230 //sysman_log(LVL_DEBUG2, "%s(%p, %p)", __func__, handler, data);
231 event_t *event = malloc(sizeof(event_t));
232 if (event == NULL) {
233 sysman_log(LVL_FATAL, "%s: cannot allocate event", __func__);
234 // TODO think about aborting system critical task
235 abort();
236 }
237 link_initialize(&event->event_queue);
238 event->handler = handler;
239 event->data = data;
240
241 fibril_mutex_lock(&event_queue_mtx);
242 list_append(&event->event_queue, &event_queue);
243 /* There's only single event loop, broadcast is unnecessary */
244 fibril_condvar_signal(&event_queue_nonempty_cv);
245 fibril_mutex_unlock(&event_queue_mtx);
246}
247
248/** Empty current content of event queue
249 *
250 * This is potentially blocking call and as long as fibrils are cooperatively
251 * scheduled, queue will be empty upon return from this function.
252 */
253void sysman_process_queue(void)
254{
255 fibril_mutex_lock(&event_queue_mtx);
256 while (!list_empty(&event_queue)) {
257 fibril_condvar_wait(&event_queue_empty_cv, &event_queue_mtx);
258 }
259 fibril_mutex_unlock(&event_queue_mtx);
260}
261
262/** Register single-use object observer callback
263 *
264 * TODO no one handles return value, it's quite fatal to lack memory for
265 * callbacks...
266 *
267 * @return EOK on success
268 * @return ENOMEM
269 */
270int sysman_object_observer(void *object, callback_handler_t handler, void *data)
271{
272 int rc;
273 observed_object_t *observed_object = NULL;
274 observed_object_t *new_observed_object = NULL;
275 ht_link_t *ht_link = hash_table_find(&observed_objects, &object);
276
277 if (ht_link == NULL) {
278 new_observed_object = observed_object_create(object);
279 if (new_observed_object == NULL) {
280 rc = ENOMEM;
281 goto fail;
282 }
283 observed_object = new_observed_object;
284 } else {
285 observed_object =
286 hash_table_get_inst(ht_link, observed_object_t, ht_link);
287 }
288
289 obj_callback_t *obj_callback = malloc(sizeof(obj_callback_t));
290 if (obj_callback == NULL) {
291 rc = ENOMEM;
292 goto fail;
293 }
294
295 obj_callback->handler = handler;
296 obj_callback->data = data;
297 list_append(&obj_callback->callbacks, &observed_object->callbacks);
298 return EOK;
299
300fail:
301 free(new_observed_object);
302 return rc;
303}
304
305int sysman_move_observers(void *src_object, void *dst_object)
306{
307 ht_link_t *src_link = hash_table_find(&observed_objects, &src_object);
308 if (src_link == NULL) {
309 return EOK;
310 }
311
312 ht_link_t *dst_link = hash_table_find(&observed_objects, &dst_object);
313 observed_object_t *dst_observed_object;
314 if (dst_link == NULL) {
315 dst_observed_object = observed_object_create(dst_object);
316 if (dst_observed_object == NULL) {
317 return ENOMEM;
318 }
319 } else {
320 dst_observed_object =
321 hash_table_get_inst(dst_link, observed_object_t, ht_link);
322 }
323
324 observed_object_t *src_observed_object =
325 hash_table_get_inst(src_link, observed_object_t, ht_link);
326
327 list_concat(&dst_observed_object->callbacks,
328 &src_observed_object->callbacks);
329 observed_object_destroy(&src_observed_object);
330
331 return EOK;
332}
333
334size_t sysman_observers_count(void *object)
335{
336 ht_link_t *ht_link = hash_table_find(&observed_objects, &object);
337
338 if (ht_link == NULL) {
339 return 0;
340 }
341
342 observed_object_t *observed_object =
343 hash_table_get_inst(ht_link, observed_object_t, ht_link);
344
345 return list_count(&observed_object->callbacks);
346}
347
348
349/*
350 * Event handlers
351 */
352
353// NOTE must run in main event loop fibril
354void sysman_event_job_process(void *data)
355{
356 job_t *job = data;
357 dyn_array_t job_closure;
358 dyn_array_initialize(&job_closure, job_t *);
359
360 int rc = job_create_closure(job, &job_closure);
361 if (rc != EOK) {
362 sysman_log(LVL_ERROR, "Cannot create closure for job %p (%i)",
363 job, rc);
364 goto fail;
365 }
366
367 /*
368 * If jobs are queued, reference is passed from closure to the queue,
369 * otherwise, we still have the reference.
370 */
371 rc = job_queue_add_closure(&job_closure);
372 if (rc != EOK) {
373 goto fail;
374 }
375
376 /* We don't need job anymore */
377 job_del_ref(&job);
378
379 job_queue_process();
380 return;
381
382fail:
383 job->retval = JOB_FAILED;
384 job_finish(job);
385 job_del_ref(&job);
386
387 dyn_array_foreach(job_closure, job_t *, closure_job) {
388 job_del_ref(&(*closure_job));
389 }
390 dyn_array_destroy(&job_closure);
391}
392
393void sysman_event_job_finished(void *data)
394{
395 notify_observers(data);
396 /* Unreference the event data */
397 job_t *job = data;
398 job_del_ref(&job);
399
400 /* The finished job, might have been blocking */
401 job_queue_process();
402}
403
404void sysman_event_unit_exposee_created(void *data)
405{
406 unit_exposee_created(data);
407}
408
409void sysman_event_unit_failed(void *data)
410{
411 unit_fail(data);
412}
413
414void sysman_event_unit_state_changed(void *data)
415{
416 notify_observers(data);
417}
418
Note: See TracBrowser for help on using the repository browser.