summaryrefslogtreecommitdiff
path: root/ace/SV_Semaphore_Complex.cpp
blob: b523d2e0007e64a0f4ef11ede87faa56e2ada746 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// SV_Semaphore_Complex.cpp
// $Id$

#include "ace/SV_Semaphore_Complex.h"
#include "ace/Log_Msg.h"
#include "ace/os_include/sys/sem.h"

#if defined (ACE_LACKS_INLINE_FUNCTIONS)
#include "ace/SV_Semaphore_Complex.i"
#endif

ACE_RCSID(ace, SV_Semaphore_Complex, "$Id$")

ACE_ALLOC_HOOK_DEFINE(ACE_SV_Semaphore_Complex)

void
ACE_SV_Semaphore_Complex::dump (void) const
{
  ACE_TRACE ("ACE_SV_Semaphore_Complex::dump");
}

// initial value of process
const int ACE_SV_Semaphore_Complex::BIGCOUNT_ = 10000;

// Define the ACE_SV_Semaphore operation arrays for the semop() calls.
sembuf ACE_SV_Semaphore_Complex::op_lock_[2] =
{
  {0, 0, 0},			// Wait for [0] (lock) to equal 0
  {0, 1, SEM_UNDO},		// then increment [0] to 1 - this locks it.
                                // UNDO to release the lock if processes exit
				// before explicitly unlocking.
};

sembuf ACE_SV_Semaphore_Complex::op_endcreate_[2] =
{
  {1, -1, SEM_UNDO},		// Decrement [1] (proc counter) with undo on
                                // exit, UNDO to adjust proc counter if
				// process exits before explicitly calling close()
  {0, -1, SEM_UNDO},		// the decrement [0] (lock) back to 0
};

sembuf ACE_SV_Semaphore_Complex::op_open_[1] =
{
  {1, -1, SEM_UNDO},		// Decrement [1] (proc counter) with undo on
                                // exit.
};

sembuf ACE_SV_Semaphore_Complex::op_close_[3] =
{
  {0, 0, 0},			// Wait for [0] (lock) to equal 0
  {0, 1, SEM_UNDO},		// then increment [0] to 1 - this lock it
  {1, 1, SEM_UNDO},		// then increment [1] (proc counter)
};

sembuf ACE_SV_Semaphore_Complex::op_unlock_[1] =
{
  {0, -1, SEM_UNDO},		// Decrement [0] (lock) back to 0
};

// Open or create an array of SV_Semaphores.  We return 0 if all is OK, else -1.

int
ACE_SV_Semaphore_Complex::open (key_t k,
				int create,
				int initial_value,
				u_short nsems,
				int perms)
{
  ACE_TRACE ("ACE_SV_Semaphore_Complex::open");
  if (k == IPC_PRIVATE)
    return -1;

  this->key_ = k;

  // Must include a count for the 2 additional semaphores we use
  // internally.
  this->sem_number_ = nsems + 2;

  if (create == ACE_SV_Semaphore_Complex::ACE_CREATE)
    {
      int result;

      do
	{
	  this->internal_id_ = ACE_OS::semget
            (this->key_,
             (u_short) 2 + nsems,
             perms | ACE_SV_Semaphore_Complex::ACE_CREATE);

	  if (this->internal_id_ == -1)
	    return -1; // permission problem or tables full

	  // When the <ACE_SV_Semaphore_Complex> is created, we know
	  // that the value of all 3 members is 0.  Get a lock on the
	  // <ACE_SV_Semaphore_Complex> by waiting for [0] to equal 0,
	  // then increment it.

	  // There is a race condition here. There is the possibility
	  // that between the <semget> above and the <semop> below,
	  // another process can call out <close> function which can
	  // remove the <ACE_SV_Semaphore> if that process is the last
	  // one using it.  Therefor we handle the error condition of
	  // an invalid <ACE_SV_Semaphore> ID specifically below, and
	  // if it does happen, we just go back and create it again.
	  result = ACE_OS::semop (this->internal_id_,
				  &ACE_SV_Semaphore_Complex::op_lock_[0],
				  2);
	}
      while (result == -1 && (errno == EINVAL || errno == EIDRM));

      if (result == -1)
	return -1;

      // Get the value of the process counter. If it equals 0, then no
      // one has initialized the ACE_SV_Semaphore yet.

      int semval = ACE_SV_Semaphore_Simple::control (GETVAL, 0, 1);

      if (semval == -1)
	return this->init ();
      else if (semval == 0)
	{
	  // We should initialize by doing a SETALL, but that would
	  // clear the adjust value that we set when we locked the
	  // ACE_SV_Semaphore above. Instead we do system calls to
	  // initialize [1], as well as all the nsems SV_Semaphores.

	  if (ACE_SV_Semaphore_Simple::control (SETVAL,
						ACE_SV_Semaphore_Complex::BIGCOUNT_,
						1) == -1)
	    return -1;
	  else
	    for (int i = 0; i < nsems; i++)
	      if (this->control (SETVAL, initial_value, i) == -1)
		return -1;
	}

      // Decrement the process counter and then release the lock.
      return ACE_OS::semop (this->internal_id_,
			    &ACE_SV_Semaphore_Complex::op_endcreate_[0],
			    2);
    }
  else
    {
      this->internal_id_ = ACE_OS::semget (this->key_, 2 + nsems, 0);
      if (this->internal_id_ == -1)
	return -1;			// doesn't exist or tables full

      // Decrement the process counter. We don't need a lock to do this.
      if (ACE_OS::semop (this->internal_id_,
			 &ACE_SV_Semaphore_Complex::op_open_[0], 1) < 0)
	return this->init ();
      return 0;
    }
}

