1/*
2 * Copyright © 2016-2019 Sören Tempel
3 *
4 * This program is free software: you can redistribute it and/or
5 * modify it under the terms of the GNU Affero General Public
6 * License as published by the Free Software Foundation, either
7 * version 3 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19#include <assert.h>
20#include <errno.h>
21#include <pthread.h>
22#include <semaphore.h>
23#include <stdlib.h>
24
25#include "queue.h"
26#include "token.h"
27#include "util.h"
28
29/**
30 * Allocates memory for a new queue and initializes it.
31 *
32 * @returns A pointer to the newly created queue.
33 */
34queue *
35newqueue(void)
36{
37 queue *qu;
38
39 qu = emalloc(sizeof(queue));
40 qu->head = qu->tail = 0;
41
42 if ((errno = pthread_mutex_init(&qu->hmtx, NULL)) ||
43 (errno = pthread_mutex_init(&qu->tmtx, NULL)))
44 die("pthread_mutex_init failed");
45
46 if (sem_init(&qu->fullsem, 0, 0) ||
47 sem_init(&qu->emptysem, 0, NUMTOKENS))
48 die("sem_init failed");
49
50 return qu;
51}
52
53/**
54 * Enqueues a new token at the end of this queue.
55 *
56 * @pre Token must not be NULL.
57 * @param qu Queue where token should be enqueued.
58 * @param value Token which should be enqueued.
59 */
60void
61enqueue(queue *qu, token *value)
62{
63 sem_ewait(&qu->emptysem);
64 pthread_mutex_elock(&qu->tmtx);
65 qu->tokens[qu->tail++ % NUMTOKENS] = value;
66 pthread_mutex_eunlock(&qu->tmtx);
67 sem_epost(&qu->fullsem);
68}
69
70/**
71 * Dequeues the least recently added token from this queue.
72 * Causes a deadlock if you already dequeued the last item from this queue.
73 *
74 * @returns Pointer to the least recently added token.
75 */
76token *
77dequeue(queue *qu)
78{
79 token *ret;
80
81 sem_ewait(&qu->fullsem);
82 pthread_mutex_elock(&qu->hmtx);
83 ret = qu->tokens[qu->head++ % NUMTOKENS];
84 pthread_mutex_eunlock(&qu->hmtx);
85 sem_epost(&qu->emptysem);
86
87 return ret;
88}
89
90/**
91 * Frees memory allocated for the given queue. Node values are not freed
92 * you have to free those manually using the address returned on dequeue.
93 *
94 * @pre The last element of the queue was already dequeued (queue is empty)
95 * besides the given queue should not be NULL.
96 * @param qu Queue for which allocated memory should be freed.
97 */
98void
99freequeue(queue *qu)
100{
101 assert(qu);
102
103 if (sem_destroy(&qu->fullsem) || sem_destroy(&qu->emptysem))
104 die("sem_destroy failed");
105
106 if ((errno = pthread_mutex_destroy(&qu->hmtx)) ||
107 (errno = pthread_mutex_destroy(&qu->tmtx)))
108 die("pthread_mutex_destroy failed");
109
110 free(qu);
111}