OSDN Git Service

Darwin porting patches from Peter Bierman <bierman@apple.com>
[pg-rex/syncrep.git] / src / backend / port / darwin / sem.c
1 /*-------------------------------------------------------------------------
2  *
3  * sem.c
4  *        System V Semaphore Emulation
5  *
6  * Copyright (c) 1999, repas AEG Automation GmbH
7  * 
8  * 2000-12-1 pmb@mac.com 
9  *   - changed from anonymous to named semaphores for darwin
10  *   - this required changing sem_info from containig an array of sem_t to an array of sem_t*
11  *
12  * IDENTIFICATION
13  *       $Header: /cvsroot/pgsql/src/backend/port/darwin/Attic/sem.c,v 1.1 2000/12/11 00:49:54 tgl Exp $
14  *
15  *-------------------------------------------------------------------------
16  */
17
18 #include <errno.h>
19 #include <semaphore.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/mman.h>
25 #include "postgres.h"
26 #include "storage/ipc.h"
27 #include "storage/proc.h"
28 #include "port/darwin/sem.h"
29
30 #define SEMMAX  IPC_NMAXSEM
31 #define SETMAX ((MAXBACKENDS + SEMMAX - 1) / SEMMAX)
32 #define OPMAX   8
33
34 #define MODE    0700
35 #define SHM_INFO_NAME   "SysV_Sem_Info"
36 #define SEM_NAME       "/pgsql-darwin"
37
38 struct pending_ops
39 {
40         int                     op[OPMAX];              /* array of pending operations */
41         int                     idx;                    /* index of first free array member */
42 };
43
44 struct sem_info
45 {
46        sem_t*          sem;
47         struct
48         {
49                 key_t           key;
50                 int                     nsems;
51                sem_t*          sem[SEMMAX];/* array of POSIX semaphores */
52                 struct sem      semV[SEMMAX];           /* array of System V semaphore
53                                                                                  * structures */
54                 struct pending_ops pendingOps[SEMMAX];  /* array of pending
55                                                                                                  * operations */
56         }                       set[SETMAX];
57 };
58
59 static struct sem_info *SemInfo = (struct sem_info *) - 1;
60
61
62 int
63 semctl(int semid, int semnum, int cmd, /* ... */ union semun arg)
64 {
65         int                     r = 0;
66
67        sem_wait(SemInfo->sem);
68
69         if (semid < 0 || semid >= SETMAX ||
70                 semnum < 0 || semnum >= SemInfo->set[semid].nsems)
71         {
72                sem_post(SemInfo->sem);
73                 errno = EINVAL;
74                 return -1;
75         }
76
77         switch (cmd)
78         {
79                 case GETNCNT:
80                         r = SemInfo->set[semid].semV[semnum].semncnt;
81                         break;
82
83                 case GETPID:
84                         r = SemInfo->set[semid].semV[semnum].sempid;
85                         break;
86
87                 case GETVAL:
88                         r = SemInfo->set[semid].semV[semnum].semval;
89                         break;
90
91                 case GETALL:
92                         for (semnum = 0; semnum < SemInfo->set[semid].nsems; semnum++)
93                                 arg.array[semnum] = SemInfo->set[semid].semV[semnum].semval;
94                         break;
95
96                 case SETVAL:
97                         SemInfo->set[semid].semV[semnum].semval = arg.val;
98                         break;
99
100                 case SETALL:
101                         for (semnum = 0; semnum < SemInfo->set[semid].nsems; semnum++)
102                                 SemInfo->set[semid].semV[semnum].semval = arg.array[semnum];
103                         break;
104
105                 case GETZCNT:
106                         r = SemInfo->set[semid].semV[semnum].semzcnt;
107                         break;
108
109                 case IPC_RMID:
110                         for (semnum = 0; semnum < SemInfo->set[semid].nsems; semnum++)
111                         {
112                                if (sem_close(SemInfo->set[semid].sem[semnum]) == -1)
113                                         r = -1;
114                         }
115                         SemInfo->set[semid].key = -1;
116                         SemInfo->set[semid].nsems = 0;
117                         break;
118
119                 default:
120                        sem_post(SemInfo->sem);
121                         errno = EINVAL;
122                         return -1;
123         }
124
125        sem_post(SemInfo->sem);
126
127         return r;
128 }
129
130 int
131 semget(key_t key, int nsems, int semflg)
132 {
133         int                     fd,
134                                 semid,
135                                 semnum /* , semnum1 */ ;
136         int                     exist = 0;
137        char                    semname[64];
138
139         if (nsems < 0 || nsems > SEMMAX)
140         {
141 #ifdef DEBUG_IPC
142                fprintf(stderr, "darwin semget aborting because nsems out of range. (%d)\n", nsems);
143 #endif
144                 errno = EINVAL;
145                 return -1;
146         }
147
148         /* open and map shared memory */
149         if (SemInfo == (struct sem_info *) - 1)
150         {
151 #ifdef DEBUG_IPC
152                fprintf(stderr, "darwin initializing shared mem for semaphore shim.\n");
153 #endif
154                 /* test if the shared memory already exists */
155                 fd = shm_open(SHM_INFO_NAME, O_RDWR | O_CREAT | O_EXCL, MODE);
156                 if (fd == -1 && errno == EEXIST)
157                 {
158                         exist = 1;
159                         fd = shm_open(SHM_INFO_NAME, O_RDWR | O_CREAT, MODE);
160                 }
161                 if (fd == -1)
162                         return fd;
163                 /* The size may only be set once. Ignore errors. */
164                ftruncate(fd, sizeof(struct sem_info));
165                 SemInfo = mmap(NULL, sizeof(struct sem_info),
166                                            PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
167                 if (SemInfo == MAP_FAILED)
168                         return -1;
169                 if (!exist)
170                 {
171                         /* create semaphore for locking */
172                        sprintf(semname, "%s-map", SEM_NAME);
173 #ifdef DEBUG_IPC
174                        fprintf(stderr, "darwin creating sem %s to cover shared mem.\n", semname);
175 #endif
176                        SemInfo->sem = sem_open(semname, O_CREAT, semflg & 0777, 1);
177                        sem_wait(SemInfo->sem);
178                         /* initilize shared memory */
179                         memset(SemInfo->set, 0, sizeof(SemInfo->set));
180                         for (semid = 0; semid < SETMAX; semid++)
181                                 SemInfo->set[semid].key = -1;
182                        sem_post(SemInfo->sem);
183                 }
184         }
185
186        sem_wait(SemInfo->sem);
187
188         if (key != IPC_PRIVATE)
189         {
190                 /* search existing element */
191                 semid = 0;
192                 while (semid < SETMAX && SemInfo->set[semid].key != key)
193                         semid++;
194                 if (!(semflg & IPC_CREAT) && semid >= SETMAX)
195                 {
196                        sem_post(SemInfo->sem);
197                         errno = ENOENT;
198                         return -1;
199                 }
200                 else if (semid < SETMAX)
201                 {
202                         if (semflg & IPC_CREAT && semflg & IPC_EXCL)
203                         {
204                                sem_post(SemInfo->sem);
205                                 errno = EEXIST;
206                                 return -1;
207                         }
208                         else
209                         {
210                                 if (nsems != 0 && SemInfo->set[semid].nsems < nsems)
211                                 {
212 #ifdef DEBUG_IPC
213 fprintf(stderr, "darwin semget failed because if (nsems != 0 && SemInfo->set[semid].nsems < nsems) %d %d\n",
214        nsems, SemInfo->set[semid].nsems);
215 #endif
216                                        sem_post(SemInfo->sem);
217                                         errno = EINVAL;
218                                         return -1;
219                                 }
220                                sem_post(SemInfo->sem);
221                                 return semid;
222                         }
223                 }
224         }
225
226         /* search first free element */
227         semid = 0;
228         while (semid < SETMAX && SemInfo->set[semid].key != -1)
229                 semid++;
230         if (semid >= SETMAX)
231         {
232 #ifdef DEBUG_IPC
233                fprintf(stderr, "darwin semget failed because all keys were -1 up to SETMAX\n");
234 #endif
235                sem_post(SemInfo->sem);
236                 errno = ENOSPC;
237                 return -1;
238         }
239
240         for (semnum = 0; semnum < nsems; semnum++)
241         {
242                sprintf(semname, "%s-%d-%d", SEM_NAME, semid, semnum);
243 #ifdef DEBUG_IPC
244                fprintf(stderr, "darwin creating sem %s to cover set %d num %dm.\n", semname, semid, semnum);
245 #endif
246                SemInfo->set[semid].sem[semnum] = sem_open(semname, O_CREAT, semflg & 0777, 0);
247
248 /* Currently sem_init always returns -1.
249         if( sem_init( &SemInfo->set[semid].sem[semnum], 1, 0 ) == -1 )  {
250           for( semnum1 = 0; semnum1 < semnum; semnum1++ )  {
251                sem_close( SemInfo->set[semid].sem[semnum1] );
252           }
253          sem_post( SemInfo->sem );
254           return -1;
255         }
256 */
257         }
258
259         SemInfo->set[semid].key = key;
260         SemInfo->set[semid].nsems = nsems;
261
262        sem_post(SemInfo->sem);
263
264         return semid;
265 }
266
267 int
268 semop(int semid, struct sembuf * sops, size_t nsops)
269 {
270         int                     i,
271                                 r = 0,
272                                 r1,
273                                 errno1 = 0,
274                                 op;
275
276        sem_wait(SemInfo->sem);
277
278         if (semid < 0 || semid >= SETMAX)
279         {
280                sem_post(SemInfo->sem);
281                 errno = EINVAL;
282                 return -1;
283         }
284         for (i = 0; i < nsops; i++)
285         {
286                 if ( /* sops[i].sem_num < 0 || */ sops[i].sem_num >= SemInfo->set[semid].nsems)
287                 {
288                        sem_post(SemInfo->sem);
289                         errno = EFBIG;
290                         return -1;
291                 }
292         }
293
294         for (i = 0; i < nsops; i++)
295         {
296                 if (sops[i].sem_op < 0)
297                 {
298                         if (SemInfo->set[semid].semV[sops[i].sem_num].semval < -sops[i].sem_op)
299                         {
300                                 if (sops[i].sem_flg & IPC_NOWAIT)
301                                 {
302                                        sem_post(SemInfo->sem);
303                                         errno = EAGAIN;
304                                         return -1;
305                                 }
306                                 SemInfo->set[semid].semV[sops[i].sem_num].semncnt++;
307                                 if (SemInfo->set[semid].pendingOps[sops[i].sem_num].idx >= OPMAX)
308                                 {
309                                         /* pending operations array overflow */
310                                        sem_post(SemInfo->sem);
311                                         errno = ERANGE;
312                                         return -1;
313                                 }
314                                 SemInfo->set[semid].pendingOps[sops[i].sem_num].op[SemInfo->set[semid].pendingOps[sops[i].sem_num].idx++] = sops[i].sem_op;
315                                 /* suspend */
316                                sem_post(SemInfo->sem);         /* avoid deadlock */
317                                r1 = sem_wait(SemInfo->set[semid].sem[sops[i].sem_num]);
318                                sem_wait(SemInfo->sem);
319                                 if (r1)
320                                 {
321                                         errno1 = errno;
322                                         r = r1;
323                                         /* remove pending operation */
324                                         SemInfo->set[semid].pendingOps[sops[i].sem_num].op[--SemInfo->set[semid].pendingOps[sops[i].sem_num].idx] = 0;
325                                 }
326                                 else
327                                         SemInfo->set[semid].semV[sops[i].sem_num].semval -= -sops[i].sem_op;
328                                 SemInfo->set[semid].semV[sops[i].sem_num].semncnt--;
329                         }
330                         else
331                                 SemInfo->set[semid].semV[sops[i].sem_num].semval -= -sops[i].sem_op;
332                 }
333                 else if (sops[i].sem_op > 0)
334                 {
335                         SemInfo->set[semid].semV[sops[i].sem_num].semval += sops[i].sem_op;
336                         op = sops[i].sem_op;
337                         while (op > 0 && SemInfo->set[semid].pendingOps[sops[i].sem_num].idx > 0)
338                         {                                       /* operations pending */
339                                 if (SemInfo->set[semid].pendingOps[sops[i].sem_num].op[SemInfo->set[semid].pendingOps[sops[i].sem_num].idx - 1] + op >= 0)
340                                 {
341                                         /* unsuspend processes */
342                                        if (sem_post(SemInfo->set[semid].sem[sops[i].sem_num]))
343                                         {
344                                                 errno1 = errno;
345                                                 r = -1;
346                                         }
347                                         /* adjust pending operations */
348                                         op += SemInfo->set[semid].pendingOps[sops[i].sem_num].op[--SemInfo->set[semid].pendingOps[sops[i].sem_num].idx];
349                                         SemInfo->set[semid].pendingOps[sops[i].sem_num].op[SemInfo->set[semid].pendingOps[sops[i].sem_num].idx] = 0;
350                                 }
351                                 else
352                                 {
353                                         /* adjust pending operations */
354                                         SemInfo->set[semid].pendingOps[sops[i].sem_num].op[SemInfo->set[semid].pendingOps[sops[i].sem_num].idx - 1] += op;
355                                         op = 0;
356                                 }
357                         }
358                 }
359                 else
360                         /* sops[i].sem_op == 0 */
361                 {
362                         /* not supported */
363                        sem_post(SemInfo->sem);
364                         errno = ENOSYS;
365                         return -1;
366                 }
367                 SemInfo->set[semid].semV[sops[i].sem_num].sempid = getpid();
368         }
369
370        sem_post(SemInfo->sem);
371
372         errno = errno1;
373         return r;
374 }