source: trunk/libc/src/kNIX.os2/heaphigh.c@ 2726

Last change on this file since 2726 was 2254, checked in by bird, 20 years ago

o LIBC_ASSERT*() are for internal libc errors, LIBCLOG_ERROR*() are

for user related error. Big code adjustements.

o Fixed a few smaller issues.
o Started fixing exec() backend.

  • Property cvs2svn:cvs-rev set to 1.4
  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 17.8 KB
Line 
1/* $Id: heaphigh.c 2254 2005-07-17 12:25:44Z bird $ */
2/** @file
3 *
4 * LIBC Base Functions for high memory heap.
5 * Note. High memory heap does not mess around with sbrk().
6 *
7 * Copyright (c) 2003 knut st. osmundsen <bird-srcspam@anduin.net>
8 *
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with This program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26
27/*******************************************************************************
28* Defined Constants And Macros *
29*******************************************************************************/
30/** The size of a chunk descriptor pool (64KB). */
31#define HIMEM_POOL_SIZE (64*1024)
32/** Default chunk size (16MB). */
33#define HIMEM_CHUNK_SIZE (16*1024*1024)
34/** Minimum chunk size (64KB). */
35#define HIMEM_CHUNK_SIZE_MIN (64*1024)
36/** Default commit size (256KB). */
37#define HIMEM_COMMIT_SIZE HIMEM_CHUNK_SIZE_MIN
38
39/** Round chunk size. */
40#define HIMEM_ROUND_SIZE(cb, align) ( ((cb) + (align) - 1) & ~((align) - 1) )
41
42
43
44/*******************************************************************************
45* Header Files *
46*******************************************************************************/
47#include "libc-alias.h"
48#define INCL_DOSMEMMGR
49#define INCL_EXAPIS
50#define INCL_ERRORS
51#include <os2emx.h>
52#include <string.h>
53#include <emx/umalloc.h>
54#include "syscalls.h"
55#define __LIBC_LOG_GROUP __LIBC_LOG_GRP_HEAP
56#include <InnoTekLIBC/logstrict.h>
57
58
59/*******************************************************************************
60* Structures and Typedefs *
61*******************************************************************************/
62/**
63 * Descriptor for a high memory chunk.
64 */
65typedef struct _HighChunk
66{
67 /** Pointer to the next one. */
68 struct _HighChunk *pNext;
69 /** Pointer to the chunk. */
70 void *pv;
71 /** Size of the chunk. */
72 size_t cb;
73 /** Size of the un committed memory. */
74 size_t cbUncommitted;
75} HIGHCHUNK;
76/** Pointer to a descriptor for a high memory chunk. */
77typedef HIGHCHUNK *PHIGHCHUNK;
78
79/**
80 * A pool of chunk descriptors.
81 */
82typedef struct _HighChunkPool
83{
84 /** Pointer to the next pool in the list.*/
85 struct _HighChunkPool *pNext;
86 /** List of free chunks. */
87 PHIGHCHUNK pFreeHead;
88 /** Index to the next uninitialized chunk.
89 * ~0 if all are initialized. */
90 unsigned iUnitialized;
91 /** Size of the pool. */
92 unsigned cChunks;
93 /** Array of cChunks entires. */
94 HIGHCHUNK aChunks[1];
95} HIGHCHUNKPOOL;
96/** Pointer to a pool of chunk descriptors. */
97typedef HIGHCHUNKPOOL *PHIGHCHUNKPOOL;
98
99
100/*******************************************************************************
101* Global Variables *
102*******************************************************************************/
103/** List of chuncks.
104 * Protected by _sys_gmtxHimem. */
105static PHIGHCHUNK gpHimemHead;
106/** Search hint containing NULL or the last used chunk.
107 * Protected by _sys_gmtxHimem. */
108static PHIGHCHUNK gpHimemHint;
109/** List of chunk pools.
110 * Protected by _sys_gmtxHimem. */
111static PHIGHCHUNKPOOL gpHimemPoolHead;
112
113
114
115/*******************************************************************************
116* Internal Functions *
117*******************************************************************************/
118static PHIGHCHUNK himemAllocChunk(void);
119static void himemFreeChunk(PHIGHCHUNK pChunk);
120
121
122/**
123 * Allocates a chunk descriptor.
124 * Caller must not own semaphore.
125 *
126 * @returns Pointer to chunk descriptor.
127 * Owner of the semaphore. Caller must free it.
128 * @returns NULL on failure.
129 */
130static PHIGHCHUNK himemAllocChunk(void)
131{
132 LIBCLOG_ENTER("\n");
133 PHIGHCHUNK pChunk;
134 PHIGHCHUNKPOOL pPool;
135 PVOID pv;
136 int rc;
137
138 /*
139 * Take semaphore.
140 */
141 if (_fmutex_request(&_sys_gmtxHimem, _FMR_IGNINT))
142 LIBCLOG_RETURN_P(NULL);
143
144 /*
145 * Walk the pool list and look for free chunks.
146 */
147 pChunk = NULL;
148 for (pPool = gpHimemPoolHead; pPool; pPool = pPool->pNext)
149 {
150 if (pPool->pFreeHead)
151 {
152 /* Unlink free chunk. */
153 pChunk = pPool->pFreeHead;
154 pPool->pFreeHead = pChunk->pNext;
155 pChunk->pNext = NULL;
156 LIBCLOG_RETURN_P(pChunk);
157 }
158 if (pPool->iUnitialized != ~0)
159 {
160 /* We commit page by page to make fork() as fast as possible. */
161 if ((((uintptr_t)&pPool->aChunks[pPool->iUnitialized]) & 0xfff) == 0)
162 {
163 rc = DosSetMem(&pPool->aChunks[pPool->iUnitialized], 0x1000, PAG_DEFAULT | PAG_COMMIT);
164 LIBC_ASSERTM(rc, "DosSetMem(%p, 0x1000,) -> rc=%d\n", (void *)&pPool->aChunks[pPool->iUnitialized], rc);
165 if (rc)
166 continue;
167 }
168 /* initialize a new chunk and return it. */
169 pChunk = &pPool->aChunks[pPool->iUnitialized++];
170 if (pPool->iUnitialized >= pPool->cChunks)
171 pPool->iUnitialized = ~0;
172 LIBCLOG_RETURN_P(pChunk);
173 }
174 }
175
176 /*
177 * We're out of chunk descriptors.
178 * Allocate another pool.
179 */
180 rc = DosAllocMemEx(&pv, HIMEM_POOL_SIZE, PAG_WRITE | PAG_READ | OBJ_ANY | OBJ_FORK);
181 if (rc)
182 {
183 LIBC_ASSERTM_FAILED("Failed to allocate more chunks. rc=%d\n", rc);
184 _fmutex_release(&_sys_gmtxHimem);
185 LIBCLOG_ERROR_RETURN_P(NULL);
186 }
187 /* Commit the first page. */
188 rc = DosSetMem(pv, 0x1000, PAG_DEFAULT | PAG_COMMIT);
189 if (rc)
190 {
191 LIBC_ASSERTM_FAILED("DosSetMem(%p, 0x1000,) -> rc=%d\n", pv, rc);
192 DosFreeMemEx(pv);
193 _fmutex_release(&_sys_gmtxHimem);
194 LIBCLOG_ERROR_RETURN_P(NULL);
195 }
196
197 /*
198 * Initialize the pool, allocate the first chunk and put the pool into the lifo.
199 */
200 pPool = (PHIGHCHUNKPOOL)pv;
201 pPool->cChunks = (HIMEM_POOL_SIZE - sizeof(HIGHCHUNKPOOL)) / sizeof(HIGHCHUNK);
202
203 pPool->iUnitialized = 1;
204 pChunk = &pPool->aChunks[0];
205
206 pPool->pNext = gpHimemPoolHead;
207 gpHimemPoolHead = pPool;
208
209 LIBCLOG_RETURN_P(pChunk);
210}
211
212
213/**
214 * Frees a chunk.
215 * The caller must own the semaphore.
216 *
217 * @param pChunk The chunk to free.
218 */
219static void himemFreeChunk(PHIGHCHUNK pChunk)
220{
221 LIBCLOG_ENTER("pChunk=%p:{pv=%p cb=%#x}\n", (void *)pChunk, pChunk->pv, pChunk->cb);
222 PHIGHCHUNKPOOL pPool;
223
224 /*
225 * Walk the pool list and look for free chunks.
226 */
227 for (pPool = gpHimemPoolHead; pPool; pPool = pPool->pNext)
228 {
229 if ( pChunk >= &pPool->aChunks[0]
230 && pChunk < &pPool->aChunks[pPool->cChunks])
231 {
232 pChunk->pv = NULL;
233 pChunk->cb = 0;
234 pChunk->cbUncommitted = 0;
235
236 pChunk->pNext = pPool->pFreeHead;
237 pPool->pFreeHead = pChunk;
238 LIBCLOG_RETURN_VOID();
239 }
240 }
241
242 LIBC_ASSERTM_FAILED("couldn't find pool which chunk %p belongs in!\n", (void *)pChunk);
243 LIBCLOG_ERROR_RETURN_VOID();
244}
245
246
247/**
248 * Heap callback function for allocating high memory.
249 * We allocate a good deal of memory, but only commits the requested size.
250 *
251 * @returns Pointer to the allocated memory on success.
252 * @returns NULL on failure.
253 * @param Heap Heap in question.
254 * @param pcb Input the requested block size.
255 * Output the amount of memory actually allocated.
256 * @param pfClean On output this will tell the call if the memory
257 * pointed to by the returned address is clean
258 * (i.e zeroed) or not.
259 *
260 * @remark Assumes it serves one heap only ATM!
261 */
262void *__libc_HimemDefaultAlloc(Heap_t Heap, size_t *pcb, int *pfClean)
263{
264 LIBCLOG_ENTER("Heap=%p pcb=%p:{%#x} pfClean=%p\n", (void *)Heap, (void *)pcb, *pcb, (void *)pfClean);
265 size_t cbAlloc;
266 size_t cbCommit;
267 PVOID pv;
268 int rc;
269 PHIGHCHUNK pChunk;
270
271
272 if (!gpHimemPoolHead)
273 {
274 /*
275 * The first time we force the allocation of the chunk decriptor
276 * pool before we allocate the actual chunk to optimize the address space.
277 */
278 pChunk = himemAllocChunk();
279 if (pChunk)
280 {
281 himemFreeChunk(pChunk);
282 _fmutex_release(&_sys_gmtxHimem);
283 }
284 }
285 else
286 {
287 /*
288 * Check if we have a chunk we can expand to satisfy the request.
289 */
290 if (_fmutex_request(&_sys_gmtxHimem, _FMR_IGNINT))
291 LIBCLOG_RETURN_P(NULL);
292 cbCommit = *pcb;
293 cbCommit = HIMEM_ROUND_SIZE(cbCommit, HIMEM_COMMIT_SIZE);
294 pChunk = gpHimemHint;
295 if (!pChunk)
296 pChunk = gpHimemHead;
297 for (; pChunk; pChunk = pChunk->pNext)
298 {
299 if (pChunk->cbUncommitted >= cbCommit)
300 {
301 /* commit the rest if it's less than the minimum commit size. */
302 if (pChunk->cbUncommitted - cbCommit < HIMEM_COMMIT_SIZE)
303 cbCommit = pChunk->cbUncommitted;
304
305 /*
306 * commit the lump.
307 */
308 pv = (char *)pChunk->pv + pChunk->cb - pChunk->cbUncommitted;
309 rc = DosSetMem(pv, cbCommit, PAG_DEFAULT | PAG_COMMIT);
310 if (rc)
311 { /* page by page */
312 void *pvCom = pv;
313 int cbCom = (int)cbCommit;
314 for (rc = 0; cbCom > 0; cbCom -= 0x1000, pv = (char *)pv + 0x1000)
315 {
316 int rc2 = DosSetMem(pvCom, 0x1000, PAG_DEFAULT | PAG_COMMIT);
317 LIBC_ASSERTM(!rc2, "DosSetMem(%p, 0x1000, commit) -> %d\n", pvCom, rc2);
318 if (rc2)
319 rc = rc2;
320 }
321 }
322
323 if (!rc)
324 {
325 pChunk->cbUncommitted -= cbCommit;
326 gpHimemHint = pChunk->cbUncommitted ? pChunk : gpHimemHead;
327 _fmutex_release(&_sys_gmtxHimem);
328
329 /* return pv and commit size. The heap takes care of joining
330 * it with the earlier part of the block. ASSUMES ONE HEAP!!! */
331 *pcb = cbCommit;
332 LIBCLOG_RETURN_MSG(pv, "ret %p *pcb=%#x\n", pv, *pcb);
333 }
334 LIBC_ASSERTM_FAILED("DosSetMem(%p, %#x, commit) -> %d\n", pv, cbCommit, rc);
335 continue;
336 }
337 }
338
339 /* Out of luck, allocate a new chunk. */
340 _fmutex_release(&_sys_gmtxHimem);
341 }
342
343
344 /*
345 * Allocate a (rather big) memory block, there is generally speaking
346 * more than enough high memory to allocate from. So, we'll round the
347 * chunks sizes up quite a bit to keep the object count low and the
348 * heap as flexible as possible.
349 */
350 cbAlloc = *pcb;
351 cbAlloc = HIMEM_ROUND_SIZE(cbAlloc, HIMEM_CHUNK_SIZE);
352 rc = DosAllocMemEx(&pv, cbAlloc, PAG_READ | PAG_WRITE | OBJ_ANY | OBJ_FORK);
353 if (rc == ERROR_NOT_ENOUGH_MEMORY)
354 {
355 /*
356 * That's odd, we're out of address space or something.
357 * Try again with the minimum rounding.
358 */
359 cbAlloc = *pcb + sizeof(HIGHCHUNK);
360 cbAlloc = HIMEM_ROUND_SIZE(cbAlloc, HIMEM_CHUNK_SIZE_MIN);
361 rc = DosAllocMemEx(pv, cbAlloc, PAG_READ | PAG_WRITE | OBJ_ANY | OBJ_FORK);
362 }
363 if (rc)
364 {
365 LIBC_ASSERTM_FAILED("Failed to allocate chunk! rc=%d cbAlloc=%d *pcb=%d\n", rc, cbAlloc, *pcb);
366 LIBCLOG_ERROR_RETURN_P(NULL);
367 }
368
369 /*
370 * Commit the requested memory size.
371 */
372 cbCommit = *pcb;
373 cbCommit = HIMEM_ROUND_SIZE(cbCommit, HIMEM_COMMIT_SIZE);
374 rc = DosSetMem(pv, cbCommit, PAG_DEFAULT | PAG_COMMIT);
375 if (!rc)
376 {
377 /*
378 * Allocate a chunk descriptor taking in the heap semaphore in the same run.
379 */
380 pChunk = himemAllocChunk();
381 if (pChunk)
382 {
383 /* init */
384 pChunk->pv = pv;
385 pChunk->cb = cbAlloc;
386 pChunk->cbUncommitted = cbAlloc - cbCommit;
387 /* link in to the list. */
388 pChunk->pNext = gpHimemHead;
389 gpHimemHead = pChunk;
390 gpHimemHint = pChunk;
391
392 /* release and return. */
393 _fmutex_release(&_sys_gmtxHimem);
394 *pcb = cbCommit;
395 *pfClean = _BLOCK_CLEAN;
396 LIBCLOG_RETURN_MSG(pv, "ret %p *pcb=%#x\n", pv, *pcb);
397 }
398 }
399 else
400 LIBC_ASSERTM_FAILED("DosSetMem(%p, %#x, PAG_DEFAULT | PAG_COMMIT) -> %d\n", pv, cbCommit, rc);
401
402 DosFreeMemEx(pv);
403 LIBCLOG_ERROR_RETURN_P(NULL);
404}
405
406
407/**
408 * Heap callback function for releaseing high memory.
409 *
410 * @param Heap Heap in question.
411 * @param pv Pointer to block.
412 * @param cb Size of block.
413 */
414void __libc_HimemDefaultRelease(Heap_t Heap, void *pv, size_t cb)
415{
416 LIBCLOG_ENTER("Heap=%p pv=%p cb=%#x\n", (void *)Heap, pv, cb);
417 int rc;
418 PHIGHCHUNK pChunk;
419 PHIGHCHUNK pPrevChunk;
420
421 LIBC_ASSERT(cb);
422 LIBC_ASSERT(!(cb & 0xfff));
423 LIBC_ASSERT(pv);
424 LIBC_ASSERT(!((uintptr_t)pv & 0xfff));
425
426
427 /*
428 * Take semaphore.
429 */
430 if (_fmutex_request(&_sys_gmtxHimem, _FMR_IGNINT) != 0)
431 return;
432
433 /*
434 * Remove from top to bottom of the described block.
435 *
436 * We must (?) handle cases where the pv+cb describes a memory area
437 * which covers several chunks. This is easier when done from the end.
438 *
439 * We ASSUME that the heap will not request areas which is not at the
440 * end of the committed memory in a chunk.
441 */
442 do
443 {
444 void *pvEnd = (char *)pv + cb;
445 for (pChunk = gpHimemHead, pPrevChunk = NULL; pChunk; pPrevChunk = pChunk, pChunk = pChunk->pNext)
446 {
447 size_t offEnd = (uintptr_t)pvEnd - (uintptr_t)pChunk->pv;
448 if (offEnd <= pChunk->cb)
449 {
450 void *pvFree;
451 size_t off = (uintptr_t)pv - (uintptr_t)pChunk->pv;
452 if (off > pChunk->cb)
453 off = 0;
454
455 /* check that it's at the end of the committed area. */
456 if (offEnd != pChunk->cb - pChunk->cbUncommitted)
457 {
458 LIBC_ASSERTM_FAILED("Bad high heap release!! pv=%p cb=%#x off=%#x offEnd=%#x; chunk pv=%p cb=%#x cbUncomitted=%#x\n",
459 pv, cb, off, offEnd, pChunk->pv, pChunk->cb, pChunk->cbUncommitted);
460 _fmutex_release(&_sys_gmtxHimem);
461 LIBCLOG_ERROR_RETURN_VOID();
462 }
463
464 /*
465 * Decommit part of the chunk.
466 */
467 if (off > 0)
468 {
469 size_t cbDecommit = offEnd - off;
470 rc = DosSetMem(pv, cbDecommit, PAG_DECOMMIT);
471 if (rc)
472 {
473 LIBC_ASSERTM_FAILED("DosSetMem(%p, %#x, decommit) -> %d\n", pv, cbDecommit, rc);
474 /* page by page */
475 for (; cbDecommit; cbDecommit -= 0x1000)
476 DosSetMem(pv, 0x1000, PAG_DECOMMIT);
477 }
478 pChunk->cbUncommitted += cbDecommit;
479
480 /* we're done. */
481 _fmutex_release(&_sys_gmtxHimem);
482 LIBCLOG_RETURN_VOID();
483 }
484
485 /*
486 * Free the chunk.
487 */
488 pvFree = pChunk->pv;
489 /* unlink and free the chunk descriptor */
490 if (pPrevChunk)
491 pPrevChunk->pNext = pChunk->pNext;
492 else
493 gpHimemHead = pChunk->pNext;
494 if (gpHimemHint == pChunk)
495 gpHimemHint = gpHimemHead;
496 himemFreeChunk(pChunk);
497
498 /* free */
499 rc = DosFreeMemEx(pvFree);
500 LIBC_ASSERTM(!rc, "DosFreeMem(%p) -> %d\n", pvFree, rc);
501
502 /* Update size and restart loop. */
503 cb -= offEnd - off;
504 break;
505 }
506 }
507
508 LIBC_ASSERTM(pChunk, "Couldn't find any chunk containing the area pv=%p cb=%#x!\n", pv, cb);
509 } while (cb && pChunk);
510
511 _fmutex_release(&_sys_gmtxHimem);
512 LIBCLOG_RETURN_VOID();
513}
514
515
516#if 0
517int __libc_HimemDefaultExpand(Heap_t Heap, void *pvBase, size_t cbOld, size_t *pcbNew, int *pfClean)
518{
519 LIBCLOG_ENTER("Heap=%p pvBase=%p cbOld=%#x pcbNew=%p:{%#x} pfClean=%p\n",
520 (void *)Heap, pvBase, cbOld, (void *)pcbNew, *pcbNew, (void *)pfClean);
521
522
523 LIBCLOG_RETURN_INT(0);
524}
525
526void __libc_HimemDefaultShrink(Heap_t Heap, void *pvBase, size_t cbOld, size_t *pcbNew)
527{
528 LIBCLOG_ENTER("Heap=%p pvBase=%p cbOld=%#x pcbNew=%p:{%#x} pfClean=%p\n",
529 (void *)Heap, pvBase, cbOld, (void *)pcbNew, *pcbNew, (void *)pfClean);
530
531 LIBCLOG_RETURN_VOID(0);
532}
533#endif
534
535
536/**
537 * Query if the system have support for high memory.
538 *
539 * @returns 1 if the system have more than 512MB of user address space.
540 * @returns 0 if the system only have 512MB of user address space.
541 */
542int __libc_HasHighMem(void)
543{
544 return _sys_gcbVirtualAddressLimit > 512*1024*1024;
545}
546
Note: See TracBrowser for help on using the repository browser.