source: mainline/uspace/srv/sysman/sysman.c@ 4b1c6a4b

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

sysman: Correct reference counting of jobs

  • Property mode set to 100644
File size: 7.4 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
38
39/* Do not expose this generally named type */
40typedef struct {
41 link_t event_queue;
42
43 event_handler_t handler;
44 void *data;
45} event_t;
46
47typedef struct {
48 link_t callbacks;
49
50 callback_handler_t handler;
51 void *data;
52} obj_callback_t;
53
54typedef struct {
55 ht_link_t ht_link;
56
57 void *object;
58 list_t callbacks;
59} observed_object_t;
60
61static LIST_INITIALIZE(event_queue);
62static fibril_mutex_t event_queue_mtx;
63static fibril_condvar_t event_queue_cv;
64
65static hash_table_t observed_objects;
66static fibril_mutex_t observed_objects_mtx;
67static fibril_condvar_t observed_objects_cv;
68
69/* Hash table functions */
70static size_t observed_objects_ht_hash(const ht_link_t *item)
71{
72 observed_object_t *callbacks =
73 hash_table_get_inst(item, observed_object_t, ht_link);
74
75 return (size_t) callbacks->object;
76}
77
78static size_t observed_objects_ht_key_hash(void *key)
79{
80 void *object = *(void **) key;
81 return (size_t) object;
82}
83
84static bool observed_objects_ht_key_equal(void *key, const ht_link_t *item)
85{
86 void *object = *(void **)key;
87 return (
88 hash_table_get_inst(item, observed_object_t, ht_link)->object ==
89 object);
90}
91
92static hash_table_ops_t observed_objects_ht_ops = {
93 .hash = &observed_objects_ht_hash,
94 .key_hash = &observed_objects_ht_key_hash,
95 .equal = NULL,
96 .key_equal = &observed_objects_ht_key_equal,
97 .remove_callback = NULL
98};
99
100static void notify_observers(void *object)
101{
102 ht_link_t *item = hash_table_find(&observed_objects, &object);
103 if (item == NULL) {
104 return;
105 }
106 observed_object_t *observed_object =
107 hash_table_get_inst(item, observed_object_t, ht_link);
108
109 list_foreach_safe(observed_object->callbacks, cur_link, next_link) {
110 obj_callback_t *callback =
111 list_get_instance(cur_link, obj_callback_t, callbacks);
112 callback->handler(object, callback->data);
113 list_remove(cur_link);
114 free(callback);
115 }
116}
117
118/*
119 * Non-static functions
120 */
121void sysman_events_init(void)
122{
123 fibril_mutex_initialize(&event_queue_mtx);
124 fibril_condvar_initialize(&event_queue_cv);
125
126 bool table =
127 hash_table_create(&observed_objects, 0, 0, &observed_objects_ht_ops);
128 if (!table) {
129 sysman_log(LVL_FATAL, "%s: Failed initialization", __func__);
130 abort();
131 }
132 fibril_mutex_initialize(&observed_objects_mtx);
133 fibril_condvar_initialize(&observed_objects_cv);
134}
135
136int sysman_events_loop(void *unused)
137{
138 while (1) {
139 /* Pop event */
140 fibril_mutex_lock(&event_queue_mtx);
141 while (list_empty(&event_queue)) {
142 fibril_condvar_wait(&event_queue_cv, &event_queue_mtx);
143 }
144
145 link_t *li_event = list_first(&event_queue);
146 list_remove(li_event);
147 event_t *event =
148 list_get_instance(li_event, event_t, event_queue);
149 fibril_mutex_unlock(&event_queue_mtx);
150
151 /* Process event */
152 sysman_log(LVL_DEBUG2, "process(%p, %p)", event->handler, event->data);
153 event->handler(event->data);
154 free(event);
155 }
156}
157
158void sysman_raise_event(event_handler_t handler, void *data)
159{
160 sysman_log(LVL_DEBUG2, "%s(%p, %p)", __func__, handler, data);
161 event_t *event = malloc(sizeof(event_t));
162 if (event == NULL) {
163 sysman_log(LVL_FATAL, "%s: cannot allocate event", __func__);
164 // TODO think about aborting system critical task
165 abort();
166 }
167 link_initialize(&event->event_queue);
168 event->handler = handler;
169 event->data = data;
170
171 fibril_mutex_lock(&event_queue_mtx);
172 list_append(&event->event_queue, &event_queue);
173 /* There's only single event loop, broadcast is unnecessary */
174 fibril_condvar_signal(&event_queue_cv);
175 fibril_mutex_unlock(&event_queue_mtx);
176}
177
178/** Register single-use object observer callback
179 *
180 * TODO no one handles return value, it's quite fatal to lack memory for
181 * callbacks... @return EOK on success
182 * @return ENOMEM
183 */
184int sysman_object_observer(void *object, callback_handler_t handler, void *data)
185{
186 int rc;
187 observed_object_t *observed_object = NULL;
188 observed_object_t *new_observed_object = NULL;
189 ht_link_t *ht_link = hash_table_find(&observed_objects, &object);
190
191 if (ht_link == NULL) {
192 observed_object = malloc(sizeof(observed_object_t));
193 if (observed_object == NULL) {
194 rc = ENOMEM;
195 goto fail;
196 }
197 new_observed_object = observed_object;
198
199 observed_object->object = object;
200 list_initialize(&observed_object->callbacks);
201 hash_table_insert(&observed_objects, &observed_object->ht_link);
202 } else {
203 observed_object =
204 hash_table_get_inst(ht_link, observed_object_t, ht_link);
205 }
206
207 obj_callback_t *obj_callback = malloc(sizeof(obj_callback_t));
208 if (obj_callback == NULL) {
209 rc = ENOMEM;
210 goto fail;
211 }
212
213 obj_callback->handler = handler;
214 obj_callback->data = data;
215 list_append(&obj_callback->callbacks, &observed_object->callbacks);
216 return EOK;
217
218fail:
219 free(new_observed_object);
220 return rc;
221}
222
223/*
224 * Event handlers
225 */
226
227// NOTE must run in main event loop fibril
228void sysman_event_job_process(void *arg)
229{
230 job_t *job = arg;
231 dyn_array_t job_closure;
232 dyn_array_initialize(&job_closure, job_ptr_t, 0);
233
234 int rc = job_create_closure(job, &job_closure);
235 if (rc != EOK) {
236 sysman_log(LVL_ERROR, "Cannot create closure for job %p (%i)",
237 job, rc);
238 goto fail;
239 }
240
241 /*
242 * If jobs are queued, reference is passed from closure to the queue,
243 * otherwise, we still have the reference.
244 */
245 rc = job_queue_add_jobs(&job_closure);
246 if (rc != EOK) {
247 goto fail;
248 }
249 /* We don't need job anymore */
250 job_del_ref(&job);
251
252 // TODO explain why calling asynchronously
253 sysman_raise_event(&sysman_event_job_queue_run, NULL);
254 return;
255
256fail:
257 job->retval = JOB_FAILED;
258 job_finish(job);
259 job_del_ref(&job);
260
261 dyn_array_foreach(job_closure, job_ptr_t, closure_job) {
262 job_del_ref(&(*closure_job));
263 }
264 dyn_array_destroy(&job_closure);
265}
266
267
268void sysman_event_job_queue_run(void *unused)
269{
270 job_t *job;
271 while ((job = job_queue_pop_runnable())) {
272 job_run(job);
273 job_del_ref(&job);
274 }
275}
276
277void sysman_event_job_changed(void *object)
278{
279 notify_observers(object);
280 /* Unreference the event data */
281 job_t *job = object;
282 job_del_ref(&job);
283}
Note: See TracBrowser for help on using the repository browser.