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

mendersoftware / mender-mcu / 1642556678

27 Jan 2025 01:30PM UTC coverage: 25.749%. First build
1642556678

push

gitlab-ci

vpodzime
feat: Support platform-specific or custom allocators

And use Zephyr-specific allocation functions on Zephyr by
default, using either a separate heap (default) or the system
heap.

This means we have to use our memory management functions instead
of the standard ones everywhere, even if they can actually just
call the standard ones.

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

64 of 184 new or added lines in 12 files covered. (34.78%)

731 of 2839 relevant lines covered (25.75%)

8.54 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 */
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;
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
    }
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))) {
113
        mender_log_error("Unable to set work queue thread stack size (ret=%d)", ret);
×
114
        return MENDER_FAIL;
×
115
    }
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
    }
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

125
    return MENDER_OK;
×
126
}
127

128
mender_err_t
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 */
NEW
136
    mender_platform_work_t *work_context = mender_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 */
143
    work_context->params.function = work_params->function;
×
144
    work_context->params.period   = work_params->period;
×
NEW
145
    if (NULL == (work_context->params.name = mender_utils_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 */
151
    if (0 != pthread_mutex_init(&work_context->sem_handle, NULL)) {
×
152
        mender_log_error("Unable to create semaphore");
×
153
        goto FAIL;
×
154
    }
155

156
    /* Create timer to handle the work periodically */
157
    struct sigevent sev       = { 0 };
×
158
    sev.sigev_notify          = SIGEV_THREAD;
×
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 */
167
    *work = work_context;
×
168

169
    return MENDER_OK;
×
170

171
FAIL:
×
172

173
    /* Release memory */
174
    if (NULL != work_context) {
×
175
        timer_delete(work_context->timer_handle);
×
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
185
mender_scheduler_work_activate(mender_work_t *work) {
×
186
    assert(NULL != work);
×
187

188
    /* Give semaphore used to protect the work function */
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 */
198
        struct itimerspec its  = { 0 };
×
199
        its.it_value.tv_sec    = work->params.period;
×
200
        its.it_interval.tv_sec = work->params.period;
×
201
        if (0 != timer_settime(work->timer_handle, 0, &its, NULL)) {
×
202
            mender_log_error("Unable to start timer");
×
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 */
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) {
×
220
    assert(NULL != work);
×
221

222
    /* Set timer period */
223
    work->params.period   = period;
×
224
    struct itimerspec its = { 0 };
×
225
    if (work->params.period > 0) {
×
226
        its.it_value.tv_sec    = work->params.period;
×
227
        its.it_interval.tv_sec = work->params.period;
×
228
    }
229
    if (0 != timer_settime(work->timer_handle, 0, &its, NULL)) {
×
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) {
×
239
    assert(NULL != work);
×
240

241
    /* Execute the work now */
242
    union sigval timer_data;
243
    timer_data.sival_ptr = (void *)work;
×
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 */
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)) {
×
259
            mender_log_error("Unable to stop timer");
×
260
            return MENDER_FAIL;
×
261
        }
262

263
        /* Wait if the work is pending or executing */
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 */
270
        work->activated = false;
×
271
    }
272

273
    return MENDER_OK;
×
274
}
275

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

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

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;
×
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");
×
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
305
mender_scheduler_timer_callback(union sigval timer_data) {
×
306
    /* Get work context */
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 */
311
    struct timespec timeout = { 0 };
×
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 */
318
    if (0 != mq_send(mender_scheduler_work_queue_handle, (const char *)&work, sizeof(mender_platform_work_t *), 0)) {
×
319
        mender_log_warning("Unable to submit work '%s' to the work queue", work->params.name);
×
320
        pthread_mutex_unlock(&work->sem_handle);
×
321
    }
322
}
323

324
__attribute__((noreturn)) static void *
325
mender_scheduler_work_queue_thread(MENDER_ARG_UNUSED void *arg) {
×
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 */
332
        if (NULL == work) {
×
333
            goto END;
×
334
        }
335

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

339
            /* Work is done, stop timer used to execute the work periodically */
340
            struct itimerspec its = { 0 };
×
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

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 */
356
    pthread_exit(NULL);
×
357
}
358

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

362
    assert(NULL != handle);
×
363

364
    /* Create mutex */
365
    if (NULL == (*handle = malloc(sizeof(pthread_mutex_t)))) {
×
366
        return MENDER_FAIL;
×
367
    }
368
    if (0 != pthread_mutex_init(*handle, NULL)) {
×
369
        FREE_AND_NULL(*handle);
×
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 */
382
    if (delay_ms >= 0) {
×
383
        struct timespec timeout;
384
        timeout.tv_sec  = delay_ms / 1000;
×
385
        timeout.tv_nsec = (delay_ms % 1000) * 1000000;
×
386
        if (0 != pthread_mutex_timedlock((pthread_mutex_t *)handle, &timeout)) {
×
387
            return MENDER_FAIL;
×
388
        }
389
    } else {
390
        if (0 != pthread_mutex_lock((pthread_mutex_t *)handle)) {
×
391
            return MENDER_FAIL;
×
392
        }
393
    }
394

395
    return MENDER_OK;
×
396
}
397

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

401
    assert(NULL != handle);
×
402

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

408
    return MENDER_OK;
×
409
}
410

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

414
    assert(NULL != handle);
×
415

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

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