qLibc
qsem.c
Go to the documentation of this file.
1/******************************************************************************
2 * qLibc
3 *
4 * Copyright (c) 2010-2015 Seungyoung Kim.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *****************************************************************************/
28
29/**
30 * @file qsem.c Semaphore APIs.
31 *
32 * @note
33 * @code
34 * [daemon main]
35 * #define MAX_SEMAPHORES (2)
36 *
37 * // create semaphores
38 * int semid = qsem_init("/some/file/for/generating/unique/key", 'q', MAX_SEMAPHORES, true);
39 * if(semid < 0) {
40 * printf("ERROR: Can't initialize semaphores.\n");
41 * return -1;
42 * }
43 *
44 * // fork childs
45 * (... child forking codes ...)
46 *
47 * // at the end of daemon, free semaphores
48 * if(semid >= 0) qsem_free(semid);
49 *
50 * [forked child]
51 * // critical section for resource 0
52 * qsem_enter(semid, 0);
53 * (... guaranteed as atomic procedure ...)
54 * qsem_leave(semid, 0);
55 *
56 * (... some codes ...)
57 *
58 * // critical section for resource 1
59 * qsem_enter(semid, 1);
60 * (... guaranteed as atomic procedure ...)
61 * qsem_leave(semid, 1);
62 *
63 * [other program which uses resource 1]
64 * int semid = qsem_getid("/some/file/for/generating/unique/key", 'q');
65 * if(semid < 0) {
66 * printf("ERROR: Can't get semaphore id.\n");
67 * return -1;
68 * }
69 *
70 * // critical section for resource 1
71 * qsem_enter(semid, 1);
72 * (... guaranteed as atomic procedure ...)
73 * qsem_leave(semid, 1);
74 *
75 * @endcode
76 */
77
78#ifndef DISABLE_IPC
79
80#include <stdio.h>
81#include <stdlib.h>
82#include <stdbool.h>
83#include <unistd.h>
84#include <sys/sem.h>
85#include "qinternal.h"
86#include "ipc/qsem.h"
87
88/**
89 * Initialize semaphore
90 *
91 * @param keyfile seed for generating unique IPC key
92 * @param keyid seed for generating unique IPC key
93 * @param nsems number of semaphores to initialize
94 * @param recreate set to true to re-create semaphore if exists
95 *
96 * @return non-negative shared memory identifier if successful, otherwise returns -1
97 *
98 * @code
99 * int semid = qsem_init("/tmp/mydaemon.pid", 'q', 10, true);
100 * @endcode
101 */
102int qsem_init(const char *keyfile, int keyid, int nsems, bool recreate) {
103 key_t semkey;
104 int semid;
105
106 // generate unique key using ftok();
107 if (keyfile != NULL) {
108 semkey = ftok(keyfile, keyid);
109 if (semkey == -1)
110 return -1;
111 } else {
112 semkey = IPC_PRIVATE;
113 }
114
115 // create semaphores
116 if ((semid = semget(semkey, nsems, IPC_CREAT | IPC_EXCL | 0666)) == -1) {
117 if (recreate == false)
118 return -1;
119
120 // destroy & re-create
121 if ((semid = qsem_getid(keyfile, keyid)) >= 0)
122 qsem_free(semid);
123 if ((semid = semget(semkey, nsems, IPC_CREAT | IPC_EXCL | 0666)) == -1)
124 return -1;
125 }
126
127 // initializing
128 int i;
129 for (i = 0; i < nsems; i++) {
130 struct sembuf sbuf;
131
132 /* set sbuf */
133 sbuf.sem_num = i;
134 sbuf.sem_op = 1;
135 sbuf.sem_flg = 0;
136
137 /* initialize */
138 if (semop(semid, &sbuf, 1) != 0) {
139 qsem_free(semid);
140 return -1;
141 }
142 }
143
144 return semid;
145}
146
147/**
148 * Get semaphore identifier by keyfile and keyid for the existing semaphore
149 *
150 * @param keyfile seed for generating unique IPC key
151 * @param keyid seed for generating unique IPC key
152 *
153 * @return non-negative shared memory identifier if successful, otherwise returns -1
154 */
155int qsem_getid(const char *keyfile, int keyid) {
156 int semid;
157
158 /* generate unique key using ftok() */
159 key_t semkey = ftok(keyfile, keyid);
160 if (semkey == -1)
161 return -1;
162
163 /* get current semaphore id */
164 if ((semid = semget(semkey, 0, 0)) == -1)
165 return -1;
166
167 return semid;
168}
169
170/**
171 * Turn on the flag of semaphore then entering critical section
172 *
173 * @param semid semaphore identifier
174 * @param semno semaphore number
175 *
176 * @return true if successful, otherwise returns false
177 *
178 * @note If the semaphore is already turned on, this will wait until released
179 */
180bool qsem_enter(int semid, int semno) {
181 struct sembuf sbuf;
182
183 /* set sbuf */
184 sbuf.sem_num = semno;
185 sbuf.sem_op = -1;
186 sbuf.sem_flg = SEM_UNDO;
187
188 /* lock */
189 if (semop(semid, &sbuf, 1) != 0)
190 return false;
191 return true;
192}
193
194/**
195 * Try to turn on the flag of semaphore. If it is already turned on, do not wait.
196 *
197 * @param semid semaphore identifier
198 * @param semno semaphore number
199 *
200 * @return true if successful, otherwise(already turned on by other) returns false
201 */
202bool qsem_enter_nowait(int semid, int semno) {
203 struct sembuf sbuf;
204
205 /* set sbuf */
206 sbuf.sem_num = semno;
207 sbuf.sem_op = -1;
208 sbuf.sem_flg = SEM_UNDO | IPC_NOWAIT;
209
210 /* lock */
211 if (semop(semid, &sbuf, 1) != 0)
212 return false;
213 return true;
214}
215
216/**
217 * Force to turn on the flag of semaphore.
218 *
219 * @param semid semaphore identifier
220 * @param semno semaphore number
221 * @param maxwaitms maximum waiting micro-seconds to release
222 * @param forceflag status will be stored, it can be NULL if you don't need this information
223 *
224 * @return true if successful, otherwise returns false
225 *
226 * @note
227 * This will wait the semaphore to be released with in maxwaitms.
228 * If it it released by locker normally with in maxwaitms, forceflag will be set to false.
229 * But if maximum maxwaitms is exceed and the semaphore is released forcely, forceflag will
230 * be set to true.
231 */
232bool qsem_enter_force(int semid, int semno, int maxwaitms, bool *forceflag) {
233 int wait;
234 for (wait = 0; wait < maxwaitms; wait += 10) {
235 if (qsem_enter_nowait(semid, semno) == true) {
236 if (forceflag != NULL)
237 *forceflag = false;
238 return true;
239 }
240 usleep(10 * 1000); // sleep 10ms
241 }
242
243 DEBUG("force to unlock semaphore %d-%d", semid, semno);
244 while (true) {
245 qsem_leave(semid, semno);
246 if (qsem_enter_nowait(semid, semno) == true)
247 break;
248 }
249
250 if (forceflag != NULL)
251 *forceflag = true;
252 return true;
253}
254
255/**
256 * Turn off the flag of semaphore then leaving critical section
257 *
258 * @param semid semaphore identifier
259 * @param semno semaphore number
260 *
261 * @return true if successful, otherwise returns false
262 */
263bool qsem_leave(int semid, int semno) {
264 struct sembuf sbuf;
265
266 /* set sbuf */
267 sbuf.sem_num = semno;
268 sbuf.sem_op = 1;
269 sbuf.sem_flg = SEM_UNDO;
270
271 /* unlock */
272 if (semop(semid, &sbuf, 1) != 0)
273 return false;
274 return true;
275}
276
277/**
278 * Get the status of semaphore
279 *
280 * @param semid semaphore identifier
281 * @param semno semaphore number
282 *
283 * @return true for the flag on, false for the flag off
284 */
285bool qsem_check(int semid, int semno) {
286 if (semctl(semid, semno, GETVAL, 0) == 0)
287 return true; // locked
288 return false; // unlocked
289}
290
291/**
292 * Release semaphore to system
293 *
294 * @param semid semaphore identifier
295 *
296 * @return true if successful, otherwise returns false
297 */
298bool qsem_free(int semid) {
299 if (semid < 0)
300 return false;
301 if (semctl(semid, 0, IPC_RMID, 0) != 0)
302 return false;
303 return true;
304}
305
306#endif /* DISABLE_IPC */
bool qsem_enter_force(int semid, int semno, int maxwaitms, bool *forceflag)
Force to turn on the flag of semaphore.
Definition qsem.c:232
bool qsem_leave(int semid, int semno)
Turn off the flag of semaphore then leaving critical section.
Definition qsem.c:263
bool qsem_enter_nowait(int semid, int semno)
Try to turn on the flag of semaphore.
Definition qsem.c:202
int qsem_getid(const char *keyfile, int keyid)
Get semaphore identifier by keyfile and keyid for the existing semaphore.
Definition qsem.c:155
bool qsem_free(int semid)
Release semaphore to system.
Definition qsem.c:298
bool qsem_check(int semid, int semno)
Get the status of semaphore.
Definition qsem.c:285
int qsem_init(const char *keyfile, int keyid, int nsems, bool recreate)
Initialize semaphore.
Definition qsem.c:102
bool qsem_enter(int semid, int semno)
Turn on the flag of semaphore then entering critical section.
Definition qsem.c:180