1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2012 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                 Eclipse Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*          http://www.eclipse.org/org/documents/epl-v10.html           *
11*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                   Phong Vo <kpv@research.att.com>                    *
20*                                                                      *
21***********************************************************************/
22#pragma prototyped
23
24#include "asohdr.h"
25
26#if defined(_UWIN) && defined(_BLD_ast) || !_aso_semaphore
27
28NoN(aso_meth_semaphore)
29
30#else
31
32#include <sys/stat.h>
33#include <sys/ipc.h>
34#include <sys/sem.h>
35
36#define SPIN		1000000
37
38typedef union Semun_u
39{
40	int			val;
41	struct semid_ds*	ds;
42	unsigned short*		array;
43} Semun_t;
44
45typedef struct APL_s
46{
47	int		id;
48	size_t		size;
49} APL_t;
50
51static void*
52aso_init_semaphore(void* data, const char* details)
53{
54	APL_t*		apl = (APL_t*)data;
55	char*		path;
56	char*		opt;
57	size_t		size;
58	size_t		n;
59	int		key;
60	int		id;
61	int		perm;
62	struct sembuf	sem;
63	char		tmp[64];
64
65	if (apl)
66	{
67		/*
68		 * semaphore 0 is the reference count
69		 * the id is dropped on last reference
70		 */
71
72		sem.sem_num = 0;
73		sem.sem_op = -1;
74		sem.sem_flg = IPC_NOWAIT;
75		semop(apl->id, &sem, 1);
76		sem.sem_op = 0;
77		if (!semop(apl->id, &sem, 1))
78			semctl(apl->id, 0, IPC_RMID);
79		free(apl);
80		return 0;
81	}
82	perm = S_IRUSR|S_IWUSR;
83	size = 128;
84	if (path = (char*)details)
85		while (opt = strchr(path, ','))
86		{
87			if (strneq(path, "perm=", 5))
88			{
89				if ((n = opt - (path + 5)) >= sizeof(tmp))
90					n = sizeof(tmp) - 1;
91				memcpy(tmp, path + 5, n);
92				tmp[n] = 0;
93				perm = strperm(tmp, NiL, perm);
94			}
95			else if (strneq(path, "size=", 5))
96			{
97				size = strtoul(path + 5, NiL, 0);
98				if (size <= 1)
99					return 0;
100			}
101			path = opt + 1;
102		}
103	key = (!path || !*path || streq(path, "private")) ? IPC_PRIVATE : (strsum(path, 0) & 0x7fff);
104	for (;;)
105	{
106		if ((id = semget(key, size, IPC_CREAT|IPC_EXCL|perm)) >= 0)
107		{
108			/*
109			 * initialize all semaphores to 0
110			 * this also sets the semaphore 0 ref count
111			 */
112
113			sem.sem_op = 1;
114			sem.sem_flg = 0;
115			for (sem.sem_num = 0; sem.sem_num < size; sem.sem_num++)
116				if (semop(id, &sem, 1) < 0)
117				{
118					(void)semctl(id, 0, IPC_RMID);
119					return 0;
120				}
121			break;
122		}
123		else if (errno == EINVAL && size > 3)
124			size /= 2;
125		else if (errno != EEXIST)
126			return 0;
127		else if ((id = semget(key, size, perm)) >= 0)
128		{
129			struct semid_ds	ds;
130			Semun_t		arg;
131			unsigned int	k;
132
133			/*
134			 * make sure all semaphores have been activated
135			 */
136
137			arg.ds = &ds;
138			for (k = 0; k < SPIN; ASOLOOP(k))
139			{
140				if (semctl(id, size-1, IPC_STAT, arg) < 0)
141					return 0;
142				if (ds.sem_otime)
143					break;
144			}
145			if (k > SPIN)
146				return 0;
147
148			/*
149			 * bump the ref count
150			 */
151
152			sem.sem_num = 0;
153			sem.sem_op = 1;
154			sem.sem_flg = 0;
155			if (semop(id, &sem, 1) < 0)
156				return 0;
157			break;
158		}
159		else if (errno == EINVAL && size > 3)
160			size /= 2;
161		else
162			return 0;
163	}
164	if (!(apl = newof(0, APL_t, 1, 0)))
165		return 0;
166	apl->id = id;
167	apl->size = size - 1;
168	return apl;
169}
170
171static ssize_t
172aso_lock_semaphore(void* data, ssize_t k, void volatile* p)
173{
174	APL_t*		apl = (APL_t*)data;
175	struct sembuf	sem;
176
177	if (!apl)
178		return -1;
179	if (k > 0)
180		sem.sem_op = 1;
181	else
182	{
183		sem.sem_op = -1;
184		k = HASH(p, apl->size) + 1;
185	}
186	sem.sem_num = k;
187	sem.sem_flg = 0;
188	return semop(apl->id, &sem, 1) < 0 ? -1 : k;
189}
190
191Asometh_t	_aso_meth_semaphore = { "semaphore", ASO_PROCESS|ASO_THREAD, aso_init_semaphore, aso_lock_semaphore };
192
193#endif
194