• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-mcu / 1526166760

04 Nov 2024 01:32PM UTC coverage: 1.176% (+0.001%) from 1.175%
1526166760

push

gitlab-ci

vpodzime
fix: Only return MENDER_DONE from update_work_function() when rebooting

Returning `MENDER_DONE` from
`mender_client_update_work_function()` signals it's done and
should not be scheduled again. And we only want to do that when
waiting for a reboot (if we have to wait).

`mender_client_check_deployment()` returns `MENDER_DONE` in case
there is no deployment so we need to translate that to
`MENDER_OK` signalling we are ok to proceed and check again next
time.

Ticket: MEN-7536
Changelog: none
Signed-off-by: Vratislav Podzimek <vratislav.podzimek@northern.tech>

0 of 3 new or added lines in 1 file covered. (0.0%)

142 existing lines in 2 files now uncovered.

31 of 2637 relevant lines covered (1.18%)

0.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/platform/scheduler/posix/src/mender-scheduler.c
1
/**
2
 * @file      mender-scheduler.c
3
 * @brief     Mender scheduler interface for Posix platform
4
 *
5
 * Copyright joelguittet and mender-mcu-client contributors
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19

20
#include <errno.h>
21
#include <math.h>
22
#include <mqueue.h>
23
#include <pthread.h>
24
#include <signal.h>
25
#include <time.h>
26
#include <unistd.h>
27
#include "mender-log.h"
28
#include "mender-scheduler.h"
29
#include "mender-utils.h"
30

31
/**
32
 * @brief Default work queue stack size (kB)
33
 */
34
#ifndef CONFIG_MENDER_SCHEDULER_WORK_QUEUE_STACK_SIZE
35
#define CONFIG_MENDER_SCHEDULER_WORK_QUEUE_STACK_SIZE (64)
36
#endif /* CONFIG_MENDER_SCHEDULER_WORK_QUEUE_STACK_SIZE */
37

38
/**
39
 * @brief Default work queue priority
40
 */
41
#ifndef CONFIG_MENDER_SCHEDULER_WORK_QUEUE_PRIORITY
42
#define CONFIG_MENDER_SCHEDULER_WORK_QUEUE_PRIORITY (0)
43
#endif /* CONFIG_MENDER_SCHEDULER_WORK_QUEUE_PRIORITY */
44

45
/**
46
 * @brief Default work queue length
47
 */
48
#ifndef CONFIG_MENDER_SCHEDULER_WORK_QUEUE_LENGTH
49
#define CONFIG_MENDER_SCHEDULER_WORK_QUEUE_LENGTH (10)
50
#endif /* CONFIG_MENDER_SCHEDULER_WORK_QUEUE_LENGTH */
51

52
/**
53
 * @brief Work context
54
 */
55
typedef struct mender_platform_work_t {
56
    mender_scheduler_work_params_t params;       /**< Work parameters */
57
    pthread_mutex_t                sem_handle;   /**< Semaphore used to indicate work is pending or executing */
58
    timer_t                        timer_handle; /**< Timer used to periodically execute work */
59
    bool                           activated;    /**< Flag indicating the work is activated */
60
} mender_platform_work_t;
61

62
/**
63
 *
64
 * @brief Work queue parameters
65
 */
66
#define MENDER_SCHEDULER_WORK_QUEUE_NAME  "/mender-work-queue"
67
#define MENDER_SCHEDULER_WORK_QUEUE_PERMS (0644)
68

69
/**
70
 * @brief Function used to handle work context timer when it expires
71
 * @param timer_data Timer data
72
 */
73
static void mender_scheduler_timer_callback(union sigval timer_data);
74

75
/**
76
 * @brief Thread used to handle work queue
77
 * @param arg Not used
78
 * @return Not used
79
 */
80
static void *mender_scheduler_work_queue_thread(void *arg);
81

82
/**
83
 * @brief Work queue handle
84
 */
85
static mqd_t mender_scheduler_work_queue_handle;
86

87
/**
88
 * @brief Work queue thread handle
89
 */
90
static pthread_t mender_scheduler_work_queue_thread_handle;
91

