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

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

sysman: Use taskman API to detect successful server start

Conflicts:

uspace/srv/logger/main.c

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