Project

General

Profile

Statistics
| Revision:

root / trunk / code / projects / colonet / server / vision / sysvsem.cpp @ 1428

History | View | Annotate | Download (8.92 KB)

1 939 rcahoon
/*
2
   +----------------------------------------------------------------------+
3
   | PHP Version 5                                                        |
4
   +----------------------------------------------------------------------+
5
   | Copyright (c) 1997-2008 The PHP Group                                |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to version 3.01 of the PHP license,      |
8
   | that is bundled with this package in the file LICENSE, and is        |
9
   | available through the world-wide-web at the following url:           |
10
   | http://www.php.net/license/3_01.txt                                  |
11
   | If you did not receive a copy of the PHP license and are unable to   |
12
   | obtain it through the world-wide-web, please send a note to          |
13
   | license@php.net so we can mail you a copy immediately.               |
14
   +----------------------------------------------------------------------+
15
   | Authors: Tom May <tom@go2net.com>                                    |
16
   |          Gavin Sherry <gavin@linuxworld.com.au>                      |
17
   +----------------------------------------------------------------------+
18
 */
19
20
/* $Id: sysvsem.c,v 1.51.2.3.2.3 2007/12/31 07:20:13 sebastian Exp $ */
21
22
/* Latest update build anc tested on Linux 2.2.14
23
 *
24
 * This has been built and tested on Solaris 2.6 and Linux 2.1.122.
25
 * It may not compile or execute correctly on other systems.
26
 *
27
 * sas: Works for me on Linux 2.0.36 and FreeBSD 3.0-current
28
 */
29
30
#include <sys/types.h>
31
#include <sys/ipc.h>
32
#include <sys/sem.h>
33
#include <errno.h>
34
35
#include <string.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
39
#include <php/main/php_config.h>
40
41
#include "php_sysvsem.h"
42
43
#if !HAVE_SEMUN
44
45
union semun {
46
        int val;                    /* value for SETVAL */
47
        struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
48
        unsigned short int *array;  /* array for GETALL, SETALL */
49
        struct seminfo *__buf;      /* buffer for IPC_INFO */
50
};
51
52
#undef HAVE_SEMUN
53
#define HAVE_SEMUN 1
54
55
#endif
56
57
/* Semaphore functions using System V semaphores.  Each semaphore
58
 * actually consists of three semaphores allocated as a unit under the
59
 * same key.  Semaphore 0 (SYSVSEM_SEM) is the actual semaphore, it is
60
 * initialized to max_acquire and decremented as processes acquire it.
61
 * The value of semaphore 1 (SYSVSEM_USAGE) is a count of the number
62
 * of processes using the semaphore.  After calling semget(), if a
63
 * process finds that the usage count is 1, it will set the value of
64
 * SYSVSEM_SEM to max_acquire.  This allows max_acquire to be set and
65
 * track the PHP code without having a global init routine or external
66
 * semaphore init code.  Except see the bug regarding a race condition
67
 * php_sysvsem_get().  Semaphore 2 (SYSVSEM_SETVAL) serializes the
68
 * calls to GETVAL SYSVSEM_USAGE and SETVAL SYSVSEM_SEM.  It can be
69
 * acquired only when it is zero.
70
 */
71
72
#define SYSVSEM_SEM                0
73
#define SYSVSEM_USAGE        1
74
#define SYSVSEM_SETVAL        2
75
76
/* {{{ release_sysvsem_sem
77
 */
78
sysvsem::~sysvsem()
79
{
80
        struct sembuf sop[2];
81
        int opcount = 1;
82
83
        /* Decrement the usage count. */
84
        sop[0].sem_num = SYSVSEM_USAGE;
85
        sop[0].sem_op  = -1;
86
        sop[0].sem_flg = SEM_UNDO;
87
        /* Release the semaphore if it has been acquired but not released. */
88
        if (this->count) {
89
                sop[1].sem_num = SYSVSEM_SEM;
90
                sop[1].sem_op  = this->count;
91
                sop[1].sem_flg = SEM_UNDO;
92
93
                opcount++;
94
        }
95
96
        semop(this->semid, sop, opcount);
97
98
99
        //TODO: Need to auto remove() semaphore when SYSVSEM_USAGE is 0
100
101
        //efree(sem_ptr);
102
}
103
/* }}} */
104
#define SETVAL_WANTS_PTR
105
106
#if defined(_AIX)
107
#undef SETVAL_WANTS_PTR
108
#endif
109
110
/* {{{ proto resource sem_get(int key [, int max_acquire [, int perm [, int auto_release]])
111
         Return an id for the semaphore with the given key, and allow max_acquire (default 1) processes to acquire it simultaneously */
