libassa  3.5.1
Semaphore.cpp
Go to the documentation of this file.
1 // -*- c++ -*-
2 //------------------------------------------------------------------------------
3 // $Id: Semaphore.cpp,v 1.5 2012/05/20 04:12:18 vlg Exp $
4 //------------------------------------------------------------------------------
5 // Semaphore.C
6 //------------------------------------------------------------------------------
7 // Copyright (c) 2000 by Vladislav Grinchenko
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Library General Public
11 // License as published by the Free Software Foundation; either
12 // version 2 of the License, or (at your option) any later version.
13 //------------------------------------------------------------------------------
14 // Created: 07/10/2000
15 //------------------------------------------------------------------------------
16 
17 #if !defined(WIN32)
18 
19 #include <sstream>
20 #include <string>
21 #include <iomanip>
22 
23 #include "assa/Semaphore.h"
24 
25 using namespace ASSA;
26 
27 /*--- Static definitions and constants ---*/
28 
29 const int ASSA::Semaphore::BIGCOUNT = 10000;
30 
31 sembuf Semaphore::m_op_lock [2] =
32 {
33  {2, 0, 0}, // Wait for [2] lock to equal 0,
34  {2, 1, SEM_UNDO} // then increment [2] to 1 - this locks it.
35  // UNDO to release the lock if processes
36  // exits before explicitly unlocking.
37 };
38 
39 sembuf Semaphore::m_op_endcreate [2] =
40 {
41  {1, -1, SEM_UNDO}, // Decrement [1] (proc counter) with undo on
42  // exit,
43  {2, -1, SEM_UNDO} // then decrement [2] (lock) back to 0.
44 };
45 
46 sembuf Semaphore::m_op_open [2] =
47 {
48  {1, -1, SEM_UNDO}, // Decrement [1] (proc counter) with undo on
49  // exit.
50 };
51 
52 sembuf Semaphore::m_op_close [3] =
53 {
54  {2, 0, 0}, // Wait for [2] lock to equal 0,
55  {2, 1, SEM_UNDO}, // then increment [2] to 1 - this locks it,
56  {1, 1, SEM_UNDO} // then increment [1] (proc counter)
57 };
58 
59 sembuf Semaphore::m_op_unlock [1] =
60 {
61  {2, -1, SEM_UNDO} // Decrement [2] (lock) back to 0
62 };
63 
64 sembuf Semaphore::m_op_op [1] =
65 {
66  {0, 99, SEM_UNDO} // Decrement or increment [0] with undo on
67  // exit. The 99 is set to the actual amount
68  // to add or substract (positive or negative)
69 };
70 
71 //------------------------------------------------------------------------------
72 int
74 create (key_t key_, int initval_)
75 {
76  trace_with_mask("Semaphore::create", SEM);
77 
78  register int semval;
79 
80  union semnum {
81  int val;
82  struct semid_ds* buf;
83  ushort* array;
84  } semctrl_arg;
85 
86  if (IPC_PRIVATE == key_) {
87  EL((ASSAERR,"Not intended for private semaphores\n"));
88  return (-1);
89  }
90  else if (key_ == (key_t) -1) {
91  EL((ASSAERR,"Probably an ftok() error by caller\n"));
92  return (-1);
93  }
94 
95  m_key = key_;
96  bool done = false;
97 
98  while (!done) {
99  if ( (m_id = semget (m_key, 3, 0666 | IPC_CREAT)) < 0) {
100  EL((ASSAERR,"Permission problem or kernel tables full\n"));
101  return (-1);
102  }
103  /*
104  When the semaphore is created, we know that the value of
105  all 3 set members is 0.
106 
107  Get a lock on the semaphore by waiting for [2] to equal 0,
108  then increment it.
109 
110  There is a race condition here. There is a possibility
111  that between the semget(2) and semop(2) below, another
112  process can cal Semaphore:::close () member function
113  which can remove a semaphore if that process is the last
114  one using it.
115 
116  Therefore, we handle the error condition of an invalid
117  semaphore ID specially below, and if it does happen, we
118  just go back and create it again.
119  */
120 
121  if (semop (m_id, &m_op_lock[0], 2) < 0) {
122  if (errno == EINVAL) {
123  continue;
124  }
125  EL((ASSAERR,"Can't lock semaphore\n"));
126  Assure_exit (false);
127  }
128  done = true;
129  } // while (!done)
130 
131  /*
132  Get the value of the process counter. If it equals 0,
133  then no one has initialized the semaphore yet.
134  */
135  if ((semval = semctl (m_id, 1, GETVAL, 0)) < 0) {
136  EL((ASSAERR,"Can't GETVAL\n"));
137  Assure_exit (false);
138  }
139 
140  if (semval == 0) {
141  /*
142  We could initalize by doing a SETALL, but that
143  would clear the adjust value that we set when
144  we locked the semaphore above. Instead, we'll do
145  two system calls to initialize semaphore value [0]
146  and process counter [1].
147  */
148  semctrl_arg.val = initval_;
149 
150  if (semctl (m_id, 0, SETVAL, semctrl_arg) < 0) {
151  EL((ASSAERR,"Can't SETVAL[0]\n"));
152  Assure_exit (false);
153  }
154 
155  semctrl_arg.val = BIGCOUNT;
156 
157  if (semctl (m_id, 1, SETVAL, semctrl_arg) < 0) {
158  EL((ASSAERR,"Can't SETVAL[1]\n"));
159  Assure_exit (false);
160  }
161  } // if (semval == 0)
162 
163  /*--- Decrement the process counter and then release the lock. ---*/
164 
165  if (semop (m_id, &m_op_endcreate[0], 2) < 0) {
166  EL((ASSAERR,"Error on semop (ndcreate)\n"));
167  Assure_exit (false);
168  }
169  return (m_id);
170 }
171 
172 int
174 open (key_t key_)
175 {
176  trace_with_mask("Semaphore::open", SEM);
177 
178  if (IPC_PRIVATE == key_) {
179  EL((ASSAERR,"Not intended for private semaphores\n"));
180  return (-1);
181  }
182  else if (key_ == (key_t) -1) {
183  EL((ASSAERR,"Probably an ftok() error by caller\n"));
184  return (-1);
185  }
186 
187  m_key = key_;
188 
189  if ((m_id = semget (m_key, 3, 0)) < 0) {
190  EL((ASSAERR,"Error on semget(3)"));
191  return (-1);
192  }
193  /*--- Decrement the process counter. No need for lock ---*/
194 
195  if (semop (m_id, &m_op_open[0], 1) < 0) {
196  EL((ASSAERR,"Error on semget(open)\n"));
197  Assure_exit(false);
198  }
199  return (m_id);
200 }
201 
202 void
204 remove ()
205 {
206  trace_with_mask("Semaphore::remove", SEM);
207 
208  if (m_id < 0 || m_key == ((key_t) -1) ) return;
209 
210  if (semctl (m_id, 0, IPC_RMID, 0) < 0) {
211  EL((ASSAERR,"Can't IPC_RMID\n"));
212  Assure_exit(false);
213  }
214  init ();
215 }
216 
217 void
219 close ()
220 {
221  trace_with_mask("Semaphore::close", SEM);
222 
223  register int semval;
224 
225  if (m_id < 0) return;
226 
227  /*
228  First get the lock on semaphore, then increment process counter.
229  */
230  if (semop (m_id, &m_op_close[0], 3) < 0) {
231  EL((ASSAERR,"Can't semop(2)\n"));
232  Assure_exit(false);
233  }
234  /*
235  Now that we have a lock, read the value of the process counter
236  to see if this is the last reference to the semaphore.
237  There is a race condition here (same as in Semaphore::create()).
238  */
239  if ((semval = semctl (m_id, 1, GETVAL, 0)) < 0) {
240  EL((ASSAERR,"Can't GETVAL\n"));
241  Assure_exit(false);
242  }
243 
244  if (semval > BIGCOUNT) {
245  EL((ASSAERR,"sem[1] > BIGCOUNT\n"));
246  Assure_exit(false);
247  }
248  else if (semval == BIGCOUNT) {
249  remove ();
250  }
251  else if (semop (m_id, &m_op_unlock[0], 1) < 0) {
252  EL((ASSAERR,"Can't unlock\n"));
253  Assure_exit(false);
254  }
255  /*--- Invalidate ---*/
256  init ();
257 }
258 
259 
260 void
262 op (int value_)
263 {
264  /* Test if m_id is still valid. If it fails, then
265  * next operation is failing because of it. If not,
266  * then something else happens here.
267  */
268  trace_with_mask("Semaphore::op", SEM);
269 
270  int semval = 0;
271  dump ();
272 
273  if ((semval = semctl (m_id, 1, GETVAL, 0)) < 0) {
274  EL((ASSAERR,"Can't GETVAL\n"));
275  Assure_exit (false);
276  }
277 
278  /* This will fail on Solaris? */
279 
280  if ((m_op_op[0].sem_op = value_) == 0) {
281  EL((ASSAERR,"Can't have value_ == 0\n"));
282  Assure_exit(false);
283  }
284 
285  if (semop (m_id, &m_op_op[0], 1) < 0) {
286  EL((ASSAERR,"sem_op error\n"));
287  Assure_exit(false);
288  }
289 }
290 
291 void
293 dump (void) const
294 {
295  trace_with_mask("Semaphore::dump", SEM);
296 
297  std::ostringstream msg;
298  msg << "\n\n\tKey.....: ";
299 
300  if (m_key == (key_t) -1) {
301  msg << m_key;
302  }
303  else {
304  msg << "0x" << std::hex << m_key << std::dec;
305  }
306 
307  msg << "\n\tID......: " << m_id << "\n\n";
308 
309  if (m_id >= 0 && m_key >= (key_t) -1) {
310  msg << "\tsemval [0]\tproc counter[1]\tlock [2]\n"
311  << "\t----------\t---------------\t--------\n";
312 
313  /*--- Get value of element in semaphore set ---*/
314  msg << "\t " << semctl (m_id, 0, GETVAL)
315  << "\t\t " << semctl (m_id, 1, GETVAL)
316  << "\t\t " << semctl (m_id, 2, GETVAL);
317  }
318  else {
319  msg << "Semaphore id = -1. No info is available.";
320  }
321  msg << std::ends;
322  DL((SEM,"%s\n\n", msg.str ().c_str ()));
323 }
324 
325 #endif /* !defined(WIN32) */
326 
#define Assure_exit(exp_)
Macro that makes program exit if assert fails.
Definition: Assure.h:39
#define EL(X)
A macro for writing error message to the Logger.
Definition: Logger.h:285
#define DL(X)
A macro for writing debug message to the Logger.
Definition: Logger.h:273
#define trace_with_mask(s, m)
trace_with_mask() is used to trace function call chain in C++ program.
Definition: Logger.h:437
Semaphore class provides a simpler and easier interface to System V semaphore system calls.
static sembuf m_op_open[2]
Decrement process counter with undo on exit.
Definition: Semaphore.h:171
int m_id
Semaphore's id.
Definition: Semaphore.h:154
void remove()
Remove a semaphore.
Definition: Semaphore.cpp:204
int create(key_t key_, int initval_=1)
Create a semaphore with a specified initial value.
Definition: Semaphore.cpp:74
void init()
Initalize by invalidating data members.
Definition: Semaphore.h:211
static sembuf m_op_op[1]
Decrement or increment semaphore with undo on exit.
Definition: Semaphore.h:186
static sembuf m_op_unlock[1]
Decremetn lock back to 0.
Definition: Semaphore.h:180
static sembuf m_op_close[3]
Wait for lock to equal 0, then increment lock to 1 (lock it), then increment process counter.
Definition: Semaphore.h:176
key_t m_key
Semaphore's key.
Definition: Semaphore.h:151
void op(int val_)
General semaphore operation.
Definition: Semaphore.cpp:262
static sembuf m_op_lock[2]
Wait for lock to equal 0, then increment lock to 1 - this locks it.
Definition: Semaphore.h:161
static const int BIGCOUNT
Definition: Semaphore.h:157
void dump(void) const
Dump the objects state along with the state of the semaphore (if connected) to the log file.
Definition: Semaphore.cpp:293
static sembuf m_op_endcreate[2]
Decrement process counter with undo on exit, then decrement lock back to 0.
Definition: Semaphore.h:166
void close()
Close a semaphore.
Definition: Semaphore.cpp:219
int open(key_t key_)
Open a semaphore that must already exist.
Definition: Semaphore.cpp:174
Definition: Acceptor.h:40
@ SEM
Class Semaphore messages
Definition: LogMask.h:37
@ ASSAERR
ASSA and system errors
Definition: LogMask.h:34
Socket & ends(Socket &os_)
ends manipulator.
Definition: Socket.h:622