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

mendersoftware / mender-mcu / 1650263247

31 Jan 2025 01:11PM UTC coverage: 25.951%. Remained the same
1650263247

push

gitlab-ci

vpodzime
chore: Rename test:static:format to test:static:code

This CI job checks multiple aspects of the code not only its
formatting.

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

737 of 2840 relevant lines covered (25.95%)

8.85 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-alloc.h"
28
#include "mender-log.h"
29
#include "mender-scheduler.h"
30
#include "mender-utils.h"
31

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

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

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

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

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

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

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

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

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

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

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

126
    return MENDER_OK;
×
127
}
128

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

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

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

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

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

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

170
    return MENDER_OK;
×
171

172
FAIL:
×
173

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

182
    return MENDER_FAIL;
×
183
}
184

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

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

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

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

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

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

216
    return MENDER_OK;
×
217
}
218

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

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

235
    return MENDER_OK;
×
236
}
237

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

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

247
    return MENDER_OK;
×
248
}
249

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

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

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

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

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

274
    return MENDER_OK;
×
275
}
276

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

283
    timer_delete(work->timer_handle);
×
284
    pthread_mutex_destroy(&work->sem_handle);
×
285
    mender_free(work->params.name);
×
286
    mender_free(work);
×
287

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

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

302
    return MENDER_OK;
×
303
}
304

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

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

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

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

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

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

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

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

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

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

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

360
mender_err_t
361
mender_scheduler_mutex_create(void **handle) {
×
362

363
    assert(NULL != handle);
×
364

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

374
    return MENDER_OK;
×
375
}
376

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

380
    assert(NULL != handle);
×
381

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

396
    return MENDER_OK;
×
397
}
398

399
mender_err_t
400
mender_scheduler_mutex_give(void *handle) {
×
401

402
    assert(NULL != handle);
×
403

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

409
    return MENDER_OK;
×
410
}
411

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

415
    assert(NULL != handle);
×
416

417
    /* Release memory */
418
    pthread_mutex_destroy((pthread_mutex_t *)handle);
×
419
    mender_free(handle);
×
420

421
    return MENDER_OK;
×
422
}
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