112
/*  max_aquire = 1, perm = 0666, auto_release = 1 */
113
sysvsem::sysvsem(long key, long max_acquire, long perm, long auto_release) throw (sysvsem_error)
114
{
115
        int semid;
116
        struct sembuf sop[3];
117
        int count;
118
119
        /* Get/create the semaphore.  Note that we rely on the semaphores
120
         * being zeroed when they are created.  Despite the fact that
121
         * the(?)  Linux semget() man page says they are not initialized,
122
         * the kernel versions 2.0.x and 2.1.z do in fact zero them.
123
         */
124
125
        semid = semget(key, 3, perm|IPC_CREAT);
126
        if (semid == -1) {
127
                fprintf(stderr, "%s failed for key 0x%lx: %s\n", __FUNCTION__, key, strerror(errno));
128
                throw sysvsem_error("failed to open semaphore");
129
        }
130
131
        /* Find out how many processes are using this semaphore.  Note
132
         * that on Linux (at least) there is a race condition here because
133
         * semaphore undo on process exit is not atomic, so we could
134
         * acquire SYSVSEM_SETVAL before a crashed process has decremented
135
         * SYSVSEM_USAGE in which case count will be greater than it
136
         * should be and we won't set max_acquire.  Fortunately this
137
         * doesn't actually matter in practice.
138
         */
139
140
        /* Wait for sem 1 to be zero . . . */
141
142
        sop[0].sem_num = SYSVSEM_SETVAL;
143
        sop[0].sem_op  = 0;
144
        sop[0].sem_flg = 0;
145
146
        /* . . . and increment it so it becomes non-zero . . . */
147
148
        sop[1].sem_num = SYSVSEM_SETVAL;
149
        sop[1].sem_op  = 1;
150
        sop[1].sem_flg = SEM_UNDO;
151
152
        /* . . . and increment the usage count. */
153
154
        sop[2].sem_num = SYSVSEM_USAGE;
155
        sop[2].sem_op  = 1;
156
        sop[2].sem_flg = SEM_UNDO;
157
        while (semop(semid, sop, 3) == -1) {
158
                if (errno != EINTR) {
159
                        fprintf(stderr, "%s failed acquiring SYSVSEM_SETVAL for key 0x%lx: %s\n", __FUNCTION__, key, strerror(errno));
160
                        break;
161
                }
162
        }
163
164
        /* Get the usage count. */
165
        count = semctl(semid, SYSVSEM_USAGE, GETVAL, NULL);
166
        if (count == -1) {
167
                fprintf(stderr, "%s failed for key 0x%lx: %s\n", __FUNCTION__, key, strerror(errno));
168
        }
169
170
        printf("proc count: %d\n", count);
171
172
        /* If we are the only user, then take this opportunity to set the max. */
173
174
        if (count == 1) {
175
#if HAVE_SEMUN
176
                /* This is correct for Linux which has union semun. */
177
                union semun semarg;
178
                semarg.val = max_acquire;
179
                if (semctl(semid, SYSVSEM_SEM, SETVAL, semarg) == -1) {
180
                        fprintf(stderr, "%s failed for key 0x%lx: %s\n", __FUNCTION__, key, strerror(errno));
181
                }
182
#elif defined(SETVAL_WANTS_PTR)
183
                /* This is correct for Solaris 2.6 which does not have union semun. */
184
                if (semctl(semid, SYSVSEM_SEM, SETVAL, &max_acquire) == -1) {
185
                        fprintf(stderr, "%s failed for key 0x%lx: %s\n", __FUNCTION__, key, strerror(errno));
186
                }
187
#else
188
                /* This works for i.e. AIX */
189
                if (semctl(semid, SYSVSEM_SEM, SETVAL, max_acquire) == -1) {
190
                        fprintf(stderr, "%s failed for key 0x%lx: %s\n", __FUNCTION__, key, strerror(errno));
191
                }
192
#endif
193
        }
194
195
        /* Set semaphore 1 back to zero. */
196
197
        sop[0].sem_num = SYSVSEM_SETVAL;
198
        sop[0].sem_op  = -1;
199
        sop[0].sem_flg = SEM_UNDO;
200
        while (semop(semid, sop, 1) == -1) {
201
                if (errno != EINTR) {
202
                        fprintf(stderr, "%s failed releasing SYSVSEM_SETVAL for key 0x%lx: %s\n", __FUNCTION__, key, strerror(errno));
203
                        break;
204
                }
205
        }
206
207
        //sem_ptr = (sysvsem_sem *) emalloc(sizeof(sysvsem_sem));
208
        this->key   = key;
209
        this->semid = semid;
210
        this->count = 0;
211
        this->auto_release = auto_release;
212
213
        //this->id = (int)sem_ptr;
214
215
        //return sem_ptr;
216
}
217
/* }}} */
218
219
/* {{{ php_sysvsem_semop
220
 */