int
ACE_SV_Semaphore_Complex::open (const char *name,
				int flags,
				int initial_value,
				u_short nsems,
				int perms)
{
  ACE_TRACE ("ACE_SV_Semaphore_Complex::open");
  return this->open (ACE_SV_Semaphore_Simple::name_2_key (name),
		     flags, initial_value, nsems, perms);
}

// Close a ACE_SV_Semaphore.  Unlike the remove above, this function
// is for a process to call before it exits, when it is done with the
// ACE_SV_Semaphore.  We "decrement" the counter of processes using
// the ACE_SV_Semaphore, and if this was the last one, we can remove
// the ACE_SV_Semaphore.

int
ACE_SV_Semaphore_Complex::close (void)
{
  ACE_TRACE ("ACE_SV_Semaphore_Complex::close");
  int semval;

  if (this->key_ == (key_t) - 1 || this->internal_id_ == -1)
    return -1;

  // The following semop() first gets a lock on the ACE_SV_Semaphore,
  // then increments [1] - the process number.

  if (ACE_OS::semop (this->internal_id_,
		     &ACE_SV_Semaphore_Complex::op_close_[0],
		     3) == -1)
    return -1;

  // Now that we have a lock, read the value of the process counter to
  // see if this is the last reference to the ACE_SV_Semaphore. There
  // is a race condition here - see the comments in create ().

  if ((semval = ACE_SV_Semaphore_Simple::control (GETVAL, 0, 1)) == -1)
    return -1;

  if (semval > ACE_SV_Semaphore_Complex::BIGCOUNT_)
    return -1;
  else if (semval == ACE_SV_Semaphore_Complex::BIGCOUNT_)
    return this->remove ();
  else
    {
      int result = ACE_OS::semop (this->internal_id_,
				  &ACE_SV_Semaphore_Complex::op_unlock_[0], 1);
      this->init ();
      return result;
    }
}

ACE_SV_Semaphore_Complex::ACE_SV_Semaphore_Complex (key_t k,
						    int flags,
						    int initial_value,
						    u_short nsems,
						    int perms)
{
  ACE_TRACE ("ACE_SV_Semaphore_Complex::ACE_SV_Semaphore_Complex");
  if (this->open (k, flags, initial_value, nsems, perms) == -1)
    ACE_ERROR ((LM_ERROR,  ACE_LIB_TEXT ("%p\n"),  ACE_LIB_TEXT ("ACE_SV_Semaphore_Complex")));
}

ACE_SV_Semaphore_Complex::ACE_SV_Semaphore_Complex (const char *name,
						    int flags,
						    int initial_value,
						    u_short nsems,
						    int perms)
{
  ACE_TRACE ("ACE_SV_Semaphore_Complex::ACE_SV_Semaphore_Complex");

  key_t key;

  if (name == 0)
    key = ACE_DEFAULT_SEM_KEY;
  else
    key = this->name_2_key (name);

  if (this->open (key, flags, initial_value, nsems, perms) == -1)
    ACE_ERROR ((LM_ERROR,  ACE_LIB_TEXT ("%p\n"),  ACE_LIB_TEXT ("ACE_SV_Semaphore_Complex")));
}

ACE_SV_Semaphore_Complex::~ACE_SV_Semaphore_Complex (void)
{
  ACE_TRACE ("ACE_SV_Semaphore_Complex::~ACE_SV_Semaphore_Complex");
  if (this->internal_id_ >= 0)
    this->close ();
}

ACE_SV_Semaphore_Complex::ACE_SV_Semaphore_Complex (void)
{
  ACE_TRACE ("ACE_SV_Semaphore_Complex::ACE_SV_Semaphore_Complex");
  this->init ();
}