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
38static int delpool(reg Sfpool_t* p)
39#else
40static int delpool(p)
41reg 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
54static Sfpool_t* newpool(reg int mode)
55#else
56static Sfpool_t* newpool(mode)
57reg 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
99static int _sfphead(Sfpool_t* p, Sfio_t* f, int n)
100#else
101static int _sfphead(p, f, n)
102Sfpool_t*	p;	/* the pool			*/
103Sfio_t*		f;	/* the stream			*/
104int		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
162done:
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
170static int _sfpdelete(Sfpool_t* p, Sfio_t* f, int n)
171#else
172static int _sfpdelete(p, f, n)
173Sfpool_t*	p;	/* the pool		*/
174Sfio_t*		f;	/* the stream		*/
175int		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
215done:
216	POOLMTXRETURN(p,0);
217}
218
219#if __STD_C
220static int _sfpmove(reg Sfio_t* f, reg int type)
221#else
222static int _sfpmove(f,type)
223reg Sfio_t*	f;
224reg 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
246Sfio_t* sfpool(reg Sfio_t* f, reg Sfio_t* pf, reg int mode)
247#else
248Sfio_t* sfpool(f,pf,mode)
249reg Sfio_t*	f;
250reg Sfio_t*	pf;
251reg 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	{	if(f)
292			SFMTXUNLOCK(f);
293		if(pf)
294			SFMTXUNLOCK(pf);
295		return pf;
296	}
297
298	/* lock streams before internal manipulations */
299	rv = NIL(Sfio_t*);
300	SFLOCK(f,0);
301	if(pf)
302		SFLOCK(pf,0);
303
304	if(!pf)	/* deleting f from its current pool */
305	{	if((p = f->pool) != NIL(Sfpool_t*) && p != &_Sfpool)
306			for(k = 0; k < p->n_sf && pf == NIL(Sfio_t*); ++k)
307				if(p->sf[k] != f) /* a stream != f represents the pool */
308					pf = p->sf[k];
309		if(!pf) /* already isolated */
310		{	rv = f; /* just return self */
311			goto done;
312		}
313
314		if(_sfpmove(f,-1) < 0 || _sfsetpool(f) < 0)
315			goto done; /* can't delete */
316
317		if(!pf->pool || pf->pool == &_Sfpool || pf->pool->n_sf <= 0 )
318			rv = pf;
319		else	rv = pf->pool->sf[0];	/* return head of old pool */
320		goto done;
321	}
322
323	if(pf->pool && pf->pool != &_Sfpool) /* always use current mode */
324		mode = pf->pool->mode;
325
326	if(mode&SF_SHARE) /* can only have write streams */
327	{	if(SFMODE(f,1) != SF_WRITE && _sfmode(f,SF_WRITE,1) < 0)
328			goto done;
329		if(SFMODE(pf,1) != SF_WRITE && _sfmode(pf,SF_WRITE,1) < 0)
330			goto done;
331		if(f->next > f->data && SFSYNC(f) < 0) /* start f clean */
332			goto done;
333	}
334
335	if(_sfpmove(f,-1) < 0)	/* isolate f from current pool */
336		goto done;
337
338	if(!(p = pf->pool) || p == &_Sfpool) /* making a new pool */
339	{	if(!(p = newpool(mode)) )
340			goto done;
341		if(_sfpmove(pf,-1) < 0) /* isolate pf from its current pool */
342			goto done;
343		pf->pool = p;
344		p->sf[0] = pf;
345		p->n_sf += 1;
346	}
347
348	f->pool = p;	/* add f to pf's pool */
349	if(_sfsetpool(f) < 0)
350		goto done;
351
352	/**/ASSERT(p->sf[0] == pf && p->sf[p->n_sf-1] == f);
353	SFOPEN(pf,0);
354	SFOPEN(f,0);
355	if(_sfpmove(f,0) < 0) /* make f head of pool */
356		goto done;
357	rv = pf;
358
359done:
360	if(f)
361	{	SFOPEN(f,0);
362		SFMTXUNLOCK(f);
363	}
364	if(pf)
365	{	SFOPEN(pf,0);
366		SFMTXUNLOCK(pf);
367	}
368	return rv;
369}
370