221
int sysvsem::php_sysvsem_semop(int acquire)
222
{
223
        struct sembuf sop;
224
225
        if (!acquire && this->count == 0) {
226
                fprintf(stderr, "SysV semaphore 0x%x is not currently acquired\n", this->key);
227
                return 0;
228
        }
229
230
        sop.sem_num = SYSVSEM_SEM;
231
        sop.sem_op  = acquire ? -1 : 1;
232
        sop.sem_flg = SEM_UNDO;
233
234
        while (semop(this->semid, &sop, 1) == -1) {
235
                if (errno != EINTR) {
236
                        fprintf(stderr, "%s failed to %s semaphore 0x%x: %s\n", __FUNCTION__, acquire ? "acquire" : "release", this->key, strerror(errno));
237
                        return 0;
238
                }
239
        }
240
241
        this->count -= acquire ? -1 : 1;
242
        return 1;
243
}
244
/* }}} */
245
246
/* {{{ proto bool sem_acquire(resource id)
247
         Acquires the semaphore with the given id, blocking if necessary */
248
int sysvsem::sem_acquire()
249
{
250
        return this->php_sysvsem_semop(1);
251
}
252
/* }}} */
253
254
/* {{{ proto bool sem_release(resource id)
255
         Releases the semaphore with the given id */
256
int sysvsem::sem_release()
257
{
258
        return this->php_sysvsem_semop(0);
259
}
260
/* }}} */
261
262
/* {{{ proto bool sem_remove(resource id)
263
         Removes semaphore from Unix systems */
264
265
/*
266
 * contributed by Gavin Sherry gavin@linuxworld.com.au
267
 * Fri Mar 16 00:50:13 EST 2001
268
 */
269
270
int sysvsem::sem_remove()
271
{
272
#if HAVE_SEMUN
273
        union semun un;
274
        struct semid_ds buf;
275
#endif
276
277
#if HAVE_SEMUN
278
        un.buf = &buf;
279
        if (semctl(this->semid, 0, IPC_STAT, un) < 0) {
280
#else
281
        if (semctl(this->semid, 0, IPC_STAT, NULL) < 0) {
282
#endif
283
                fprintf(stderr, "SysV semaphore 0x%x does not (any longer) exist\n", this->key);
284
                return 0;
285
        }
286
287
#if HAVE_SEMUN
288
        if (semctl(this->semid, 0, IPC_RMID, un) < 0) {
289
#else
290
        if (semctl(this->semid, 0, IPC_RMID, NULL) < 0) {
291
#endif
292
                fprintf(stderr, "%s failed for SysV sempphore 0x%x: %s\n", __FUNCTION__, this->key, strerror(errno));
293
                return 0;
294
        }
295
296
        /* let release_sysvsem_sem know we have removed
297
         * the semaphore to avoid issues with releasing.
298
         */
299
300
        this->count = -1;
301
        return 1;
302
}
303
304
/* }}} */
305
306
/*
307
 * Local variables:
308
 * tab-width: 4
309
 * c-basic-offset: 4
310
 * End:
311
 * vim600: sw=4 ts=4 fdm=marker
312
 * vim<600: sw=4 ts=4
313
 */