92
mender_err_t
93
mender_scheduler_init(void) {
×
94
    int ret;
95

96
    /* Create and start work queue */
UNCOV
97
    struct mq_attr mq_attr = { 0 };
×
98
    mq_attr.mq_maxmsg      = CONFIG_MENDER_SCHEDULER_WORK_QUEUE_LENGTH;
×
99
    mq_attr.mq_msgsize     = sizeof(mender_platform_work_t *);
×
100
    mq_unlink(MENDER_SCHEDULER_WORK_QUEUE_NAME);
×
101
    if ((mender_scheduler_work_queue_handle = mq_open(MENDER_SCHEDULER_WORK_QUEUE_NAME, O_CREAT | O_RDWR, MENDER_SCHEDULER_WORK_QUEUE_PERMS, &mq_attr)) < 0) {
×
102
        mender_log_error("Unable to create work queue (errno=%d)", errno);
×
103
        return MENDER_FAIL;
×
104
    }
105
    pthread_attr_t pthread_attr;
UNCOV
106
    if (0 != (ret = pthread_attr_init(&pthread_attr))) {
×
107
        mender_log_error("Unable to initialize work queue thread attributes (ret=%d)", ret);
×
108
        return MENDER_FAIL;
×
109
    }
UNCOV
110
    if (0
×
111
        != (ret = pthread_attr_setstacksize(
×
112
                &pthread_attr, ((CONFIG_MENDER_SCHEDULER_WORK_QUEUE_STACK_SIZE > 16) ? CONFIG_MENDER_SCHEDULER_WORK_QUEUE_STACK_SIZE : 16) * 1024))) {
UNCOV
113
        mender_log_error("Unable to set work queue thread stack size (ret=%d)", ret);
×
114
        return MENDER_FAIL;
×
115
    }
UNCOV
116
    if (0 != (ret = pthread_create(&mender_scheduler_work_queue_thread_handle, &pthread_attr, mender_scheduler_work_queue_thread, NULL))) {
×
117
        mender_log_error("Unable to create work queue thread (ret=%d)", ret);
×
118
        return MENDER_FAIL;
×
119
    }
UNCOV
120
    if (0 != (ret = pthread_setschedprio(mender_scheduler_work_queue_thread_handle, CONFIG_MENDER_SCHEDULER_WORK_QUEUE_PRIORITY))) {
×
121
        mender_log_error("Unable to set work queue thread priority (ret=%d)", ret);
×
122
        return MENDER_FAIL;
×
123
    }
124

UNCOV
125
    return MENDER_OK;
×
126
}
127

128
mender_err_t
UNCOV
129
mender_scheduler_work_create(mender_scheduler_work_params_t *work_params, mender_work_t **work) {
×
130
    assert(NULL != work_params);
×
131
    assert(NULL != work_params->function);
×
132
    assert(NULL != work_params->name);
×
133
    assert(NULL != work);
×
134

135
    /* Create work context */
UNCOV
136
    mender_platform_work_t *work_context = calloc(1, sizeof(mender_platform_work_t));
×
137
    if (NULL == work_context) {
×
138
        mender_log_error("Unable to allocate memory");
×
139
        goto FAIL;
×
140
    }
141

142
    /* Copy work parameters */
UNCOV
143
    work_context->params.function = work_params->function;
×
UNCOV
144
    work_context->params.period   = work_params->period;
×
145
    if (NULL == (work_context->params.name = strdup(work_params->name))) {
×
146
        mender_log_error("Unable to allocate memory");
×
147
        goto FAIL;
×
148
    }
149

150
    /* Create semaphore used to protect work function */
UNCOV
151
    if (0 != pthread_mutex_init(&work_context->sem_handle, NULL)) {
×
UNCOV
152
        mender_log_error("Unable to create semaphore");
×
153
        goto FAIL;
×
154
    }
155

156
    /* Create timer to handle the work periodically */
UNCOV
157
    struct sigevent sev       = { 0 };
×
UNCOV
158
    sev.sigev_notify          = SIGEV_THREAD;
×
UNCOV
159
    sev.sigev_notify_function = mender_scheduler_timer_callback;
×
160
    sev.sigev_value.sival_ptr = work_context;
×
161
    if (0 != timer_create(CLOCK_REALTIME, &sev, &work_context->timer_handle)) {
×
162
        mender_log_error("Unable to create timer");
×
163
        goto FAIL;
×
164
    }
165

166
    /* Return handle to the new work */
UNCOV
167
    *work = work_context;
×
168

UNCOV
169
    return MENDER_OK;
×
170

UNCOV
171
FAIL:
×
172

173
    /* Release memory */
174
    if (NULL != work_context) {
×
UNCOV
175
        timer_delete(work_context->timer_handle);
×
UNCOV
176
        pthread_mutex_destroy(&work_context->sem_handle);
×
177
        free(work_context->params.name);
×
178
        free(work_context);
×
179
    }
180

181
    return MENDER_FAIL;
×
182
}
183

