source: mainline/uspace/srv/sysman/job.c@ 5a88d87

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

sysman: Implement stopping units

Currently fails service monitoring because of taskman flawed event flags.
However, job closure works well.

  • Property mode set to 100644
File size: 6.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/list.h>
30#include <assert.h>
31#include <errno.h>
32#include <stdlib.h>
33
34#include "repo.h"
35#include "edge.h"
36#include "job.h"
37#include "log.h"
38#include "sysman.h"
39
40
41/*
42 * Static functions
43 */
44
45
46/** Remove blocking_job from blocked job structure
47 *
48 * @note Caller must remove blocked_job from collection of blocked_jobs
49 */
50static void job_unblock(job_t *blocked_job, job_t *blocking_job)
51{
52 if (blocking_job->retval == JOB_FAILED) {
53 blocked_job->blocking_job_failed = true;
54 }
55 blocked_job->blocking_jobs -= 1;
56
57 job_del_ref(&blocked_job);
58}
59
60static void job_init(job_t *job, unit_t *u, unit_state_t target_state)
61{
62 assert(job);
63 assert(u);
64 memset(job, 0, sizeof(*job));
65
66 link_initialize(&job->job_queue);
67
68 atomic_set(&job->refcnt, 0);
69
70 job->target_state = target_state;
71 job->unit = u;
72
73 dyn_array_initialize(&job->blocked_jobs, job_t *);
74 job->blocking_jobs = 0;
75 job->blocking_job_failed = false;
76
77 job->state = JOB_EMBRYO;
78 job->retval = JOB_UNDEFINED_;
79}
80
81static bool job_eval_retval(job_t *job)
82{
83 unit_t *u = job->unit;
84
85 if (u->state == job->target_state) {
86 job->retval = JOB_OK;
87 return true;
88 } else if (u->state == STATE_FAILED) {
89 job->retval = JOB_FAILED;
90 return true;
91 } else {
92 return false;
93 }
94}
95
96static void job_check(void *object, void *data)
97{
98 unit_t *u = object;
99 job_t *job = data;
100
101 /*
102 * We have one reference from caller for our disposal,
103 * if needed, pass it to observer.
104 */
105 if (job_eval_retval(job)) {
106 job_finish(job);
107 job_del_ref(&job);
108 } else {
109 // TODO place for timeout
110 sysman_object_observer(u, &job_check, job);
111 }
112}
113
114static void job_destroy(job_t **job_ptr)
115{
116 job_t *job = *job_ptr;
117 if (job == NULL) {
118 return;
119 }
120
121 assert(!link_used(&job->job_queue));
122
123 dyn_array_foreach(job->blocked_jobs, job_t *, job_it) {
124 job_del_ref(&(*job_it));
125 }
126 dyn_array_destroy(&job->blocked_jobs);
127
128 free(job);
129 *job_ptr = NULL;
130}
131
132/*
133 * Non-static functions
134 */
135
136/** Create job assigned to the unit
137 *
138 * @param[in] unit
139 * @param[in] target_state
140 *
141 * @return NULL or newly created job (there is a single refernce for the creator)
142 */
143job_t *job_create(unit_t *u, unit_state_t target_state)
144{
145 job_t *job = malloc(sizeof(job_t));
146 if (job != NULL) {
147 job_init(job, u, target_state);
148
149 /* Add one reference for the creator */
150 job_add_ref(job);
151 }
152
153 return job;
154}
155
156/** Add one reference to job
157 *
158 * Usage:
159 * - adding observer which references the job,
160 * - raising and event that references the job,
161 * - anytime any other new reference is made.
162 */
163void job_add_ref(job_t *job)
164{
165 atomic_inc(&job->refcnt);
166}
167
168/** Remove one reference from job, last remover destroys the job
169 *
170 * Usage:
171 * - inside observer callback that references the job,
172 * - inside event handler that references the job,
173 * - anytime you dispose a reference to the job.
174 */
175void job_del_ref(job_t **job_ptr)
176{
177 job_t *job = *job_ptr;
178
179 assert(job != NULL);
180 assert(atomic_get(&job->refcnt) > 0);
181 if (atomic_predec(&job->refcnt) == 0) {
182 job_destroy(job_ptr);
183 }
184}
185
186void job_run(job_t *job)
187{
188 assert(job->state == JOB_PENDING);
189
190 job->state = JOB_RUNNING;
191 unit_t *u = job->unit;
192 sysman_log(LVL_DEBUG, "%s(%p), %s -> %i",
193 __func__, job, unit_name(u), job->target_state);
194
195 /* Propagate failure */
196 if (job->blocking_job_failed) {
197 goto fail;
198 }
199
200 int rc;
201 // TODO put here similar evaluation as in job_check
202 // goal is to have job_run "idempotent"
203 switch (job->target_state) {
204 case STATE_STARTED:
205 if (u->state == job->target_state) {
206 rc = EOK;
207 } else {
208 rc = unit_start(u);
209 }
210 break;
211 case STATE_STOPPED:
212 if (u->state == job->target_state) {
213 rc = EOK;
214 } else {
215 rc = unit_stop(u);
216 }
217 break;
218 default:
219 // TODO implement other states?
220 assert(false);
221 }
222 if (rc != EOK) {
223 //TODO here is 'rc' value "lost" (not propagated further)
224 sysman_log(LVL_DEBUG, "%s(%p), %s -> %i, error: %i",
225 __func__, job, unit_name(u), job->target_state, rc);
226 goto fail;
227 }
228
229 /*
230 * job_check deletes reference, we want job to remain to caller, thus
231 * add one dummy ref
232 */
233 job_add_ref(job);
234 job_check(job->unit, job);
235 return;
236
237fail:
238 job->retval = JOB_FAILED;
239 job_finish(job);
240}
241
242/** Unblocks blocked jobs and notify observers
243 *
244 * @param[in] job job with defined return value
245 */
246void job_finish(job_t *job)
247{
248 assert(job->state != JOB_FINISHED);
249 assert(job->retval != JOB_UNDEFINED_);
250 assert(!job->unit->job || job->unit->job == job);
251
252 sysman_log(LVL_DEBUG2, "%s(%p) %s ret %i, ref %i",
253 __func__, job, unit_name(job->unit), job->retval,
254 atomic_get(&job->refcnt));
255
256 job->state = JOB_FINISHED;
257
258 /* First remove references, then clear the array */
259 assert(job->blocked_jobs.size == job->blocked_jobs_count);
260 dyn_array_foreach(job->blocked_jobs, job_t *, job_it) {
261 job_unblock(*job_it, job);
262 }
263 dyn_array_clear(&job->blocked_jobs);
264
265 /* Add reference for event handler */
266 if (job->unit->job == NULL) {
267 job_add_ref(job);
268 } else {
269 /* Pass reference from unit */
270 job->unit->job = NULL;
271 }
272 sysman_raise_event(&sysman_event_job_finished, job);
273}
274
Note: See TracBrowser for help on using the repository browser.