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  */
102 int 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  */
155 int 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  */
180 bool 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  */
202 bool 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  */
232 bool 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  */
263 bool 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  */
285 bool 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  */
298 bool 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_leave(int semid, int semno)
Turn off the flag of semaphore then leaving critical section.
Definition: qsem.c:263
bool qsem_enter_force(int semid, int semno, int maxwaitms, bool *forceflag)
Force to turn on the flag of semaphore.
Definition: qsem.c:232
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_enter_nowait(int semid, int semno)
Try to turn on the flag of semaphore.
Definition: qsem.c:202
bool qsem_enter(int semid, int semno)
Turn on the flag of semaphore then entering critical section.
Definition: qsem.c:180
int qsem_init(const char *keyfile, int keyid, int nsems, bool recreate)
Initialize semaphore.
Definition: qsem.c:102
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