libassa 3.5.0
|
00001 // -*- c++ -*- 00002 //------------------------------------------------------------------------------ 00003 // $Id: Semaphore.cpp,v 1.4 2006/07/20 02:30:54 vlg Exp $ 00004 //------------------------------------------------------------------------------ 00005 // Semaphore.C 00006 //------------------------------------------------------------------------------ 00007 // Copyright (c) 2000 by Vladislav Grinchenko 00008 // 00009 // This library is free software; you can redistribute it and/or 00010 // modify it under the terms of the GNU Library General Public 00011 // License as published by the Free Software Foundation; either 00012 // version 2 of the License, or (at your option) any later version. 00013 //------------------------------------------------------------------------------ 00014 // Created: 07/10/2000 00015 //------------------------------------------------------------------------------ 00016 00017 #if !defined(WIN32) 00018 00019 #include <sstream> 00020 #include <string> 00021 #include <iomanip> 00022 00023 #include "assa/Semaphore.h" 00024 00025 using namespace ASSA; 00026 00027 /*--- Static definitions and constants ---*/ 00028 00029 const int ASSA::Semaphore::BIGCOUNT = 10000; 00030 00031 sembuf Semaphore::m_op_lock [2] = 00032 { 00033 {2, 0, 0}, // Wait for [2] lock to equal 0, 00034 {2, 1, SEM_UNDO} // then increment [2] to 1 - this locks it. 00035 // UNDO to release the lock if processes 00036 // exits before explicitly unlocking. 00037 }; 00038 00039 sembuf Semaphore::m_op_endcreate [2] = 00040 { 00041 {1, -1, SEM_UNDO}, // Decrement [1] (proc counter) with undo on 00042 // exit, 00043 {2, -1, SEM_UNDO} // then decrement [2] (lock) back to 0. 00044 }; 00045 00046 sembuf Semaphore::m_op_open [2] = 00047 { 00048 {1, -1, SEM_UNDO}, // Decrement [1] (proc counter) with undo on 00049 // exit. 00050 }; 00051 00052 sembuf Semaphore::m_op_close [3] = 00053 { 00054 {2, 0, 0}, // Wait for [2] lock to equal 0, 00055 {2, 1, SEM_UNDO}, // then increment [2] to 1 - this locks it, 00056 {1, 1, SEM_UNDO} // then increment [1] (proc counter) 00057 }; 00058 00059 sembuf Semaphore::m_op_unlock [1] = 00060 { 00061 {2, -1, SEM_UNDO} // Decrement [2] (lock) back to 0 00062 }; 00063 00064 sembuf Semaphore::m_op_op [1] = 00065 { 00066 {0, 99, SEM_UNDO} // Decrement or increment [0] with undo on 00067 // exit. The 99 is set to the actual amount 00068 // to add or substract (positive or negative) 00069 }; 00070 00071 00072 //------------------------------------------------------------------------------ 00073 00074 00075 int 00076 Semaphore:: 00077 create (key_t key_, int initval_) 00078 { 00079 trace_with_mask("Semaphore::create", SEM); 00080 00081 register int semval; 00082 00083 union semnum { 00084 int val; 00085 struct semid_ds* buf; 00086 ushort* array; 00087 } semctrl_arg; 00088 00089 if (IPC_PRIVATE == key_) { 00090 EL((ASSAERR,"Not intended for private semaphores\n")); 00091 return (-1); 00092 } 00093 else if (key_ == (key_t) -1) { 00094 EL((ASSAERR,"Probably an ftok() error by caller\n")); 00095 return (-1); 00096 } 00097 m_key = key_; 00098 bool done = false; 00099 00100 while (!done) { 00101 if ( (m_id = semget (m_key, 3, 0666 | IPC_CREAT)) < 0) { 00102 EL((ASSAERR,"Permission problem or kernel tables full\n")); 00103 return (-1); 00104 } 00105 /* 00106 When the semaphore is created, we know that the value of 00107 all 3 set members is 0. 00108 00109 Get a lock on the semaphore by waiting for [2] to equal 0, 00110 then increment it. 00111 00112 There is a race condition here. There is a possibility 00113 that between the semget(2) and semop(2) below, another 00114 process can cal Semaphore:::close () member function 00115 which can remove a semaphore if that process is the last 00116 one using it. 00117 00118 Therefore, we handle the error condition of an invalid 00119 semaphore ID specially below, and if it does happen, we 00120 just go back and create it again. 00121 */ 00122 00123 if (semop (m_id, &m_op_lock[0], 2) < 0) { 00124 if (errno == EINVAL) { 00125 continue; 00126 } 00127 EL((ASSAERR,"Can't lock semaphore\n")); 00128 Assure_exit (false); 00129 } 00130 done = true; 00131 } // while (!done) 00132 00133 /* 00134 Get the value of the process counter. If it equals 0, 00135 then no one has initialized the semaphore yet. 00136 */ 00137 if ((semval = semctl (m_id, 1, GETVAL, 0)) < 0) { 00138 EL((ASSAERR,"Can't GETVAL\n")); 00139 Assure_exit (false); 00140 } 00141 00142 if (semval == 0) { 00143 /* 00144 We could initalize by doing a SETALL, but that 00145 would clear the adjust value that we set when 00146 we locked the semaphore above. Instead, we'll do 00147 two system calls to initialize semaphore value [0] 00148 and process counter [1]. 00149 */ 00150 semctrl_arg.val = initval_; 00151 00152 if (semctl (m_id, 0, SETVAL, semctrl_arg) < 0) { 00153 EL((ASSAERR,"Can't SETVAL[0]\n")); 00154 Assure_exit (false); 00155 } 00156 00157 semctrl_arg.val = BIGCOUNT; 00158 00159 if (semctl (m_id, 1, SETVAL, semctrl_arg) < 0) { 00160 EL((ASSAERR,"Can't SETVAL[1]\n")); 00161 Assure_exit (false); 00162 } 00163 } // if (semval == 0) 00164 00165 /*--- Decrement the process counter and then release the lock. ---*/ 00166 00167 if (semop (m_id, &m_op_endcreate[0], 2) < 0) { 00168 EL((ASSAERR,"Error on semop (ndcreate)\n")); 00169 Assure_exit (false); 00170 } 00171 return (m_id); 00172 } 00173 00174 int 00175 Semaphore:: 00176 open (key_t key_) 00177 { 00178 trace_with_mask("Semaphore::open", SEM); 00179 00180 if (IPC_PRIVATE == key_) { 00181 EL((ASSAERR,"Not intended for private semaphores\n")); 00182 return (-1); 00183 } 00184 else if (key_ == (key_t) -1) { 00185 EL((ASSAERR,"Probably an ftok() error by caller\n")); 00186 return (-1); 00187 } 00188 00189 m_key = key_; 00190 00191 if ((m_id = semget (m_key, 3, 0)) < 0) { 00192 EL((ASSAERR,"Error on semget(3)")); 00193 return (-1); 00194 } 00195 /*--- Decrement the process counter. No need for lock ---*/ 00196 00197 if (semop (m_id, &m_op_open[0], 1) < 0) { 00198 EL((ASSAERR,"Error on semget(open)\n")); 00199 Assure_exit(false); 00200 } 00201 return (m_id); 00202 } 00203 00204 void 00205 Semaphore:: 00206 remove () 00207 { 00208 trace_with_mask("Semaphore::remove", SEM); 00209 00210 if (m_id < 0 || m_key == ((key_t) -1) ) return; 00211 00212 if (semctl (m_id, 0, IPC_RMID, 0) < 0) { 00213 EL((ASSAERR,"Can't IPC_RMID\n")); 00214 Assure_exit(false); 00215 } 00216 init (); 00217 } 00218 00219 void 00220 Semaphore:: 00221 close () 00222 { 00223 trace_with_mask("Semaphore::close", SEM); 00224 00225 register int semval; 00226 00227 if (m_id < 0) return; 00228 00229 /* 00230 First get the lock on semaphore, then increment process counter. 00231 */ 00232 if (semop (m_id, &m_op_close[0], 3) < 0) { 00233 EL((ASSAERR,"Can't semop(2)\n")); 00234 Assure_exit(false); 00235 } 00236 /* 00237 Now that we have a lock, read the value of the process counter 00238 to see if this is the last reference to the semaphore. 00239 There is a race condition here (same as in Semaphore::create()). 00240 */ 00241 if ((semval = semctl (m_id, 1, GETVAL, 0)) < 0) { 00242 EL((ASSAERR,"Can't GETVAL\n")); 00243 Assure_exit(false); 00244 } 00245 00246 if (semval > BIGCOUNT) { 00247 EL((ASSAERR,"sem[1] > BIGCOUNT\n")); 00248 Assure_exit(false); 00249 } 00250 else if (semval == BIGCOUNT) { 00251 remove (); 00252 } 00253 else if (semop (m_id, &m_op_unlock[0], 1) < 0) { 00254 EL((ASSAERR,"Can't unlock\n")); 00255 Assure_exit(false); 00256 } 00257 /*--- Invalidate ---*/ 00258 init (); 00259 } 00260 00261 00262 void 00263 Semaphore:: 00264 op (int value_) 00265 { 00266 /* Test if m_id is still valid. If it fails, then 00267 * next operation is failing because of it. If not, 00268 * then something else happens here. 00269 */ 00270 trace_with_mask("Semaphore::op", SEM); 00271 00272 int semval = 0; 00273 dump (); 00274 00275 if ((semval = semctl (m_id, 1, GETVAL, 0)) < 0) { 00276 EL((ASSAERR,"Can't GETVAL\n")); 00277 Assure_exit (false); 00278 } 00279 /* This will fail on Solaris? */ 00280 00281 if ((m_op_op[0].sem_op = value_) == 0) { 00282 EL((ASSAERR,"Can't have value_ == 0\n")); 00283 Assure_exit(false); 00284 } 00285 00286 if (semop (m_id, &m_op_op[0], 1) < 0) { 00287 EL((ASSAERR,"sem_op error\n")); 00288 Assure_exit(false); 00289 } 00290 } 00291 00292 void 00293 Semaphore:: 00294 dump (void) const 00295 { 00296 trace_with_mask("Semaphore::dump", SEM); 00297 00298 std::ostringstream msg; 00299 msg << "\n\n\tKey.....: "; 00300 00301 if (m_key == (key_t) -1) { 00302 msg << m_key; 00303 } 00304 else { 00305 msg << "0x" << std::hex << m_key << std::dec; 00306 } 00307 00308 msg << "\n\tID......: " << m_id << "\n\n"; 00309 00310 if (m_id >= 0 && m_key >= (key_t) -1) { 00311 msg << "\tsemval [0]\tproc counter[1]\tlock [2]\n" 00312 << "\t----------\t---------------\t--------\n"; 00313 00314 /*--- Get value of element in semaphore set ---*/ 00315 msg << "\t " << semctl (m_id, 0, GETVAL) 00316 << "\t\t " << semctl (m_id, 1, GETVAL) 00317 << "\t\t " << semctl (m_id, 2, GETVAL); 00318 } 00319 else { 00320 msg << "Semaphore id = -1. No info is available."; 00321 } 00322 msg << std::ends; 00323 DL((SEM,"%s\n\n", msg.str ().c_str ())); 00324 } 00325 00326 #endif /* !defined(WIN32) */ 00327