184
mender_err_t
UNCOV
185
mender_scheduler_work_activate(mender_work_t *work) {
×
186
    assert(NULL != work);
×
187

188
    /* Give semaphore used to protect the work function */
UNCOV
189
    if (0 != pthread_mutex_unlock(&work->sem_handle)) {
×
190
        mender_log_error("Unable to give semaphore");
×
191
        return MENDER_FAIL;
×
192
    }
193

194
    /* Check the timer period */
195
    if (work->params.period > 0) {
×
196

197
        /* Start the timer to handle the work */
UNCOV
198
        struct itimerspec its  = { 0 };
×
UNCOV
199
        its.it_value.tv_sec    = work->params.period;
×
200
        its.it_interval.tv_sec = work->params.period;
×
UNCOV
201
        if (0 != timer_settime(work->timer_handle, 0, &its, NULL)) {
×
UNCOV
202
            mender_log_error("Unable to start timer");
×
UNCOV
203
            return MENDER_FAIL;
×
204
        }
205

206
        /* Execute the work now */
207
        union sigval timer_data;
208
        timer_data.sival_ptr = (void *)work;
×
209
        mender_scheduler_timer_callback(timer_data);
×
210
    }
211

212
    /* Indicate the work has been activated */
UNCOV
213
    work->activated = true;
×
214

215
    return MENDER_OK;
×
216
}
217

218
mender_err_t
219
mender_scheduler_work_set_period(mender_work_t *work, uint32_t period) {
×
UNCOV
220
    assert(NULL != work);
×
221

222
    /* Set timer period */
UNCOV
223
    work->params.period   = period;
×
UNCOV
224
    struct itimerspec its = { 0 };
×
225
    if (work->params.period > 0) {
×
226
        its.it_value.tv_sec    = work->params.period;
×
UNCOV
227
        its.it_interval.tv_sec = work->params.period;
×
228
    }
229
    if (0 != timer_settime(work->timer_handle, 0, &its, NULL)) {
×
UNCOV
230
        mender_log_error("Unable to set timer period");
×
231
        return MENDER_FAIL;
×
232
    }
233

234
    return MENDER_OK;
×
235
}
236

237
mender_err_t
238
mender_scheduler_work_execute(mender_work_t *work) {
×
UNCOV
239
    assert(NULL != work);
×
240

241
    /* Execute the work now */
242
    union sigval timer_data;
UNCOV
243
    timer_data.sival_ptr = (void *)work;
×
UNCOV
244
    mender_scheduler_timer_callback(timer_data);
×
245

246
    return MENDER_OK;
×
247
}
248

249
mender_err_t
250
mender_scheduler_work_deactivate(mender_work_t *work) {
×
251
    assert(NULL != work);
×
252

253
    /* Check if the work was activated */
UNCOV
254
    if (work->activated) {
×
255

256
        /* Stop the timer used to periodically execute the work (if it is running) */
257
        struct itimerspec its = { 0 };
×
258
        if (0 != timer_settime(work->timer_handle, 0, &its, NULL)) {
×
UNCOV
259
            mender_log_error("Unable to stop timer");
×
UNCOV
260
            return MENDER_FAIL;
×
261
        }
262

263
        /* Wait if the work is pending or executing */
UNCOV
264
        if (0 != pthread_mutex_lock(&work->sem_handle)) {
×
265
            mender_log_error("Work '%s' is pending or executing", work->params.name);
×
266
            return MENDER_FAIL;
×
267
        }
268

269
        /* Indicate the work has been deactivated */
UNCOV
270
        work->activated = false;
×
271
    }
272

273
    return MENDER_OK;
×
274
}
275

276
mender_err_t
UNCOV
277
mender_scheduler_work_delete(mender_work_t *work) {
×
278
    if (NULL == work) {
×
UNCOV
279
        return MENDER_OK;
×
280
    }
281

UNCOV
282
    timer_delete(work->timer_handle);
×
UNCOV
283
    pthread_mutex_destroy(&work->sem_handle);
×
UNCOV
284
    free(work->params.name);
×
285
    free(work);
×
286

UNCOV
287
    return MENDER_OK;
×
288
}
289
mender_err_t
290
mender_scheduler_exit(void) {
×
291
    /* Submit empty work to the work queue, this ask the work queue thread to terminate */
292
    mender_platform_work_t *work = NULL;
×
UNCOV
293
    if (0 != mq_send(mender_scheduler_work_queue_handle, (const char *)&work, sizeof(mender_platform_work_t *), 0)) {
×
294
        mender_log_error("Unable to submit empty work to the work queue");
×
UNCOV
295
        return MENDER_FAIL;
×
296
    }
297

298
    /* Wait end of execution of the work queue thread */
299
    pthread_join(mender_scheduler_work_queue_thread_handle, NULL);
×
300

301
    return MENDER_OK;
×
302
}
303

