source: mainline/uspace/srv/sysman/sysman.c@ 012dd8e

Last change on this file since 012dd8e was dba056b, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

sysman: Using exposees to detect asynchronous start

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