1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2011 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 #include	"sfhdr.h"
23 
24 /*	Management of pools of streams.
25 **	If pf is not nil, f is pooled with pf and f becomes current;
26 **	otherwise, f is isolated from its pool. flag can be one of
27 **	0 or SF_SHARE.
28 **
29 **	Written by Kiem-Phong Vo.
30 */
31 
32 /* Note that we do not free the space for a pool once it is allocated.
33 ** This is to prevent memory faults in calls such as sfsync(NULL) that walk the pool
34 ** link list and during such walks may free up streams&pools. Free pools will be
35 ** reused in newpool().
36 */
37 #if __STD_C
delpool(reg Sfpool_t * p)38 static int delpool(reg Sfpool_t* p)
39 #else
40 static int delpool(p)
41 reg Sfpool_t*	p;
42 #endif
43 {
44 	POOLMTXENTER(p);
45 
46 	if(p->s_sf && p->sf != p->array)
47 		free((Void_t*)p->sf);
48 	p->mode = SF_AVAIL;
49 
50 	POOLMTXRETURN(p,0);
51 }
52 
53 #if __STD_C
newpool(reg int mode)54 static Sfpool_t* newpool(reg int mode)
55 #else
56 static Sfpool_t* newpool(mode)
57 reg int	mode;
58 #endif
59 {
60 	reg Sfpool_t	*p, *last = &_Sfpool;
61 
62 	/* look to see if there is a free pool */
63 	for(last = &_Sfpool, p = last->next; p; last = p, p = p->next)
64 	{	if(p->mode == SF_AVAIL )
65 		{	p->mode = 0;
66 			break;
67 		}
68 	}
69 
70 	if(!p)
71 	{	POOLMTXLOCK(last);
72 
73 		if(!(p = (Sfpool_t*) malloc(sizeof(Sfpool_t))) )
74 		{	POOLMTXUNLOCK(last);
75 			return NIL(Sfpool_t*);
76 		}
77 
78 		(void)vtmtxopen(&p->mutex, VT_INIT); /* initialize mutex */
79 
80 		p->mode = 0;
81 		p->n_sf = 0;
82 		p->next = NIL(Sfpool_t*);
83 		last->next = p;
84 
85 		POOLMTXUNLOCK(last);
86 	}
87 
88 	POOLMTXENTER(p);
89 
90 	p->mode = mode&SF_SHARE;
91 	p->s_sf = sizeof(p->array)/sizeof(p->array[0]);
92 	p->sf = p->array;
93 
94 	POOLMTXRETURN(p,p);
95 }
96 
97 /* move a stream to head */
98 #if __STD_C
_sfphead(Sfpool_t * p,Sfio_t * f,int n)99 static int _sfphead(Sfpool_t* p, Sfio_t* f, int n)
100 #else
101 static int _sfphead(p, f, n)
102 Sfpool_t*	p;	/* the pool			*/
103 Sfio_t*		f;	/* the stream			*/
104 int		n;	/* current position in pool	*/
105 #endif
106 {
107 	reg Sfio_t*	head;
108 	reg ssize_t	k, w, v;
109 	reg int		rv;
110 
111 	POOLMTXENTER(p);
112 
113 	if(n == 0)
114 		POOLMTXRETURN(p,0);
115 
116 	head = p->sf[0];
117 	if(SFFROZEN(head) )
118 		POOLMTXRETURN(p,-1);
119 
120 	SFLOCK(head,0);
121 	rv = -1;
122 
123 	if(!(p->mode&SF_SHARE) || (head->mode&SF_READ) || (f->mode&SF_READ) )
124 	{	if(SFSYNC(head) < 0)
125 			goto done;
126 	}
127 	else	/* shared pool of write-streams, data can be moved among streams */
128 	{	if(SFMODE(head,1) != SF_WRITE && _sfmode(head,SF_WRITE,1) < 0)
129 			goto done;
130 		/**/ASSERT(f->next == f->data);
131 
132 		v = head->next - head->data;	/* pending data		*/
133 		if((k = v - (f->endb-f->data)) <= 0)
134 			k = 0;
135 		else	/* try to write out amount exceeding f's capacity */
136 		{	if((w = SFWR(head,head->data,k,head->disc)) == k)
137 				v -= k;
138 			else	/* write failed, recover buffer then quit */
139 			{	if(w > 0)
140 				{	v -= w;
141 					memcpy(head->data,(head->data+w),v);
142 				}
143 				head->next = head->data+v;
144 				goto done;
145 			}
146 		}
147 
148 		/* move data from head to f */
149 		if((head->data+k) != f->data )
150 			memcpy(f->data,(head->data+k),v);
151 		f->next = f->data+v;
152 	}
153 
154 	f->mode &= ~SF_POOL;
155 	head->mode |= SF_POOL;
156 	head->next = head->endr = head->endw = head->data; /* clear write buffer */
157 
158 	p->sf[n] = head;
159 	p->sf[0] = f;
160 	rv = 0;
161 
162 done:
163 	head->mode &= ~SF_LOCK; /* partially unlock because it's no longer head */
164 
165 	POOLMTXRETURN(p,rv);
166 }
167 
168 /* delete a stream from its pool */
169 #if __STD_C
_sfpdelete(Sfpool_t * p,Sfio_t * f,int n)170 static int _sfpdelete(Sfpool_t* p, Sfio_t* f, int n)
171 #else
172 static int _sfpdelete(p, f, n)
173 Sfpool_t*	p;	/* the pool		*/
174 Sfio_t*		f;	/* the stream		*/
175 int		n;	/* position in pool	*/
176 #endif
177 {
178 	POOLMTXENTER(p);
179 
180 	p->n_sf -= 1;
181 	for(; n < p->n_sf; ++n)
182 		p->sf[n] = p->sf[n+1];
183 
184 	f->pool = NIL(Sfpool_t*);
185 	f->mode &= ~SF_POOL;
186 
187 	if(p->n_sf == 0 || p == &_Sfpool)
188 	{	if(p != &_Sfpool)
189 			delpool(p);
190 		goto done;
191 	}
192 
193 	/* !_Sfpool, make sure head stream is an open stream */
194 	for(n = 0; n < p->n_sf; ++n)
195 		if(!SFFROZEN(p->sf[n]))
196 			break;
197 	if(n < p->n_sf && n > 0)
198 	{	f = p->sf[n];
199 		p->sf[n] = p->sf[0];
200 		p->sf[0] = f;
201 	}
202 
203 	/* head stream has SF_POOL off */
204 	f = p->sf[0];
205 	f->mode &= ~SF_POOL;
206 	if(!SFFROZEN(f))
207 		_SFOPEN(f);
208 
209 	/* if only one stream left, delete pool */
210 	if(p->n_sf == 1 )
211 	{	_sfpdelete(p,f,0);
212 		_sfsetpool(f);
213 	}
214 
215 done:
216 	POOLMTXRETURN(p,0);
217 }
218 
219 #if __STD_C
_sfpmove(reg Sfio_t * f,reg int type)220 static int _sfpmove(reg Sfio_t* f, reg int type)
221 #else
222 static int _sfpmove(f,type)
223 reg Sfio_t*	f;
224 reg int		type;	/* <0 : deleting, 0: move-to-front, >0: inserting */
225 #endif
226 {
227 	reg Sfpool_t*	p;
228 	reg int		n;
229 
230 	if(type > 0)
231 		return _sfsetpool(f);
232 	else
233 	{	if(!(p = f->pool) )
234 			return -1;
235 		for(n = p->n_sf-1; n >= 0; --n)
236 			if(p->sf[n] == f)
237 				break;
238 		if(n < 0)
239 			return -1;
240 
241 		return type == 0 ? _sfphead(p,f,n) : _sfpdelete(p,f,n);
242 	}
243 }
244 
245 #if __STD_C
sfpool(reg Sfio_t * f,reg Sfio_t * pf,reg int mode)246 Sfio_t* sfpool(reg Sfio_t* f, reg Sfio_t* pf, reg int mode)
247 #else
248 Sfio_t* sfpool(f,pf,mode)
249 reg Sfio_t*	f;
250 reg Sfio_t*	pf;
251 reg int		mode;
252 #endif
253 {
254 	int		k;
255 	Sfpool_t*	p;
256 	Sfio_t*		rv;
257 
258 	_Sfpmove = _sfpmove;
259 
260 	if(!f)	/* return head of pool of pf regardless of lock states */
261 	{	if(!pf)
262 			return NIL(Sfio_t*);
263 		else if(!pf->pool || pf->pool == &_Sfpool)
264 			return pf;
265 		else	return pf->pool->sf[0];
266 	}
267 
268 	if(f)	/* check for permissions */
269 	{	SFMTXLOCK(f);
270 		if((f->mode&SF_RDWR) != f->mode && _sfmode(f,0,0) < 0)
271 		{	SFMTXUNLOCK(f);
272 			return NIL(Sfio_t*);
273 		}
274 		if(f->disc == _Sfudisc)
275 			(void)sfclose((*_Sfstack)(f,NIL(Sfio_t*)));
276 	}
277 	if(pf)
278 	{	SFMTXLOCK(pf);
279 		if((pf->mode&SF_RDWR) != pf->mode && _sfmode(pf,0,0) < 0)
280 		{	if(f)
281 				SFMTXUNLOCK(f);
282 			SFMTXUNLOCK(pf);
283 			return NIL(Sfio_t*);
284 		}
285 		if(pf->disc == _Sfudisc)
286 			(void)sfclose((*_Sfstack)(pf,NIL(Sfio_t*)));
287 	}
288 
289 	/* f already in the same pool with pf */
290 	if(f == pf || (pf && f->pool == pf->pool && f->pool != &_Sfpool) )
291 	{
292 		if(f)
293 			SFMTXUNLOCK(f);
294 		if(pf)
295 			SFMTXUNLOCK(pf);
296 		return pf;
297 	}
298 
299 	/* lock streams before internal manipulations */
300 	rv = NIL(Sfio_t*);
301 	SFLOCK(f,0);
302 	if(pf)
303 		SFLOCK(pf,0);
304 
305 	if(!pf)	/* deleting f from its current pool */
306 	{	if((p = f->pool) != NIL(Sfpool_t*) && p != &_Sfpool)
307 			for(k = 0; k < p->n_sf && pf == NIL(Sfio_t*); ++k)
308 				if(p->sf[k] != f) /* a stream != f represents the pool */
309 					pf = p->sf[k];
310 		if(!pf) /* already isolated */
311 		{	rv = f; /* just return self */
312 			goto done;
313 		}
314 
315 		if(_sfpmove(f,-1) < 0 || _sfsetpool(f) < 0)
316 			goto done; /* can't delete */
317 
318 		if(!pf->pool || pf->pool == &_Sfpool || pf->pool->n_sf <= 0 )
319 			rv = pf;
320 		else	rv = pf->pool->sf[0];	/* return head of old pool */
321 		goto done;
322 	}
323 
324 	if(pf->pool && pf->pool != &_Sfpool) /* always use current mode */
325 		mode = pf->pool->mode;
326 
327 	if(mode&SF_SHARE) /* can only have write streams */
328 	{	if(SFMODE(f,1) != SF_WRITE && _sfmode(f,SF_WRITE,1) < 0)
329 			goto done;
330 		if(SFMODE(pf,1) != SF_WRITE && _sfmode(pf,SF_WRITE,1) < 0)
331 			goto done;
332 		if(f->next > f->data && SFSYNC(f) < 0) /* start f clean */
333 			goto done;
334 	}
335 
336 	if(_sfpmove(f,-1) < 0)	/* isolate f from current pool */
337 		goto done;
338 
339 	if(!(p = pf->pool) || p == &_Sfpool) /* making a new pool */
340 	{	if(!(p = newpool(mode)) )
341 			goto done;
342 		if(_sfpmove(pf,-1) < 0) /* isolate pf from its current pool */
343 			goto done;
344 		pf->pool = p;
345 		p->sf[0] = pf;
346 		p->n_sf += 1;
347 	}
348 
349 	f->pool = p;	/* add f to pf's pool */
350 	if(_sfsetpool(f) < 0)
351 		goto done;
352 
353 	/**/ASSERT(p->sf[0] == pf && p->sf[p->n_sf-1] == f);
354 	SFOPEN(pf,0);
355 	SFOPEN(f,0);
356 	if(_sfpmove(f,0) < 0) /* make f head of pool */
357 		goto done;
358 	rv = pf;
359 
360 done:
361 	if(f)
362 	{	SFOPEN(f,0);
363 		SFMTXUNLOCK(f);
364 	}
365 	if(pf)
366 	{	SFOPEN(pf,0);
367 		SFMTXUNLOCK(pf);
368 	}
369 	return rv;
370 }
371