1 /****************************************************************************
2 **
3 **  This file is part of GAP, a system for computational discrete algebra.
4 **
5 **  Copyright of GAP belongs to its developers, whose names are too numerous
6 **  to list here. Please refer to the COPYRIGHT file for details.
7 **
8 **  SPDX-License-Identifier: GPL-2.0-or-later
9 **
10 **  This file implements functions and variables related to memory management,
11 **  for use by GASMAN.
12 **
13 */
14 
15 #include "sysmem.h"
16 
17 #include "stats.h"
18 #include "sysfiles.h"
19 #include "sysopt.h"
20 
21 #ifdef GAP_MEM_CHECK
22 #include <fcntl.h>
23 #endif
24 
25 #include <unistd.h>
26 
27 #ifdef HAVE_MADVISE
28 #include <sys/mman.h>
29 #endif
30 
31 #ifdef HAVE_VM_ALLOCATE
32 #include <mach/mach.h>
33 #endif
34 
35 
36 Int SyStorMax;
37 Int SyStorOverrun;
38 Int SyStorKill;
39 Int SyStorMin;
40 
41 #if defined(USE_GASMAN)
42 UInt SyAllocPool;
43 #endif
44 
45 
46 /****************************************************************************
47 **
48 *F * * * * * * * * * * * * * * gasman interface * * * * * * * * * * * * * * *
49 */
50 
51 
52 /****************************************************************************
53 **
54 *F  SyMsgsBags( <full>, <phase>, <nr> ) . . . . . . . display Gasman messages
55 **
56 **  'SyMsgsBags' is the function that is used by Gasman to  display  messages
57 **  during garbage collections.
58 */
59 
60 Int SyGasmanNumbers[2][9];
61 
SyMsgsBags(UInt full,UInt phase,Int nr)62 void SyMsgsBags (
63     UInt                full,
64     UInt                phase,
65     Int                 nr )
66 {
67     Char                cmd [3];        /* command string buffer           */
68     Char                str [32];       /* string buffer                   */
69     Char                ch;             /* leading character               */
70     UInt                i;              /* loop variable                   */
71     Int                 copynr;         /* copy of <nr>                    */
72     UInt                shifted;        /* non-zero if nr > 10^6 and so
73                                            has to be shifted down          */
74     static UInt         tstart = 0;
75 
76     /* remember the numbers */
77     if (phase > 0)
78       {
79         SyGasmanNumbers[full][phase] = nr;
80 
81         /* in a full GC clear the partial numbers */
82         if (full)
83           SyGasmanNumbers[0][phase] = 0;
84       }
85     else
86       {
87         SyGasmanNumbers[full][0]++;
88         tstart = SyTime();
89       }
90     if (phase == 6)
91       {
92         UInt x = SyTime() - tstart;
93         SyGasmanNumbers[full][7] = x;
94         SyGasmanNumbers[full][8] += x;
95       }
96 
97     /* convert <nr> into a string with leading blanks                      */
98     copynr = nr;
99     ch = '0';  str[7] = '\0';
100     shifted = (nr >= ((phase % 2) ? 10000000 : 1000000)) ? 1 : 0;
101     if (shifted)
102       {
103         nr /= 1024;
104       }
105     if ((phase % 2) == 1 && shifted && nr > 1000000)
106       {
107         shifted++;
108         nr /= 1024;
109       }
110 
111     for ( i = ((phase % 2) == 1 && shifted) ? 6 : 7 ;
112           i != 0; i-- ) {
113         if      ( 0 < nr ) { str[i-1] = '0' + ( nr) % 10;  ch = ' '; }
114         else if ( nr < 0 ) { str[i-1] = '0' + (-nr) % 10;  ch = '-'; }
115         else               { str[i-1] = ch;                ch = ' '; }
116         nr = nr / 10;
117     }
118     nr = copynr;
119 
120     if ((phase % 2) == 1 && shifted == 1)
121       str[6] = 'K';
122     if ((phase % 2) == 1 && shifted == 2)
123       str[6] = 'M';
124 
125 
126 
127     /* ordinary full garbage collection messages                           */
128     if ( 1 <= SyMsgsFlagBags && full ) {
129         if ( phase == 0 ) { SyFputs( "#G  FULL ", 3 );                     }
130         if ( phase == 1 ) { SyFputs( str, 3 );  SyFputs( "/",         3 ); }
131         if ( phase == 2 ) { SyFputs( str, 3 );  SyFputs( shifted ? "mb live  " : "kb live  ", 3 ); }
132         if ( phase == 3 ) { SyFputs( str, 3 );  SyFputs( "/",         3 ); }
133         if ( phase == 4 ) { SyFputs( str, 3 );  SyFputs( shifted ? "mb dead  " : "kb dead  ", 3 ); }
134         if ( phase == 5 ) { SyFputs( str, 3 );  SyFputs( "/",         3 ); }
135         if ( phase == 6 ) { SyFputs( str, 3 );  SyFputs( shifted ? "mb free\n" : "kb free\n", 3 ); }
136     }
137 
138     /* ordinary partial garbage collection messages                        */
139     if ( 2 <= SyMsgsFlagBags && ! full ) {
140         if ( phase == 0 ) { SyFputs( "#G  PART ", 3 );                     }
141         if ( phase == 1 ) { SyFputs( str, 3 );  SyFputs( "/",         3 ); }
142         if ( phase == 2 ) { SyFputs( str, 3 );  SyFputs( shifted ? "mb+live  ":"kb+live  ", 3 ); }
143         if ( phase == 3 ) { SyFputs( str, 3 );  SyFputs( "/",         3 ); }
144         if ( phase == 4 ) { SyFputs( str, 3 );  SyFputs( shifted ? "mb+dead  ":"kb+dead  ", 3 ); }
145         if ( phase == 5 ) { SyFputs( str, 3 );  SyFputs( "/",         3 ); }
146         if ( phase == 6 ) { SyFputs( str, 3 );  SyFputs( shifted ? "mb free\n":"kb free\n", 3 ); }
147     }
148     /* package (window) mode full garbage collection messages              */
149     if ( phase != 0 ) {
150       cmd[0] = '@';
151       cmd[1] = ( full ? '0' : ' ' ) + phase;
152       cmd[2] = '\0';
153       i = 0;
154       for ( ; 0 < nr; nr /=10 )
155         str[i++] = '0' + (nr % 10);
156       str[i++] = '+';
157       str[i++] = '\0';
158       syWinPut( 1, cmd, str );
159     }
160 }
161 
162 
163 #if defined(USE_GASMAN)
164 
165 /****************************************************************************
166 **
167 *f  SyAllocBags( <size>, <need> )
168 **
169 **  For UNIX, 'SyAllocBags' calls 'sbrk', which will work on most systems.
170 **
171 **  Note that   it may  happen that  another   function   has  called  'sbrk'
172 **  between  two calls to  'SyAllocBags',  so that the  next  allocation will
173 **  not be immediately adjacent to the last one.   In this case 'SyAllocBags'
174 **  returns the area to the operating system,  and either returns 0 if <need>
175 **  was 0 or aborts GAP if <need> was 1.  'SyAllocBags' will refuse to extend
176 **  the workspace beyond 'SyStorMax' or to reduce it below 'SyStorMin'.
177 */
178 
179 static UInt pagesize = 4096;   /* Will be initialised if SyAllocPool > 0 */
180 
SyRoundUpToPagesize(UInt x)181 static inline UInt SyRoundUpToPagesize(UInt x)
182 {
183     UInt r;
184     r = x % pagesize;
185     return r == 0 ? x : x - r + pagesize;
186 }
187 
188 static void *   POOL = NULL;
189 static UInt *** syWorkspace = NULL;
190 static UInt     syWorksize = 0;
191 
EndOfWorkspace(void)192 static inline UInt *** EndOfWorkspace(void)
193 {
194     return syWorkspace + syWorksize * (1024 / sizeof(UInt **));
195 }
196 
197 #ifdef GAP_MEM_CHECK
198 
199 /***************************************************************
200  *  GAP_MEM_CHECK
201  *
202  * The following code is used by GAP_MEM_CHECK support, which is
203  * documented in gasman.c
204  */
205 
206 #if !defined(HAVE_MADVISE) || !defined(SYS_IS_64_BIT)
207 #error GAP_MEM_CHECK requires MADVISE and 64-bit OS
208 #endif
209 
SyMAdviseFree(void)210 void SyMAdviseFree(void)
211 {
212 }
213 
214 
215 enum { membufcount = 64 };
216 static void * membufs[membufcount];
217 static UInt   membufSize;
218 
GetMembufCount(void)219 UInt GetMembufCount(void)
220 {
221     return membufcount;
222 }
223 
GetMembuf(UInt i)224 void * GetMembuf(UInt i)
225 {
226     return membufs[i];
227 }
228 
GetMembufSize(void)229 UInt GetMembufSize(void)
230 {
231     return membufSize;
232 }
233 
order_pointers(const void * a,const void * b)234 static int order_pointers(const void * a, const void * b)
235 {
236     void * const * pa = a;
237     void * const * pb = b;
238     if (*pa < *pb) {
239         return -1;
240     }
241     else if (*pa > *pb) {
242         return 1;
243     }
244     else {
245         return 0;
246     }
247 }
248 
SyAnonMMap(size_t size)249 static void * SyAnonMMap(size_t size)
250 {
251     size = SyRoundUpToPagesize(size);
252     membufSize = size;
253 
254     unlink("/dev/shm/gapmem");
255     int fd = open("/dev/shm/gapmem", O_RDWR | O_CREAT | O_EXCL, 0600);
256     if (fd < 0) {
257         Panic("Fatal error setting up multiheap");
258     }
259 
260     if (ftruncate(fd, size) < 0) {
261         Panic("Fatal error setting up multiheap!");
262     }
263 
264     for (int i = 0; i < membufcount; ++i) {
265         membufs[i] =
266             mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
267         if (membufs[i] == MAP_FAILED) {
268             Panic("Fatal error setting up multiheap!!");
269         }
270     }
271 
272     // Sort the membufs, so membufs[0] is the first in memory.
273     // We will always refer to the copy of the master pointers in
274     // membufs[0].
275     qsort(membufs, membufcount, sizeof(void *), order_pointers);
276     return membufs[0];
277 }
278 
SyTryToIncreasePool(void)279 int SyTryToIncreasePool(void)
280 {
281     return -1;
282 }
283 
284 #elif defined(HAVE_MADVISE)
285 
286 #ifndef MAP_ANONYMOUS
287 #define MAP_ANONYMOUS MAP_ANON
288 #endif
289 
290 #ifdef SYS_IS_CYGWIN32
291 #define GAP_MMAP_FLAGS MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE
292 #else
293 #define GAP_MMAP_FLAGS MAP_PRIVATE|MAP_ANONYMOUS
294 #endif
295 
296 static void *SyMMapStart = NULL;   /* Start of mmap'ed region for POOL */
297 static void *SyMMapEnd;            /* End of mmap'ed region for POOL */
298 static void *SyMMapAdvised;        /* We have already advised about non-usage
299                                       up to here. */
300 
SyMAdviseFree(void)301 void SyMAdviseFree(void) {
302     size_t size;
303     void *from;
304     if (!SyMMapStart)
305         return;
306     from = EndOfWorkspace();
307     from = (void *)SyRoundUpToPagesize((UInt) from);
308     if (from > SyMMapAdvised) {
309         SyMMapAdvised = from;
310         return;
311     }
312     if (from < SyMMapStart || from >= SyMMapEnd || from >= SyMMapAdvised)
313         return;
314     size = (char *)SyMMapAdvised - (char *)from;
315 #if defined(MADV_FREE)
316     madvise(from, size, MADV_FREE);
317 #elif defined(MADV_DONTNEED)
318     madvise(from, size, MADV_DONTNEED);
319 #endif
320     SyMMapAdvised = from;
321     /* On Darwin, MADV_FREE and MADV_DONTNEED will not actually update
322      * a process's resident memory until those pages are explicitly
323      * unmapped or needed elsewhere.
324      *
325      * The following code accomplishes this, but is not portable and
326      * potentially not safe, since the POSIX standard does not make
327      * any sufficiently strong promises with regard to the use of
328      * MAP_FIXED.
329      *
330      * We probably don't want to do this and just live with pages
331      * remaining with a process until reused even if that appears to
332      * inflate the resident set size.
333      *
334      * Maybe we do want to do this until it breaks to avoid questions
335      * by users...
336      */
337 #if !defined(NO_DIRTY_OSX_MMAP_TRICK) && defined(SYS_IS_DARWIN)
338     if (mmap(from, size, PROT_NONE,
339             MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) != from) {
340         Panic("OS X trick to free pages did not work!");
341     }
342     if (mmap(from, size, PROT_READ|PROT_WRITE,
343             MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) != from) {
344         Panic("OS X trick to free pages did not work!!");
345     }
346 #endif
347 }
348 
SyAnonMMap(size_t size)349 static void * SyAnonMMap(size_t size)
350 {
351     void *result;
352     size = SyRoundUpToPagesize(size);
353 #ifdef SYS_IS_64_BIT
354     /* The following is at 16 Terabyte: */
355     result = mmap((void *) (16L*1024*1024*1024*1024), size,
356                   PROT_READ|PROT_WRITE, GAP_MMAP_FLAGS, -1, 0);
357     if (result == MAP_FAILED) {
358         result = mmap(NULL, size, PROT_READ|PROT_WRITE,
359             GAP_MMAP_FLAGS, -1, 0);
360     }
361 #else
362     result = mmap(NULL, size, PROT_READ|PROT_WRITE,
363         GAP_MMAP_FLAGS, -1, 0);
364 #endif
365     if (result == MAP_FAILED)
366         result = NULL;
367     SyMMapStart = result;
368     SyMMapEnd = (char *)result + size;
369     SyMMapAdvised = (char *)result + size;
370     return result;
371 }
372 
SyTryToIncreasePool(void)373 static int SyTryToIncreasePool(void)
374 /* This tries to increase the pool size by a factor of 3/2, if this
375  * worked, then 0 is returned, otherwise -1. */
376 {
377     void *result;
378     size_t size;
379     size_t newchunk;
380 
381     size = (Int) SyMMapEnd - (Int) SyMMapStart;
382     newchunk = SyRoundUpToPagesize(size/2);
383     result = mmap(SyMMapEnd, newchunk, PROT_READ|PROT_WRITE,
384                   GAP_MMAP_FLAGS, -1, 0);
385     if (result == MAP_FAILED) return -1;
386     if (result != SyMMapEnd) {
387         munmap(result,newchunk);
388         return -1;
389     }
390     /* We actually got an extension! */
391     SyMMapEnd = (void *)((char *)SyMMapEnd + newchunk);
392     SyAllocPool += newchunk;
393     return 0;
394 }
395 
396 #else
397 
SyMAdviseFree(void)398 static void SyMAdviseFree(void)
399 {
400     /* do nothing */
401 }
402 
SyTryToIncreasePool(void)403 static int SyTryToIncreasePool(void)
404 {
405     return -1;   /* Refuse */
406 }
407 
408 #endif // defined(GAP_MEM_CHECK)
409 
410 
411 static int halvingsdone = 0;
412 
SyInitialAllocPool(void)413 static void SyInitialAllocPool(void)
414 {
415 #ifdef HAVE_SYSCONF
416 #ifdef _SC_PAGESIZE
417    pagesize = sysconf(_SC_PAGESIZE);
418 #endif
419 #endif
420    /* Otherwise we take the default of 4k as pagesize. */
421 
422    do {
423        /* Always round up to pagesize: */
424        SyAllocPool = SyRoundUpToPagesize(SyAllocPool);
425 #ifdef HAVE_MADVISE
426        POOL = SyAnonMMap(SyAllocPool+pagesize);   /* For alignment */
427 #else
428        POOL = calloc(SyAllocPool+pagesize,1);   /* For alignment */
429 #endif
430        if (POOL != NULL) {
431            /* fprintf(stderr,"Pool size is %lx.\n",SyAllocPool); */
432            break;
433        }
434        SyAllocPool = SyAllocPool / 2;
435        halvingsdone++;
436        if (SyDebugLoading) fputs("gap: halving pool size.\n", stderr);
437        if (SyAllocPool < 16*1024*1024) {
438            Panic("cannot allocate initial memory");
439        }
440    } while (1);   /* Is left by break */
441 
442    /* ensure alignment of start address */
443    syWorkspace = (UInt***)(SyRoundUpToPagesize((UInt) POOL));
444    /* Now both syWorkspace and SyAllocPool are aligned to pagesize */
445 }
446 
SyAllocBagsFromPool(Int size,UInt need)447 static UInt *** SyAllocBagsFromPool(Int size, UInt need)
448 {
449   /* get the storage, but only if we stay within the bounds              */
450   /* if ( (0 < size && syWorksize + size <= SyStorMax) */
451   /* first check if we would get above SyStorKill, if yes exit! */
452   if ( need < 2 && SyStorKill != 0 && 0 < size
453                 && SyStorKill < syWorksize + size ) {
454       Panic("will not extend workspace above -K limit!");
455   }
456   if (size > 0) {
457     while ((syWorksize+size)*1024 > SyAllocPool) {
458         if (SyTryToIncreasePool()) return (UInt***)-1;
459     }
460     return EndOfWorkspace();
461   }
462   else if  (size < 0 && (need >= 2 || SyStorMin <= syWorksize + size))
463     return EndOfWorkspace();
464   else
465     return (UInt***)-1;
466 }
467 
468 #if defined(HAVE_SBRK) && !defined(HAVE_VM_ALLOCATE) /* prefer `vm_allocate' over `sbrk' */
469 
SyAllocBags(Int size,UInt need)470 UInt *** SyAllocBags(Int size, UInt need)
471 {
472     UInt * * *          ret;
473     UInt adjust = 0;
474 
475     if (SyAllocPool > 0) {
476       if (POOL == NULL) SyInitialAllocPool();
477       /* Note that this does abort GAP if it does not succeed! */
478 
479       ret = SyAllocBagsFromPool(size,need);
480     }
481     else {
482 
483 
484 
485         /* force alignment on first call                                       */
486         if ( syWorkspace == (UInt***)0 ) {
487 #ifdef SYS_IS_64_BIT
488             syWorkspace = (UInt***)sbrk( 8 - (UInt)sbrk(0) % 8 );
489 #else
490             syWorkspace = (UInt***)sbrk( 4 - (UInt)sbrk(0) % 4 );
491 #endif
492             syWorkspace = (UInt***)sbrk( 0 );
493         }
494 
495         /* get the storage, but only if we stay within the bounds              */
496         /* if ( (0 < size && syWorksize + size <= SyStorMax) */
497         /* first check if we would get above SyStorKill, if yes exit! */
498         if ( need < 2 && SyStorKill != 0 && 0 < size &&
499              SyStorKill < syWorksize + size ) {
500             Panic("will not extend workspace above -K limit!");
501         }
502         if (0 < size )
503           {
504 #ifndef SYS_IS_64_BIT
505             while (size > 1024*1024)
506               {
507                 ret = (UInt ***)sbrk(1024*1024*1024);
508                 if (ret != (UInt ***)-1  &&
509                     ret != EndOfWorkspace())
510                   {
511                     sbrk(-1024*1024*1024);
512                     ret = (UInt ***)-1;
513                   }
514                 if (ret == (UInt ***)-1)
515                   break;
516                 memset(EndOfWorkspace(), 0, 1024*1024*1024);
517                 size -= 1024*1024;
518                 syWorksize += 1024*1024;
519                 adjust++;
520               }
521 #endif
522             ret = (UInt ***)sbrk(size*1024);
523             if (ret != (UInt ***)-1  &&
524                 ret != EndOfWorkspace())
525               {
526                 sbrk(-size*1024);
527                 ret = (UInt ***)-1;
528               }
529             if (ret != (UInt ***)-1)
530               memset(EndOfWorkspace(), 0,
531                      1024*size);
532 
533           }
534         else if  (size < 0 && (need >= 2 || SyStorMin <= syWorksize + size))  {
535 #ifndef SYS_IS_64_BIT
536           while (size < -1024*1024)
537             {
538               ret = (UInt ***)sbrk(-1024*1024*1024);
539               if (ret == (UInt ***)-1)
540                 break;
541               size += 1024*1024;
542               syWorksize -= 1024*1024;
543             }
544 #endif
545             ret = (UInt ***)sbrk(size*1024);
546         }
547         else {
548           ret = (UInt***)-1;
549         }
550     }
551 
552 
553     /* update the size info                                                */
554     if ( ret != (UInt***)-1 ) {
555         syWorksize += size;
556         /* set the overrun flag if we became larger than SyStorMax */
557         if ( SyStorMax != 0 && syWorksize  > SyStorMax)  {
558           SyStorOverrun = -1;
559           SyStorMax=syWorksize*2; /* new maximum */
560           InterruptExecStat(); /* interrupt at the next possible point */
561         }
562     }
563 
564     /* test if the allocation failed                                       */
565     if ( ret == (UInt***)-1 && need ) {
566         Panic("cannot extend the workspace any more!");
567     }
568     /* if we de-allocated the whole workspace then remember this */
569     if (syWorksize == 0)
570       syWorkspace = (UInt ***)0;
571 
572     /* otherwise return the result (which could be 0 to indicate failure)  */
573     if ( ret == (UInt***)-1 )
574         return 0;
575     else
576       {
577         return (UInt***)(((Char *)ret) - 1024*1024*1024*adjust);
578       }
579 
580 }
581 
582 #endif
583 
584 
585 /****************************************************************************
586 **
587 *f  SyAllocBags( <size>, <need> )
588 **
589 **  Under MACH virtual memory managment functions are used instead of 'sbrk'.
590 */
591 #ifdef HAVE_VM_ALLOCATE
592 
593 #if (defined(SYS_IS_DARWIN) && SYS_IS_DARWIN) || defined(__gnu_hurd__)
594 #define task_self mach_task_self
595 #endif
596 
597 static vm_address_t syBase;
598 
SyAllocBags(Int size,UInt need)599 UInt * * * SyAllocBags (
600     Int                 size,
601     UInt                need )
602 {
603     UInt * * *          ret = (UInt***)-1;
604     vm_address_t        adr;
605 
606     if (SyAllocPool > 0) {
607       if (POOL == NULL) SyInitialAllocPool();
608       /* Note that this does abort GAP if it does not succeed! */
609 
610       ret = SyAllocBagsFromPool(size,need);
611       if (ret != (UInt ***)-1)
612           syWorksize += size;
613 
614     }
615     else {
616         if ( SyStorKill != 0 && 0 < size && SyStorKill < 1024*(syWorksize + size) ) {
617             if (need) {
618                 Panic("will not extend workspace above -K limit!");
619             }
620         }
621         /* check that <size> is divisible by <vm_page_size>                    */
622         else if ( size*1024 % vm_page_size != 0 ) {
623             Panic("memory block size is not a multiple of vm_page_size");
624         }
625 
626         /* check that we don't try to shrink uninitialized memory                */
627         else if ( size <= 0 && syBase == 0 ) {
628             Panic("trying to shrink uninitialized vm memory");
629         }
630 
631         /* allocate memory anywhere on first call                              */
632         else if ( 0 < size && syBase == 0 ) {
633             if ( vm_allocate(task_self(),&syBase,size*1024,TRUE) == KERN_SUCCESS ) {
634                 syWorksize = size;
635                 ret = (UInt***) syBase;
636             }
637         }
638 
639         /* don't shrink memory but mark it as deactivated                      */
640         else if ( size < 0 && syWorksize + size > SyStorMin) {
641             adr = (vm_address_t)( (char*) syBase + (syWorksize+size)*1024 );
642             if ( vm_deallocate(task_self(),adr,-size*1024) == KERN_SUCCESS ) {
643                 ret = (UInt***)( (char*) syBase + syWorksize*1024 );
644                 syWorksize += size;
645             }
646         }
647 
648         /* get more memory from system                                         */
649         else {
650             adr = (vm_address_t)( (char*) syBase + syWorksize*1024 );
651             if ( vm_allocate(task_self(),&adr,size*1024,FALSE) == KERN_SUCCESS ) {
652                 ret = (UInt***) ( (char*) syBase + syWorksize*1024 );
653                 syWorksize += size;
654             }
655         }
656 
657         /* test if the allocation failed                                       */
658         if ( ret == (UInt***)-1 && need ) {
659             Panic("cannot extend the workspace any more!!");
660         }
661     }
662 
663     /* otherwise return the result (which could be 0 to indicate failure)  */
664     if ( ret == (UInt***)-1 ){
665         if (need) {
666             Panic("cannot extend the workspace any more!!!");
667         }
668         return (UInt***) 0;
669     }
670     else {
671         if (syWorksize  > SyStorMax)  {
672             SyStorOverrun = -1;
673             SyStorMax=syWorksize*2; /* new maximum */
674             InterruptExecStat(); /* interrupt at the next possible point */
675        }
676      }
677     return ret;
678 }
679 
680 #endif
681 
682 
683 #endif // defined(USE_GASMAN)
684