304
static void
UNCOV
305
mender_scheduler_timer_callback(union sigval timer_data) {
×
306
    /* Get work context */
UNCOV
307
    mender_platform_work_t *work = (mender_platform_work_t *)timer_data.sival_ptr;
×
308
    assert(NULL != work);
×
309

310
    /* Exit if the work is already pending or executing */
UNCOV
311
    struct timespec timeout = { 0 };
×
UNCOV
312
    if (0 != pthread_mutex_timedlock(&work->sem_handle, &timeout)) {
×
313
        mender_log_debug("Work '%s' is not activated, already pending or executing", work->params.name);
314
        return;
×
315
    }
316

317
    /* Submit the work to the work queue */
UNCOV
318
    if (0 != mq_send(mender_scheduler_work_queue_handle, (const char *)&work, sizeof(mender_platform_work_t *), 0)) {
×
UNCOV
319
        mender_log_warning("Unable to submit work '%s' to the work queue", work->params.name);
×
UNCOV
320
        pthread_mutex_unlock(&work->sem_handle);
×
321
    }
322
}
323

324
__attribute__((noreturn)) static void *
UNCOV
325
mender_scheduler_work_queue_thread(MENDER_ARG_UNUSED void *arg) {
×
UNCOV
326
    mender_platform_work_t *work = NULL;
×
327

328
    /* Handle work to be executed */
329
    while (mq_receive(mender_scheduler_work_queue_handle, (char *)&work, sizeof(mender_platform_work_t *), NULL) > 0) {
×
330

331
        /* Check if empty work is received from the work queue, this ask the work queue thread to terminate */
UNCOV
332
        if (NULL == work) {
×
UNCOV
333
            goto END;
×
334
        }
335

336
        /* Call work function */
UNCOV
337
        if (MENDER_DONE == work->params.function()) {
×
338

339
            /* Work is done, stop timer used to execute the work periodically */
UNCOV
340
            struct itimerspec its = { 0 };
×
UNCOV
341
            if (0 != timer_settime(work->timer_handle, 0, &its, NULL)) {
×
342
                mender_log_error("Unable to stop timer");
×
343
            }
344
        }
345

346
        /* Release semaphore used to protect the work function */
347
        pthread_mutex_unlock(&work->sem_handle);
×
348
    }
349

UNCOV
350
END:
×
351
    /* Release memory */
352
    mq_close(mender_scheduler_work_queue_handle);
×
353
    mq_unlink(MENDER_SCHEDULER_WORK_QUEUE_NAME);
×
354

355
    /* Terminate work queue thread */
UNCOV
356
    pthread_exit(NULL);
×
357
}
358

359
mender_err_t
UNCOV
360
mender_scheduler_mutex_create(void **handle) {
×
361

UNCOV
362
    assert(NULL != handle);
×
363

364
    /* Create mutex */
UNCOV
365
    if (NULL == (*handle = malloc(sizeof(pthread_mutex_t)))) {
×
UNCOV
366
        return MENDER_FAIL;
×
367
    }
UNCOV
368
    if (0 != pthread_mutex_init(*handle, NULL)) {
×
UNCOV
369
        FREE_AND_NULL(*handle);
×
UNCOV
370
        return MENDER_FAIL;
×
371
    }
372

373
    return MENDER_OK;
×
374
}
375

376
mender_err_t
377
mender_scheduler_mutex_take(void *handle, int32_t delay_ms) {
×
378

379
    assert(NULL != handle);
×
380

381
    /* Take mutex */
UNCOV
382
    if (delay_ms >= 0) {
×
383
        struct timespec timeout;
384
        timeout.tv_sec  = delay_ms / 1000;
×
UNCOV
385
        timeout.tv_nsec = (delay_ms % 1000) * 1000000;
×
UNCOV
386
        if (0 != pthread_mutex_timedlock((pthread_mutex_t *)handle, &timeout)) {
×
UNCOV
387
            return MENDER_FAIL;
×
388
        }
389
    } else {
390
        if (0 != pthread_mutex_lock((pthread_mutex_t *)handle)) {
×
UNCOV
391
            return MENDER_FAIL;
×
392
        }
393
    }
394

395
    return MENDER_OK;
×
396
}
397

398
mender_err_t
UNCOV
399
mender_scheduler_mutex_give(void *handle) {
×
400

401
    assert(NULL != handle);
×
402

403
    /* Give mutex */
UNCOV
404
    if (0 != pthread_mutex_unlock((pthread_mutex_t *)handle)) {
×
UNCOV
405
        return MENDER_FAIL;
×
406
    }
407

UNCOV
408
    return MENDER_OK;
×
409
}
410

411
mender_err_t
412
mender_scheduler_mutex_delete(void *handle) {
×
413

UNCOV
414
    assert(NULL != handle);
×
415

416
    /* Release memory */
UNCOV
417
    pthread_mutex_destroy((pthread_mutex_t *)handle);
×
UNCOV
418
    free(handle);
×
419

UNCOV
420
    return MENDER_OK;
×
421
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc