-
Notifications
You must be signed in to change notification settings - Fork 1
/
mcs.c
327 lines (281 loc) · 10.2 KB
/
mcs.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#ifdef RCSID
static char RCSid[] =
"$Header: d:/cvsroot/tads/TADS2/MCS.C,v 1.2 1999/05/17 02:52:12 MJRoberts Exp $";
#endif
/*
* Copyright (c) 1991, 2002 Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
mcs.c - memory cache swapping manager
Function
Implements swapping functions for cache manager
Notes
None
Modified
08/04/91 MJRoberts - creation
*/
#include <assert.h>
#include <stdio.h>
#include "os.h"
#include "std.h"
#include "err.h"
#include "mcs.h"
#include "mch.h"
#include "mcm.h"
/* initialize swapper: allocate memory for swap page table */
void mcsini(mcscxdef *ctx, mcmcx1def *gmemctx, ulong maxsiz,
osfildef *fp, char *swapfilename, errcxdef *errctx)
{
uchar *p;
ctx->mcscxtab = (mcsdsdef **)0; /* anticipate failure */
/* allocate space from the low-level heap for page table and one page */
p = mchalo(errctx, ((MCSPAGETAB * sizeof(mcsdsdef *))
+ (MCSPAGECNT * sizeof(mcsdsdef))), "mcsini");
/* set up the context with pointers to this chunk */
ctx->mcscxtab = (mcsdsdef **)p;
memset(p, 0, (size_t)(MCSPAGETAB * sizeof(mcsdsdef *)));
p += MCSPAGETAB * sizeof(mcsdsdef *);
ctx->mcscxtab[0] = (mcsdsdef *)p;
/* set up the rest of the context */
ctx->mcscxtop = (ulong)0;
ctx->mcscxmax = maxsiz;
ctx->mcscxmsg = 0;
ctx->mcscxfp = fp;
ctx->mcscxerr = errctx;
ctx->mcscxmem = gmemctx;
/*
* store the swap filename - make a copy so that the caller doesn't
* have to retain the original copy (in case it's on the stack)
*/
if (swapfilename != 0)
{
ctx->mcscxfname = (char *)mchalo(errctx,
(strlen(swapfilename)+1),
"mcsini");
strcpy(ctx->mcscxfname, swapfilename);
}
else
ctx->mcscxfname = 0;
}
/* close the swapper */
void mcsclose(mcscxdef *ctx)
{
if (ctx->mcscxtab) mchfre(ctx->mcscxtab);
}
/*
* Attempt to compact the swap file when it grows too big. The segment
* descriptors are always allocated in increasing seek location within
* the swap file. To compress the file, make each descriptor's
* allocated size equal its used size for each in-use segment, and leave
* free segments at their allocated sizes.
*/
static void mcscompact(mcscxdef *ctx)
{
char buf[512];
ulong max;
mcsseg cur_in;
mcsseg cur_out;
mcsdsdef *desc_in;
mcsdsdef *desc_out;
uint siz;
uint rdsiz;
ulong ptr_in;
ulong ptr_out;
max = 0; /* start at offset zero within file */
for (cur_in = cur_out = 0 ; cur_in < ctx->mcscxmsg ; ++cur_in)
{
desc_in = mcsdsc(ctx, cur_in);
/*
* If the present descriptor's address is wrong, and the swap
* segment is in use, move the swap segment. If it's not in
* use, we don't need to move it, because we're going to throw
* away the segment entirely.
*/
if (desc_in->mcsdsptr != max
&& (desc_in->mcsdsflg & MCSDSFINUSE))
{
/* ptr_in is the old location, ptr_out is the new location */
ptr_in = desc_in->mcsdsptr;
ptr_out = max;
/* copy through our buffer */
for (siz = desc_in->mcsdsosz ; siz ; siz -= rdsiz)
{
/* size is whole buffer, or last piece if smaller */
rdsiz = (siz > sizeof(buf) ? sizeof(buf) : siz);
/* seek to old location and get the piece */
osfseek(ctx->mcscxfp, ptr_in, OSFSK_SET);
(void)osfrb(ctx->mcscxfp, buf, (size_t)rdsiz);
/* seek to new location and write the piece */
osfseek(ctx->mcscxfp, ptr_out, OSFSK_SET);
(void)osfwb(ctx->mcscxfp, buf, (size_t)rdsiz);
/* adjust the pointers by the size copied */
ptr_in += rdsiz;
ptr_out += rdsiz;
}
}
/* adjust object descriptor to reflect new location */
desc_in->mcsdsptr = max;
/*
* Make current object's size exact if it's in use. If it's
* not in use, delete the segment altogether.
*/
if (desc_in->mcsdsflg & MCSDSFINUSE)
{
desc_in->mcsdssiz = desc_in->mcsdsosz;
max += desc_in->mcsdssiz;
/* copy descriptor to correct position to close any holes */
if (cur_out != cur_in)
{
desc_out = mcsdsc(ctx, cur_out);
OSCPYSTRUCT(*desc_out, *desc_in);
/* we need to renumber the corresponding object as well */
mcmcsw(ctx->mcscxmem, (mcmon)desc_in->mcsdsobj,
cur_out, cur_in);
}
/* we actually wrote this one, so move output pointer */
++cur_out;
}
else
{
/*
* We need to renumber the corresponding object so that it
* knows there is no swap segment for it any more.
*/
mcmcsw(ctx->mcscxmem, (mcmon)desc_in->mcsdsobj,
MCSSEGINV, cur_in);
}
}
/*
* Adjust the top of the file for our new size, and add the savings
* into the available space counter. Also, adjust the total handle
* count to reflect any descriptors that we've deleted.
*/
ctx->mcscxmax += (ctx->mcscxtop - max);
ctx->mcscxtop = max;
ctx->mcscxmsg = cur_out;
}
/* swap an object out to the swap file */
mcsseg mcsout(mcscxdef *ctx, uint objid, uchar *ptr, ushort siz,
mcsseg oldseg, int dirty)
{
mcsdsdef *desc;
mcsdsdef **pagep;
uint i;
uint j;
mcsseg min;
mcsseg cur;
ushort minsiz;
IF_DEBUG(printf("<< mcsout: objid=%d, ptr=%lx, siz=%u, oldseg=%u >>\n",
objid, (unsigned long)ptr, siz, oldseg));
/* see if old segment can be reused */
if (oldseg != MCSSEGINV)
{
desc = mcsdsc(ctx, oldseg);
if (!(desc->mcsdsflg & MCSDSFINUSE) /* if old seg is not in use */
&& desc->mcsdsobj == objid /* and it has same object */
&& desc->mcsdssiz >= siz /* and it's still big enough */
&& !dirty) /* and the object in memory hasn't been changed */
{
/* we can reuse the old segment without rewriting it */
desc->mcsdsflg |= MCSDSFINUSE; /* mark segment as in use */
return(oldseg);
}
}
/* look for the smallest unused segment big enough for this object */
for (cur = 0, min = MCSSEGINV, i = 0, pagep = ctx->mcscxtab
; cur < ctx->mcscxmsg && i < MCSPAGETAB && *pagep ; ++pagep, ++i)
{
for (j = 0, desc = *pagep ; cur < ctx->mcscxmsg && j < MCSPAGECNT
; ++desc, ++j, ++cur)
{
if (!(desc->mcsdsflg & MCSDSFINUSE)
&& desc->mcsdssiz >= siz
&& (min == MCSSEGINV || desc->mcsdssiz < minsiz))
{
min = cur;
minsiz = desc->mcsdssiz;
if (minsiz == siz) break; /* exact match - we're done */
}
}
/* quit if we found an exact match */
if (min != MCSSEGINV && minsiz == siz) break;
}
/* if we found nothing, allocate a new segment if possible */
if (min == MCSSEGINV)
{
if (siz > ctx->mcscxmax)
{
/* swap file is too big; compact it and try again */
mcscompact(ctx);
if (siz > ctx->mcscxmax)
errsig(ctx->mcscxerr, ERR_SWAPBIG);
}
min = ctx->mcscxmsg;
if ((min >> 8) >= MCSPAGETAB) /* exceeded pages in page table? */
errsig(ctx->mcscxerr, ERR_SWAPPG);
if (!ctx->mcscxtab[min >> 8]) /* haven't allocate page yet? */
{
ctx->mcscxtab[min >> 8] =
(mcsdsdef *)mchalo(ctx->mcscxerr,
(MCSPAGECNT * sizeof(mcsdsdef)),
"mcsout");
}
/* set up new descriptor */
desc = mcsdsc(ctx, min);
desc->mcsdsptr = ctx->mcscxtop;
desc->mcsdssiz = siz;
desc->mcsdsobj = objid;
/* write out the segment */
mcswrt(ctx, desc, ptr, siz);
desc->mcsdsflg = MCSDSFINUSE;
/* update context information to account for new segment */
ctx->mcscxtop += siz; /* add to top seek offset in file */
ctx->mcscxmax -= siz; /* take size out of quota */
ctx->mcscxmsg++; /* increment last segment allocated */
return(min);
}
else
{
desc = mcsdsc(ctx, min);
desc->mcsdsobj = objid;
mcswrt(ctx, desc, ptr, siz);
desc->mcsdsflg |= MCSDSFINUSE;
return(min);
}
}
void mcsin(mcscxdef *ctx, mcsseg seg, uchar *ptr, ushort siz)
{
mcsdsdef *desc = mcsdsc(ctx, seg);
IF_DEBUG(printf("<< mcsin: seg=%u, ptr=%lx, siz=%d, objid=%u >>\n",
seg, (unsigned long)ptr, siz, desc->mcsdsobj));
assert(seg < ctx->mcscxmsg);
/* can only swap in as much as we wrote */
if (desc->mcsdsosz < siz) siz = desc->mcsdsosz;
/* seek to and read the segment */
if (osfseek(ctx->mcscxfp, desc->mcsdsptr, OSFSK_SET))
errsig(ctx->mcscxerr, ERR_FSEEK);
if (osfrb(ctx->mcscxfp, ptr, (size_t)siz))
errsig(ctx->mcscxerr, ERR_FREAD);
desc->mcsdsflg &= ~MCSDSFINUSE; /* segment no longer in use */
}
void mcswrt(mcscxdef *ctx, mcsdsdef *desc, uchar *buf, ushort bufl)
{
int tries;
desc->mcsdsosz = bufl;
for (tries = 0 ; tries < 2 ; ++tries)
{
/* attempt to write the object to the swap file */
if (osfseek(ctx->mcscxfp, desc->mcsdsptr, OSFSK_SET))
errsig(ctx->mcscxerr, ERR_FSEEK);
if (!osfwb(ctx->mcscxfp, buf, (size_t)bufl))
return;
/* couldn't write it; compact the swap file */
mcscompact(ctx);
}
/* couldn't write to swap file, even after compacting it */
errsig(ctx->mcscxerr, ERR_FWRITE);
}