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 
28 NoN(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 
38 typedef union Semun_u
39 {
40 	int			val;
41 	struct semid_ds*	ds;
42 	unsigned short*		array;
43 } Semun_t;
44 
45 typedef struct APL_s
46 {
47 	int		id;
48 	size_t		size;
49 } APL_t;
50 
51 static void*
52 aso_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 
171 static ssize_t
172 aso_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 
191 Asometh_t	_aso_meth_semaphore = { "semaphore", ASO_PROCESS|ASO_THREAD, aso_init_semaphore, aso_lock_semaphore };
192 
193 #endif
194