Semaphore.cpp

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

Generated on Mon Dec 19 16:37:15 2005 for libassa by  doxygen 1.4.5