1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
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 { 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
359 done:
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