FORM  4.1
threads.c
Go to the documentation of this file.
1 
22 /* #[ License : */
23 /*
24  * Copyright (C) 1984-2013 J.A.M. Vermaseren
25  * When using this file you are requested to refer to the publication
26  * J.A.M.Vermaseren "New features of FORM" math-ph/0010025
27  * This is considered a matter of courtesy as the development was paid
28  * for by FOM the Dutch physics granting agency and we would like to
29  * be able to track its scientific use to convince FOM of its value
30  * for the community.
31  *
32  * This file is part of FORM.
33  *
34  * FORM is free software: you can redistribute it and/or modify it under the
35  * terms of the GNU General Public License as published by the Free Software
36  * Foundation, either version 3 of the License, or (at your option) any later
37  * version.
38  *
39  * FORM is distributed in the hope that it will be useful, but WITHOUT ANY
40  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
41  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
42  * details.
43  *
44  * You should have received a copy of the GNU General Public License along
45  * with FORM. If not, see <http://www.gnu.org/licenses/>.
46  */
47 /* #] License : */
48 
49 #ifdef WITHPTHREADS
50 
51 #define WHOLEBRACKETS
52 /*
53  #[ Variables :
54 
55  The sortbot additions are from 17-may-2007 and after. They consitute
56  an attempt to make the final merge sorting faster for the master.
57  This way the master has only one compare per term.
58  It does add some complexity, but the final merge routine (MasterMerge)
59  is much simpler for the sortbots. On the other hand the original merging is
60  for a large part a copy of the MergePatches routine in sort.c and hence
61  even though complex the bad part has been thoroughly debugged.
62 */
63 
64 #include "form3.h"
65 
66 static int numberofthreads;
67 static int numberofworkers;
68 static int identityofthreads = 0;
69 static int *listofavailables;
70 static int topofavailables = 0;
71 static pthread_key_t identitykey;
72 static INILOCK(numberofthreadslock);
73 static INILOCK(availabilitylock);
74 static pthread_t *threadpointers = 0;
75 static pthread_mutex_t *wakeuplocks;
76 static pthread_mutex_t *wakeupmasterthreadlocks;
77 static pthread_cond_t *wakeupconditions;
78 static pthread_condattr_t *wakeupconditionattributes;
79 static int *wakeup;
80 static int *wakeupmasterthread;
81 static INILOCK(wakeupmasterlock);
82 static pthread_cond_t wakeupmasterconditions = PTHREAD_COND_INITIALIZER;
83 static pthread_cond_t *wakeupmasterthreadconditions;
84 static int wakeupmaster = 0;
85 static int identityretval;
86 /* static INILOCK(clearclocklock); */
87 static LONG *timerinfo;
88 static LONG *sumtimerinfo;
89 static int numberclaimed;
90 
91 static THREADBUCKET **threadbuckets, **freebuckets;
92 static int numthreadbuckets;
93 static int numberoffullbuckets;
94 
95 /* static int numberbusy = 0; */
96 
97 INILOCK(dummylock);
98 INIRWLOCK(dummyrwlock);
99 static pthread_cond_t dummywakeupcondition = PTHREAD_COND_INITIALIZER;
100 
101 #ifdef WITHSORTBOTS
102 static POSITION SortBotPosition;
103 static int numberofsortbots;
104 static INILOCK(wakeupsortbotlock);
105 static pthread_cond_t wakeupsortbotconditions = PTHREAD_COND_INITIALIZER;
106 static int topsortbotavailables = 0;
107 static LONG numberofterms;
108 #endif
109 
110 /*
111  #] Variables :
112  #[ Identity :
113  #[ StartIdentity :
114 */
120 void StartIdentity()
121 {
122  pthread_key_create(&identitykey,FinishIdentity);
123 }
124 
125 /*
126  #] StartIdentity :
127  #[ FinishIdentity :
128 */
133 void FinishIdentity(void *keyp)
134 {
135  DUMMYUSE(keyp);
136 /* free(keyp); */
137 }
138 
139 /*
140  #] FinishIdentity :
141  #[ SetIdentity :
142 */
147 int SetIdentity(int *identityretval)
148 {
149 /*
150 #ifdef _MSC_VER
151  printf("addr %d\n",&numberofthreadslock);
152  printf("size %d\n",sizeof(numberofthreadslock));
153 #endif
154 */
155  LOCK(numberofthreadslock);
156  *identityretval = identityofthreads++;
157  UNLOCK(numberofthreadslock);
158  pthread_setspecific(identitykey,(void *)identityretval);
159  return(*identityretval);
160 }
161 
162 /*
163  #] SetIdentity :
164  #[ WhoAmI :
165 */
166 
177 int WhoAmI()
178 {
179  int *identity;
180 /*
181  First a fast exit for when there is at most one thread
182 */
183  if ( identityofthreads <= 1 ) return(0);
184 /*
185  Now the reading of the key.
186  According to the book the statement should read:
187 
188  pthread_getspecific(identitykey,(void **)(&identity));
189 
190  but according to the information in pthread.h it is:
191 */
192  identity = (int *)pthread_getspecific(identitykey);
193  return(*identity);
194 }
195 
196 /*
197  #] WhoAmI :
198  #[ BeginIdentities :
199 */
205 VOID BeginIdentities()
206 {
207  StartIdentity();
208  SetIdentity(&identityretval);
209 }
210 
211 /*
212  #] BeginIdentities :
213  #] Identity :
214  #[ StartHandleLock :
215 */
222 void StartHandleLock()
223 {
224  AM.handlelock = dummyrwlock;
225 }
226 
227 /*
228  #] StartHandleLock :
229  #[ StartAllThreads :
230 */
248 int StartAllThreads(int number)
249 {
250  int identity, j, dummy, mul;
251  ALLPRIVATES *B;
252  pthread_t thethread;
253  identity = WhoAmI();
254 
255 #ifdef WITHSORTBOTS
256  timerinfo = (LONG *)Malloc1(sizeof(LONG)*number*2,"timerinfo");
257  sumtimerinfo = (LONG *)Malloc1(sizeof(LONG)*number*2,"sumtimerinfo");
258  for ( j = 0; j < number*2; j++ ) { timerinfo[j] = 0; sumtimerinfo[j] = 0; }
259  mul = 2;
260 #else
261  timerinfo = (LONG *)Malloc1(sizeof(LONG)*number,"timerinfo");
262  sumtimerinfo = (LONG *)Malloc1(sizeof(LONG)*number,"sumtimerinfo");
263  for ( j = 0; j < number; j++ ) { timerinfo[j] = 0; sumtimerinfo[j] = 0; }
264  mul = 1;
265 #endif
266 
267  listofavailables = (int *)Malloc1(sizeof(int)*(number+1),"listofavailables");
268  threadpointers = (pthread_t *)Malloc1(sizeof(pthread_t)*number*mul,"threadpointers");
269  AB = (ALLPRIVATES **)Malloc1(sizeof(ALLPRIVATES *)*number*mul,"Private structs");
270 
271  wakeup = (int *)Malloc1(sizeof(int)*number*mul,"wakeup");
272  wakeuplocks = (pthread_mutex_t *)Malloc1(sizeof(pthread_mutex_t)*number*mul,"wakeuplocks");
273  wakeupconditions = (pthread_cond_t *)Malloc1(sizeof(pthread_cond_t)*number*mul,"wakeupconditions");
274  wakeupconditionattributes = (pthread_condattr_t *)
275  Malloc1(sizeof(pthread_condattr_t)*number*mul,"wakeupconditionattributes");
276 
277  wakeupmasterthread = (int *)Malloc1(sizeof(int)*number*mul,"wakeupmasterthread");
278  wakeupmasterthreadlocks = (pthread_mutex_t *)Malloc1(sizeof(pthread_mutex_t)*number*mul,"wakeupmasterthreadlocks");
279  wakeupmasterthreadconditions = (pthread_cond_t *)Malloc1(sizeof(pthread_cond_t)*number*mul,"wakeupmasterthread");
280 
281  numberofthreads = number;
282  numberofworkers = number - 1;
283  threadpointers[identity] = pthread_self();
284  topofavailables = 0;
285  for ( j = 1; j < number; j++ ) {
286  if ( pthread_create(&thethread,NULL,RunThread,(void *)(&dummy)) )
287  goto failure;
288  }
289 /*
290  Now we initialize the master at the same time that the workers are doing so.
291 */
292  B = InitializeOneThread(identity);
293  AR.infile = &(AR.Fscr[0]);
294  AR.outfile = &(AR.Fscr[1]);
295  AR.hidefile = &(AR.Fscr[2]);
296  AM.sbuflock = dummylock;
297  AS.inputslock = dummylock;
298  AS.outputslock = dummylock;
299  AS.MaxExprSizeLock = dummylock;
300  AP.PreVarLock = dummylock;
301  AC.halfmodlock = dummylock;
302  MakeThreadBuckets(number,0);
303 /*
304  Now we wait for the workers to finish their startup.
305  We don't want to initialize the sortbots yet and run the risk that
306  some of them may end up with a lower number than one of the workers.
307 */
308  MasterWaitAll();
309 #ifdef WITHSORTBOTS
310  if ( numberofworkers > 2 ) {
311  numberofsortbots = numberofworkers-2;
312  for ( j = numberofworkers+1; j < 2*numberofworkers-1; j++ ) {
313  if ( pthread_create(&thethread,NULL,RunSortBot,(void *)(&dummy)) )
314  goto failure;
315  }
316  }
317  else {
318  numberofsortbots = 0;
319  }
320  MasterWaitAllSortBots();
321  DefineSortBotTree();
322 #endif
323  IniSortBlocks(number-1);
324  AS.MasterSort = 0;
325  AM.storefilelock = dummylock;
326 /*
327 MesPrint("AB = %x %x %x %d",AB[0],AB[1],AB[2], identityofthreads);
328 */
329  return(0);
330 failure:
331  MesPrint("Cannot start %d threads",number);
332  Terminate(-1);
333  return(-1);
334 }
335 
336 /*
337  #] StartAllThreads :
338  #[ InitializeOneThread :
339 */
343 UBYTE *scratchname[] = { (UBYTE *)"scratchsize",
344  (UBYTE *)"scratchsize",
345  (UBYTE *)"hidesize" };
372 ALLPRIVATES *InitializeOneThread(int identity)
373 {
374  WORD *t, *ScratchBuf;
375  int i, j, bsize, *bp;
376  LONG ScratchSize[3], IOsize;
377  ALLPRIVATES *B;
378  UBYTE *s;
379 
380  wakeup[identity] = 0;
381  wakeuplocks[identity] = dummylock;
382  pthread_condattr_init(&(wakeupconditionattributes[identity]));
383  pthread_condattr_setpshared(&(wakeupconditionattributes[identity]),PTHREAD_PROCESS_PRIVATE);
384  wakeupconditions[identity] = dummywakeupcondition;
385  pthread_cond_init(&(wakeupconditions[identity]),&(wakeupconditionattributes[identity]));
386  wakeupmasterthread[identity] = 0;
387  wakeupmasterthreadlocks[identity] = dummylock;
388  wakeupmasterthreadconditions[identity] = dummywakeupcondition;
389 
390  bsize = sizeof(ALLPRIVATES);
391  bsize = (bsize+sizeof(int)-1)/sizeof(int);
392  B = (ALLPRIVATES *)Malloc1(sizeof(int)*bsize,"B struct");
393  for ( bp = (int *)B, j = 0; j < bsize; j++ ) *bp++ = 0;
394 
395  AB[identity] = B;
396 /*
397  12-jun-2007 JV:
398  For the timing one has to know a few things:
399  The POSIX standard is that there is only a single process ID and that
400  getrusage returns the time of all the threads together.
401  Under Linux there are two methods though: The older LinuxThreads and NPTL.
402  LinuxThreads gives each thread its own process id. This makes that we
403  can time the threads with getrusage, and hence this was done. Under NPTL
404  this has been 'corrected' and suddenly getruage doesn't work anymore the
405  way it used to. Now we need
406  clock_gettime(CLOCK_THREAD_CPUTIME_ID,&timing)
407  which is declared in <time.h> and we need -lrt extra in the link statement.
408  (this is at least the case on blade02 at DESY-Zeuthen).
409  See also the code in tools.c at the routine Timer.
410  We may still have to include more stuff there.
411 */
412  if ( identity > 0 ) TimeCPU(0);
413 
414 #ifdef WITHSORTBOTS
415 
416  if ( identity > numberofworkers ) {
417 /*
418  Some workspace is needed when we have a PolyFun and we have to add
419  two terms and the new result is going to be longer than the old result.
420 */
421  LONG length = AM.WorkSize*sizeof(WORD)/8+AM.MaxTer*2;
422  AT.WorkSpace = (WORD *)Malloc1(length,"WorkSpace");
423  AT.WorkTop = AT.WorkSpace + length/sizeof(WORD);
424  AT.WorkPointer = AT.WorkSpace;
425  AT.identity = identity;
426 /*
427  The SB struct gets treated in IniSortBlocks.
428  The SortBotIn variables will be defined DefineSortBotTree.
429 */
430  if ( AN.SoScratC == 0 ) {
431  AN.SoScratC = (UWORD *)Malloc1(2*(AM.MaxTal+2)*sizeof(UWORD),"Scratch in SortBot");
432  }
433  AT.SS = (SORTING *)Malloc1(sizeof(SORTING),"dummy sort buffer");
434  AT.SS->PolyFlag = 0;
435 
436  AT.comsym[0] = 8;
437  AT.comsym[1] = SYMBOL;
438  AT.comsym[2] = 4;
439  AT.comsym[3] = 0;
440  AT.comsym[4] = 1;
441  AT.comsym[5] = 1;
442  AT.comsym[6] = 1;
443  AT.comsym[7] = 3;
444  AT.comnum[0] = 4;
445  AT.comnum[1] = 1;
446  AT.comnum[2] = 1;
447  AT.comnum[3] = 3;
448  AT.comfun[0] = FUNHEAD+4;
449  AT.comfun[1] = FUNCTION;
450  AT.comfun[2] = FUNHEAD;
451  AT.comfun[3] = 0;
452 #if FUNHEAD > 3
453  for ( i = 4; i <= FUNHEAD; i++ )
454  AT.comfun[i] = 0;
455 #endif
456  AT.comfun[FUNHEAD+1] = 1;
457  AT.comfun[FUNHEAD+2] = 1;
458  AT.comfun[FUNHEAD+3] = 3;
459  AT.comind[0] = 7;
460  AT.comind[1] = INDEX;
461  AT.comind[2] = 3;
462  AT.comind[3] = 0;
463  AT.comind[4] = 1;
464  AT.comind[5] = 1;
465  AT.comind[6] = 3;
466 
467  AT.inprimelist = -1;
468  AT.sizeprimelist = 0;
469  AT.primelist = 0;
470  AT.bracketinfo = 0;
471 
472  AR.CompareRoutine = &Compare1;
473 
474  AR.sLevel = 0;
475  AR.wranfia = 0;
476  AR.wranfcall = 0;
477  AR.wranfnpair1 = NPAIR1;
478  AR.wranfnpair2 = NPAIR2;
479  AN.NumFunSorts = 5;
480  AN.MaxFunSorts = 5;
481  AN.SplitScratch = 0;
482  AN.SplitScratchSize = AN.InScratch = 0;
483  AN.SplitScratch1 = 0;
484  AN.SplitScratchSize1 = AN.InScratch1 = 0;
485 
486  AN.FunSorts = (SORTING **)Malloc1((AN.NumFunSorts+1)*sizeof(SORTING *),"FunSort pointers");
487  for ( i = 0; i <= AN.NumFunSorts; i++ ) AN.FunSorts[i] = 0;
488  AN.FunSorts[0] = AT.S0 = AT.SS;
489 
490  return(B);
491  }
492  if ( identity == 0 && AN.SoScratC == 0 ) {
493  AN.SoScratC = (UWORD *)Malloc1(2*(AM.MaxTal+2)*sizeof(UWORD),"Scratch in SortBot");
494  }
495 #endif
496  AR.CurDum = AM.IndDum;
497  for ( j = 0; j < 3; j++ ) {
498  if ( identity == 0 ) {
499  if ( j == 2 ) {
500  ScratchSize[j] = AM.HideSize;
501  }
502  else {
503  ScratchSize[j] = AM.ScratSize;
504  }
505  if ( ScratchSize[j] < 10*AM.MaxTer ) ScratchSize[j] = 10 * AM.MaxTer;
506  }
507  else {
508 /*
509  ScratchSize[j] = AM.ScratSize / (numberofthreads-1);
510  ScratchSize[j] = ScratchSize[j] / 20;
511  if ( ScratchSize[j] < 10*AM.MaxTer ) ScratchSize[j] = 10 * AM.MaxTer;
512 */
513  if ( j == 1 ) ScratchSize[j] = AM.ThreadScratOutSize;
514  else ScratchSize[j] = AM.ThreadScratSize;
515  if ( ScratchSize[j] < 4*AM.MaxTer ) ScratchSize[j] = 4 * AM.MaxTer;
516  AR.Fscr[j].name = 0;
517  }
518  ScratchSize[j] = ( ScratchSize[j] + 255 ) / 256;
519  ScratchSize[j] = ScratchSize[j] * 256;
520  ScratchBuf = (WORD *)Malloc1(ScratchSize[j]*sizeof(WORD),(char *)(scratchname[j]));
521  AR.Fscr[j].POsize = ScratchSize[j] * sizeof(WORD);
522  AR.Fscr[j].POfull = AR.Fscr[j].POfill = AR.Fscr[j].PObuffer = ScratchBuf;
523  AR.Fscr[j].POstop = AR.Fscr[j].PObuffer + ScratchSize[j];
524  PUTZERO(AR.Fscr[j].POposition);
525  AR.Fscr[j].pthreadslock = dummylock;
526  AR.Fscr[j].wPOsize = AR.Fscr[j].POsize;
527  AR.Fscr[j].wPObuffer = AR.Fscr[j].PObuffer;
528  AR.Fscr[j].wPOfill = AR.Fscr[j].POfill;
529  AR.Fscr[j].wPOfull = AR.Fscr[j].POfull;
530  AR.Fscr[j].wPOstop = AR.Fscr[j].POstop;
531  }
532  AR.InInBuf = 0;
533  AR.InHiBuf = 0;
534  AR.Fscr[0].handle = -1;
535  AR.Fscr[1].handle = -1;
536  AR.Fscr[2].handle = -1;
537  AR.FoStage4[0].handle = -1;
538  AR.FoStage4[1].handle = -1;
539  IOsize = AM.S0->file.POsize;
540 #ifdef WITHZLIB
541  AR.FoStage4[0].ziosize = IOsize;
542  AR.FoStage4[1].ziosize = IOsize;
543 #endif
544  AR.FoStage4[0].POsize = ((IOsize+sizeof(WORD)-1)/sizeof(WORD))*sizeof(WORD);
545  AR.FoStage4[1].POsize = ((IOsize+sizeof(WORD)-1)/sizeof(WORD))*sizeof(WORD);
546 
547  AR.hidefile = &(AR.Fscr[2]);
548  AR.StoreData.Handle = -1;
549  AR.SortType = AC.SortType;
550 
551  AN.IndDum = AM.IndDum;
552 
553  if ( identity > 0 ) {
554  s = (UBYTE *)(FG.fname); i = 0;
555  while ( *s ) { s++; i++; }
556  s = (UBYTE *)Malloc1(sizeof(char)*(i+12),"name for Fscr[0] file");
557  sprintf((char *)s,"%s.%d",FG.fname,identity);
558  s[i-3] = 's'; s[i-2] = 'c'; s[i-1] = '0';
559  AR.Fscr[0].name = (char *)s;
560  s = (UBYTE *)(FG.fname); i = 0;
561  while ( *s ) { s++; i++; }
562  s = (UBYTE *)Malloc1(sizeof(char)*(i+12),"name for Fscr[1] file");
563  sprintf((char *)s,"%s.%d",FG.fname,identity);
564  s[i-3] = 's'; s[i-2] = 'c'; s[i-1] = '1';
565  AR.Fscr[1].name = (char *)s;
566  }
567 
568  AR.CompressBuffer = (WORD *)Malloc1((AM.CompressSize+10)*sizeof(WORD),"compresssize");
569  AR.ComprTop = AR.CompressBuffer + AM.CompressSize;
570  AR.CompareRoutine = &Compare1;
571 /*
572  Here we make all allocations for the struct AT
573  (which is AB[identity].T or B->T with B = AB+identity).
574 */
575  AT.WorkSpace = (WORD *)Malloc1(AM.WorkSize*sizeof(WORD),"WorkSpace");
576  AT.WorkTop = AT.WorkSpace + AM.WorkSize;
577  AT.WorkPointer = AT.WorkSpace;
578 
579  AT.n_coef = (WORD *)Malloc1(sizeof(WORD)*4*AM.MaxTal+2,"maxnumbersize");
580  AT.n_llnum = AT.n_coef + 2*AM.MaxTal;
581 
582  AT.Nest = (NESTING)Malloc1((LONG)sizeof(struct NeStInG)*AM.maxFlevels,"functionlevels");
583  AT.NestStop = AT.Nest + AM.maxFlevels;
584  AT.NestPoin = AT.Nest;
585 
586  AT.WildMask = (WORD *)Malloc1((LONG)AM.MaxWildcards*sizeof(WORD),"maxwildcards");
587 
588  LOCK(availabilitylock);
589  AT.ebufnum = inicbufs(); /* Buffer for extras during execution */
590  AT.fbufnum = inicbufs(); /* Buffer for caching in factorization */
591  UNLOCK(availabilitylock);
592 
593  AT.RepCount = (int *)Malloc1((LONG)((AM.RepMax+3)*sizeof(int)),"repeat buffers");
594  AN.RepPoint = AT.RepCount;
595  AN.polysortflag = 0;
596  AN.subsubveto = 0;
597  AT.RepTop = AT.RepCount + AM.RepMax;
598 
599  AT.WildArgTaken = (WORD *)Malloc1((LONG)AC.WildcardBufferSize*sizeof(WORD)/2
600  ,"argument list names");
601  AT.WildcardBufferSize = AC.WildcardBufferSize;
602  AT.previousEfactor = 0;
603 
604  AT.identity = identity;
605  AT.LoadBalancing = 0;
606 /*
607  Still to do: the SS stuff.
608  the Fscr[3]
609  the FoStage4[2]
610 */
611  if ( AT.WorkSpace == 0 ||
612  AT.n_coef == 0 ||
613  AT.Nest == 0 ||
614  AT.WildMask == 0 ||
615  AT.RepCount == 0 ||
616  AT.WildArgTaken == 0 ) goto OnError;
617 /*
618  And initializations
619 */
620  AT.comsym[0] = 8;
621  AT.comsym[1] = SYMBOL;
622  AT.comsym[2] = 4;
623  AT.comsym[3] = 0;
624  AT.comsym[4] = 1;
625  AT.comsym[5] = 1;
626  AT.comsym[6] = 1;
627  AT.comsym[7] = 3;
628  AT.comnum[0] = 4;
629  AT.comnum[1] = 1;
630  AT.comnum[2] = 1;
631  AT.comnum[3] = 3;
632  AT.comfun[0] = FUNHEAD+4;
633  AT.comfun[1] = FUNCTION;
634  AT.comfun[2] = FUNHEAD;
635  AT.comfun[3] = 0;
636 #if FUNHEAD > 3
637  for ( i = 4; i <= FUNHEAD; i++ )
638  AT.comfun[i] = 0;
639 #endif
640  AT.comfun[FUNHEAD+1] = 1;
641  AT.comfun[FUNHEAD+2] = 1;
642  AT.comfun[FUNHEAD+3] = 3;
643  AT.comind[0] = 7;
644  AT.comind[1] = INDEX;
645  AT.comind[2] = 3;
646  AT.comind[3] = 0;
647  AT.comind[4] = 1;
648  AT.comind[5] = 1;
649  AT.comind[6] = 3;
650  AT.locwildvalue[0] = SUBEXPRESSION;
651  AT.locwildvalue[1] = SUBEXPSIZE;
652  for ( i = 2; i < SUBEXPSIZE; i++ ) AT.locwildvalue[i] = 0;
653  AT.mulpat[0] = TYPEMULT;
654  AT.mulpat[1] = SUBEXPSIZE+3;
655  AT.mulpat[2] = 0;
656  AT.mulpat[3] = SUBEXPRESSION;
657  AT.mulpat[4] = SUBEXPSIZE;
658  AT.mulpat[5] = 0;
659  AT.mulpat[6] = 1;
660  for ( i = 7; i < SUBEXPSIZE+5; i++ ) AT.mulpat[i] = 0;
661  AT.proexp[0] = SUBEXPSIZE+4;
662  AT.proexp[1] = EXPRESSION;
663  AT.proexp[2] = SUBEXPSIZE;
664  AT.proexp[3] = -1;
665  AT.proexp[4] = 1;
666  for ( i = 5; i < SUBEXPSIZE+1; i++ ) AT.proexp[i] = 0;
667  AT.proexp[SUBEXPSIZE+1] = 1;
668  AT.proexp[SUBEXPSIZE+2] = 1;
669  AT.proexp[SUBEXPSIZE+3] = 3;
670  AT.proexp[SUBEXPSIZE+4] = 0;
671  AT.dummysubexp[0] = SUBEXPRESSION;
672  AT.dummysubexp[1] = SUBEXPSIZE+4;
673  for ( i = 2; i < SUBEXPSIZE; i++ ) AT.dummysubexp[i] = 0;
674  AT.dummysubexp[SUBEXPSIZE] = WILDDUMMY;
675  AT.dummysubexp[SUBEXPSIZE+1] = 4;
676  AT.dummysubexp[SUBEXPSIZE+2] = 0;
677  AT.dummysubexp[SUBEXPSIZE+3] = 0;
678 
679  AT.MinVecArg[0] = 7+ARGHEAD;
680  AT.MinVecArg[ARGHEAD] = 7;
681  AT.MinVecArg[1+ARGHEAD] = INDEX;
682  AT.MinVecArg[2+ARGHEAD] = 3;
683  AT.MinVecArg[3+ARGHEAD] = 0;
684  AT.MinVecArg[4+ARGHEAD] = 1;
685  AT.MinVecArg[5+ARGHEAD] = 1;
686  AT.MinVecArg[6+ARGHEAD] = -3;
687  t = AT.FunArg;
688  *t++ = 4+ARGHEAD+FUNHEAD;
689  for ( i = 1; i < ARGHEAD; i++ ) *t++ = 0;
690  *t++ = 4+FUNHEAD;
691  *t++ = 0;
692  *t++ = FUNHEAD;
693  for ( i = 2; i < FUNHEAD; i++ ) *t++ = 0;
694  *t++ = 1; *t++ = 1; *t++ = 3;
695 
696  AT.inprimelist = -1;
697  AT.sizeprimelist = 0;
698  AT.primelist = 0;
699  AT.nfac = AT.nBer = 0;
700  AT.factorials = 0;
701  AT.bernoullis = 0;
702  AR.wranfia = 0;
703  AR.wranfcall = 0;
704  AR.wranfnpair1 = NPAIR1;
705  AR.wranfnpair2 = NPAIR2;
706  AR.wranfseed = 0;
707  AN.SplitScratch = 0;
708  AN.SplitScratchSize = AN.InScratch = 0;
709  AN.SplitScratch1 = 0;
710  AN.SplitScratchSize1 = AN.InScratch1 = 0;
711 /*
712  Now the sort buffers. They depend on which thread. The master
713  inherits the sortbuffer from AM.S0
714 */
715  if ( identity == 0 ) {
716  AT.S0 = AM.S0;
717  }
718  else {
719 /*
720  For the moment we don't have special settings.
721  They may become costly in virtual memory.
722 */
723  AT.S0 = AllocSort(AM.S0->LargeSize*sizeof(WORD)/numberofworkers
724  ,AM.S0->SmallSize*sizeof(WORD)/numberofworkers
725  ,AM.S0->SmallEsize*sizeof(WORD)/numberofworkers
726  ,AM.S0->TermsInSmall
727  ,AM.S0->MaxPatches
728 /* ,AM.S0->MaxPatches/numberofworkers */
729  ,AM.S0->MaxFpatches/numberofworkers
730  ,AM.S0->file.POsize);
731  }
732  AR.CompressPointer = AR.CompressBuffer;
733 /*
734  Install the store caches (15-aug-2006 JV)
735 */
736  AT.StoreCache = AT.StoreCacheAlloc = 0;
737  if ( AM.NumStoreCaches > 0 ) {
738  STORECACHE sa, sb;
739  LONG size;
740  size = sizeof(struct StOrEcAcHe)+AM.SizeStoreCache;
741  size = ((size-1)/sizeof(size_t)+1)*sizeof(size_t);
742  AT.StoreCacheAlloc = (STORECACHE)Malloc1(size*AM.NumStoreCaches,"StoreCaches");
743  sa = AT.StoreCache = AT.StoreCacheAlloc;
744  for ( i = 0; i < AM.NumStoreCaches; i++ ) {
745  sb = (STORECACHE)(VOID *)((UBYTE *)sa+size);
746  if ( i == AM.NumStoreCaches-1 ) {
747  sa->next = 0;
748  }
749  else {
750  sa->next = sb;
751  }
752  SETBASEPOSITION(sa->position,-1);
753  SETBASEPOSITION(sa->toppos,-1);
754  sa = sb;
755  }
756  }
757 
758  ReserveTempFiles(2);
759  return(B);
760 OnError:;
761  MLOCK(ErrorMessageLock);
762  MesPrint("Error initializing thread %d",identity);
763  MUNLOCK(ErrorMessageLock);
764  Terminate(-1);
765  return(B);
766 }
767 
768 /*
769  #] InitializeOneThread :
770  #[ FinalizeOneThread :
771 */
782 void FinalizeOneThread(int identity)
783 {
784  timerinfo[identity] = TimeCPU(1);
785 }
786 
787 /*
788  #] FinalizeOneThread :
789  #[ ClearAllThreads :
790 */
797 VOID ClearAllThreads()
798 {
799  int i;
800  MasterWaitAll();
801  for ( i = 1; i <= numberofworkers; i++ ) {
802  WakeupThread(i,CLEARCLOCK);
803  }
804 #ifdef WITHSORTBOTS
805  for ( i = numberofworkers+1; i <= numberofworkers+numberofsortbots; i++ ) {
806  WakeupThread(i,CLEARCLOCK);
807  }
808 #endif
809 }
810 
811 /*
812  #] ClearAllThreads :
813  #[ TerminateAllThreads :
814 */
821 VOID TerminateAllThreads()
822 {
823  int i;
824  for ( i = 1; i <= numberofworkers; i++ ) {
825  GetThread(i);
826  WakeupThread(i,TERMINATETHREAD);
827  }
828 #ifdef WITHSORTBOTS
829  for ( i = numberofworkers+1; i <= numberofworkers+numberofsortbots; i++ ) {
830  WakeupThread(i,TERMINATETHREAD);
831  }
832 #endif
833  for ( i = 1; i <= numberofworkers; i++ ) {
834  pthread_join(threadpointers[i],NULL);
835  }
836 #ifdef WITHSORTBOTS
837  for ( i = numberofworkers+1; i <= numberofworkers+numberofsortbots; i++ ) {
838  pthread_join(threadpointers[i],NULL);
839  }
840 #endif
841 }
842 
843 /*
844  #] TerminateAllThreads :
845  #[ MakeThreadBuckets :
846 */
872 int MakeThreadBuckets(int number, int par)
873 {
874  int i;
875  LONG sizethreadbuckets;
876  THREADBUCKET *thr;
877 /*
878  First we need a decent estimate. Not all terms should be maximal.
879 */
880  sizethreadbuckets = ( AC.ThreadBucketSize + 1 ) * AM.MaxTer + 2;
881  if ( AC.ThreadBucketSize >= 250 ) sizethreadbuckets /= 4;
882  else if ( AC.ThreadBucketSize >= 90 ) sizethreadbuckets /= 3;
883  else if ( AC.ThreadBucketSize >= 40 ) sizethreadbuckets /= 2;
884 
885  if ( par == 0 ) {
886  numthreadbuckets = 2*(number-1);
887  threadbuckets = (THREADBUCKET **)Malloc1(numthreadbuckets*sizeof(THREADBUCKET *),"threadbuckets");
888  freebuckets = (THREADBUCKET **)Malloc1(numthreadbuckets*sizeof(THREADBUCKET *),"threadbuckets");
889  }
890  if ( par > 0 ) {
891  if ( sizethreadbuckets <= threadbuckets[0]->threadbuffersize ) return(0);
892  for ( i = 0; i < numthreadbuckets; i++ ) {
893  thr = threadbuckets[i];
894  M_free(thr->deferbuffer,"deferbuffer");
895  }
896  }
897  else {
898  for ( i = 0; i < numthreadbuckets; i++ ) {
899  threadbuckets[i] = (THREADBUCKET *)Malloc1(sizeof(THREADBUCKET),"threadbuckets");
900  threadbuckets[i]->lock = dummylock;
901  }
902  }
903  for ( i = 0; i < numthreadbuckets; i++ ) {
904  thr = threadbuckets[i];
905  thr->threadbuffersize = sizethreadbuckets;
906  thr->free = BUCKETFREE;
907  thr->deferbuffer = (POSITION *)Malloc1(2*sizethreadbuckets*sizeof(WORD)
908  +(AC.ThreadBucketSize+1)*sizeof(POSITION),"deferbuffer");
909  thr->threadbuffer = (WORD *)(thr->deferbuffer+AC.ThreadBucketSize+1);
910  thr->compressbuffer = (WORD *)(thr->threadbuffer+sizethreadbuckets);
911  thr->busy = BUCKETPREPARINGTERM;
912  thr->usenum = thr->totnum = 0;
913  thr->type = BUCKETDOINGTERMS;
914  }
915  return(0);
916 }
917 
918 /*
919  #] MakeThreadBuckets :
920  #[ GetTimerInfo :
921 */
922 
928 int GetTimerInfo(LONG** ti,LONG** sti)
929 {
930  *ti = timerinfo;
931  *sti = sumtimerinfo;
932 #ifdef WITHSORTBOTS
933  return AM.totalnumberofthreads*2;
934 #else
935  return AM.totalnumberofthreads;
936 #endif
937 }
938 
939 /*
940  #] GetTimerInfo :
941  #[ WriteTimerInfo :
942 */
943 
948 void WriteTimerInfo(LONG* ti,LONG* sti)
949 {
950  int i;
951 #ifdef WITHSORTBOTS
952  int max = AM.totalnumberofthreads*2;
953 #else
954  int max = AM.totalnumberofthreads;
955 #endif
956  for ( i=0; i<max; ++i ) {
957  timerinfo[i] = ti[i];
958  sumtimerinfo[i] = sti[i];
959  }
960 }
961 
962 /*
963  #] WriteTimerInfo :
964  #[ GetWorkerTimes :
965 */
971 LONG GetWorkerTimes()
972 {
973  LONG retval = 0;
974  int i;
975  for ( i = 1; i <= numberofworkers; i++ ) retval += timerinfo[i] + sumtimerinfo[i];
976 #ifdef WITHSORTBOTS
977  for ( i = numberofworkers+1; i <= numberofworkers+numberofsortbots; i++ )
978  retval += timerinfo[i] + sumtimerinfo[i];
979 #endif
980  return(retval);
981 }
982 
983 /*
984  #] GetWorkerTimes :
985  #[ UpdateOneThread :
986 */
993 int UpdateOneThread(int identity)
994 {
995  ALLPRIVATES *B = AB[identity], *B0 = AB[0];
996  AR.GetFile = AR0.GetFile;
997  AR.KeptInHold = AR0.KeptInHold;
998  AR.CurExpr = AR0.CurExpr;
999  AR.SortType = AC.SortType;
1000  if ( AT.WildcardBufferSize < AC.WildcardBufferSize ) {
1001  M_free(AT.WildArgTaken,"argument list names");
1002  AT.WildcardBufferSize = AC.WildcardBufferSize;
1003  AT.WildArgTaken = (WORD *)Malloc1((LONG)AC.WildcardBufferSize*sizeof(WORD)/2
1004  ,"argument list names");
1005  if ( AT.WildArgTaken == 0 ) return(-1);
1006  }
1007  return(0);
1008 }
1009 
1010 /*
1011  #] UpdateOneThread :
1012  #[ LoadOneThread :
1013 */
1027 int LoadOneThread(int from, int identity, THREADBUCKET *thr, int par)
1028 {
1029  WORD *t1, *t2;
1030  ALLPRIVATES *B = AB[identity], *B0 = AB[from];
1031 
1032  AR.DefPosition = AR0.DefPosition;
1033  AR.NoCompress = AR0.NoCompress;
1034  AR.gzipCompress = AR0.gzipCompress;
1035  AR.BracketOn = AR0.BracketOn;
1036  AR.CurDum = AR0.CurDum;
1037  AR.DeferFlag = AR0.DeferFlag;
1038  AR.TePos = 0;
1039  AR.sLevel = AR0.sLevel;
1040  AR.Stage4Name = AR0.Stage4Name;
1041  AR.GetOneFile = AR0.GetOneFile;
1042  AR.PolyFun = AR0.PolyFun;
1043  AR.PolyFunType = AR0.PolyFunType;
1044  AR.Eside = AR0.Eside;
1045  AR.Cnumlhs = AR0.Cnumlhs;
1046 /*
1047  AR.MaxBracket = AR0.MaxBracket;
1048 
1049  The compressbuffer contents are mainly relevant for keep brackets
1050  We should do this only if there is a keep brackets statement
1051  We may however still need the compressbuffer for expressions in the rhs.
1052 */
1053  if ( par >= 1 ) {
1054 /*
1055  We may not need this %%%%% 7-apr-2006
1056 */
1057  t1 = AR.CompressBuffer; t2 = AR0.CompressBuffer;
1058  while ( t2 < AR0.CompressPointer ) *t1++ = *t2++;
1059  AR.CompressPointer = t1;
1060 
1061  }
1062  else {
1063  AR.CompressPointer = AR.CompressBuffer;
1064  }
1065  if ( AR.DeferFlag ) {
1066  if ( AR.infile->handle < 0 ) {
1067  AR.infile->POfill = AR0.infile->POfill;
1068  }
1069  else {
1070 /*
1071  We have to set the value of POposition to something that will
1072  force a read in the first try.
1073 */
1074  AR.infile->POfull = AR.infile->POfill = AR.infile->PObuffer;
1075  }
1076  }
1077  if ( par == 0 ) {
1078  AN.threadbuck = thr;
1079  AN.ninterms = thr->firstterm;
1080  }
1081  else if ( par == 1 ) {
1082  WORD *tstop;
1083  t1 = thr->threadbuffer; tstop = t1 + *t1;
1084  t2 = AT.WorkPointer;
1085  while ( t1 < tstop ) *t2++ = *t1++;
1086  AN.ninterms = thr->firstterm;
1087  }
1088  AN.TeInFun = 0;
1089  AN.ncmod = AC.ncmod;
1090  AT.BrackBuf = AT0.BrackBuf;
1091  AT.bracketindexflag = AT0.bracketindexflag;
1092 /*
1093  The relevant variables and the term are in their place.
1094  There is nothing more to do.
1095 */
1096  return(0);
1097 }
1098 
1099 /*
1100  #] LoadOneThread :
1101  #[ BalanceRunThread :
1102 */
1118 int BalanceRunThread(PHEAD int identity, WORD *term, WORD level)
1119 {
1120  GETBIDENTITY
1121  ALLPRIVATES *BB;
1122  WORD *t, *tt;
1123  int i, *ti, *tti;
1124 
1125  LoadOneThread(AT.identity,identity,0,2);
1126 /*
1127  Extra loading if needed. Quantities changed in Generator.
1128  Like the level that has to be passed.
1129 */
1130  BB = AB[identity];
1131  BB->R.level = level;
1132  BB->T.TMbuff = AT.TMbuff;
1133  ti = AT.RepCount; tti = BB->T.RepCount;
1134  i = AN.RepPoint - AT.RepCount;
1135  BB->N.RepPoint = BB->T.RepCount + i;
1136  for ( ; i >= 0; i-- ) tti[i] = ti[i];
1137 
1138  t = term; i = *term;
1139  tt = BB->T.WorkSpace;
1140  NCOPY(tt,t,i);
1141  BB->T.WorkPointer = tt;
1142 
1143  WakeupThread(identity,HIGHERLEVELGENERATION);
1144 
1145  return(0);
1146 }
1147 
1148 /*
1149  #] BalanceRunThread :
1150  #[ SetWorkerFiles :
1151 */
1156 void SetWorkerFiles()
1157 {
1158  int id;
1159  ALLPRIVATES *B, *B0 = AB[0];
1160  for ( id = 1; id < AM.totalnumberofthreads; id++ ) {
1161  B = AB[id];
1162  AR.infile = &(AR.Fscr[0]);
1163  AR.outfile = &(AR.Fscr[1]);
1164  AR.hidefile = &(AR.Fscr[2]);
1165  AR.infile->handle = AR0.infile->handle;
1166  AR.hidefile->handle = AR0.hidefile->handle;
1167  if ( AR.infile->handle < 0 ) {
1168  AR.infile->PObuffer = AR0.infile->PObuffer;
1169  AR.infile->POstop = AR0.infile->POstop;
1170  AR.infile->POfill = AR0.infile->POfill;
1171  AR.infile->POfull = AR0.infile->POfull;
1172  AR.infile->POsize = AR0.infile->POsize;
1173  AR.InInBuf = AR0.InInBuf;
1174  AR.infile->POposition = AR0.infile->POposition;
1175  AR.infile->filesize = AR0.infile->filesize;
1176  }
1177  else {
1178  AR.infile->PObuffer = AR.infile->wPObuffer;
1179  AR.infile->POstop = AR.infile->wPOstop;
1180  AR.infile->POfill = AR.infile->wPOfill;
1181  AR.infile->POfull = AR.infile->wPOfull;
1182  AR.infile->POsize = AR.infile->wPOsize;
1183  AR.InInBuf = 0;
1184  PUTZERO(AR.infile->POposition);
1185  }
1186 /*
1187  If there is some writing, it betters happens to ones own outfile.
1188  Currently this is to be done only for InParallel.
1189  Merging of the outputs is then done by the CopyExpression routine.
1190 */
1191  {
1192  AR.outfile->PObuffer = AR.outfile->wPObuffer;
1193  AR.outfile->POstop = AR.outfile->wPOstop;
1194  AR.outfile->POfill = AR.outfile->wPOfill;
1195  AR.outfile->POfull = AR.outfile->wPOfull;
1196  AR.outfile->POsize = AR.outfile->wPOsize;
1197  PUTZERO(AR.outfile->POposition);
1198  }
1199  if ( AR.hidefile->handle < 0 ) {
1200  AR.hidefile->PObuffer = AR0.hidefile->PObuffer;
1201  AR.hidefile->POstop = AR0.hidefile->POstop;
1202  AR.hidefile->POfill = AR0.hidefile->POfill;
1203  AR.hidefile->POfull = AR0.hidefile->POfull;
1204  AR.hidefile->POsize = AR0.hidefile->POsize;
1205  AR.InHiBuf = AR0.InHiBuf;
1206  AR.hidefile->POposition = AR0.hidefile->POposition;
1207  AR.hidefile->filesize = AR0.hidefile->filesize;
1208  }
1209  else {
1210  AR.hidefile->PObuffer = AR.hidefile->wPObuffer;
1211  AR.hidefile->POstop = AR.hidefile->wPOstop;
1212  AR.hidefile->POfill = AR.hidefile->wPOfill;
1213  AR.hidefile->POfull = AR.hidefile->wPOfull;
1214  AR.hidefile->POsize = AR.hidefile->wPOsize;
1215  AR.InHiBuf = 0;
1216  PUTZERO(AR.hidefile->POposition);
1217  }
1218  }
1219  if ( AR0.StoreData.dirtyflag ) {
1220  for ( id = 1; id < AM.totalnumberofthreads; id++ ) {
1221  B = AB[id];
1222  AR.StoreData = AR0.StoreData;
1223  }
1224  }
1225 }
1226 
1227 /*
1228  #] SetWorkerFiles :
1229  #[ RunThread :
1230 */
1238 void *RunThread(void *dummy)
1239 {
1240  WORD *term, *ttin, *tt, *ttco, *oldwork;
1241  int identity, wakeupsignal, identityretv, i, tobereleased, errorcode;
1242  ALLPRIVATES *B;
1243  THREADBUCKET *thr;
1244  POSITION *ppdef;
1245  EXPRESSIONS e;
1246  DUMMYUSE(dummy);
1247  identity = SetIdentity(&identityretv);
1248  threadpointers[identity] = pthread_self();
1249  B = InitializeOneThread(identity);
1250  while ( ( wakeupsignal = ThreadWait(identity) ) > 0 ) {
1251  switch ( wakeupsignal ) {
1252 /*
1253  #[ STARTNEWEXPRESSION :
1254 */
1255  case STARTNEWEXPRESSION:
1256 /*
1257  Set up the sort routines etc.
1258  Start with getting some buffers synchronized with the compiler
1259 */
1260  if ( UpdateOneThread(identity) ) {
1261  MLOCK(ErrorMessageLock);
1262  MesPrint("Update error in starting expression in thread %d in module %d",identity,AC.CModule);
1263  MUNLOCK(ErrorMessageLock);
1264  Terminate(-1);
1265  }
1266  AR.DeferFlag = AC.ComDefer;
1267  AR.sLevel = AS.sLevel;
1268  AR.MaxDum = AM.IndDum;
1269  AR.expchanged = AB[0]->R.expchanged;
1270  AR.expflags = AB[0]->R.expflags;
1271 /*
1272  Now fire up the sort buffer.
1273 */
1274  NewSort(BHEAD0);
1275  break;
1276 /*
1277  #] STARTNEWEXPRESSION :
1278  #[ LOWESTLEVELGENERATION :
1279 */
1280  case LOWESTLEVELGENERATION:
1281  e = Expressions + AR.CurExpr;
1282  thr = AN.threadbuck;
1283  ppdef = thr->deferbuffer;
1284  ttin = thr->threadbuffer;
1285  ttco = thr->compressbuffer;
1286  term = AT.WorkPointer;
1287  thr->usenum = 0;
1288  tobereleased = 0;
1289  AN.inputnumber = thr->firstterm;
1290  AN.ninterms = thr->firstterm;
1291  do {
1292  thr->usenum++; /* For if the master wants to steal the bucket */
1293  tt = term; i = *ttin;
1294  NCOPY(tt,ttin,i);
1295  AT.WorkPointer = tt;
1296  if ( AR.DeferFlag ) {
1297  tt = AR.CompressBuffer; i = *ttco;
1298  NCOPY(tt,ttco,i);
1299  AR.CompressPointer = tt;
1300  AR.DefPosition = ppdef[0]; ppdef++;
1301  }
1302  if ( thr->free == BUCKETTERMINATED ) {
1303 /*
1304  The next statement allows the master to steal the bucket
1305  for load balancing purposes. We do still execute the current
1306  term, but afterwards we drop out.
1307  Once we have written the release code, we cannot use this
1308  bucket anymore. Hence the exit to the label bucketstolen.
1309 */
1310  if ( thr->usenum == thr->totnum ) {
1311  thr->free = BUCKETCOMINGFREE;
1312  }
1313  else {
1314  thr->free = BUCKETRELEASED;
1315  tobereleased = 1;
1316  }
1317  }
1318 /*
1319  What if we want to steal and we set thr->free while
1320  the thread is inside the next code for a long time?
1321  if ( AT.LoadBalancing ) {
1322 */
1323  LOCK(thr->lock);
1324  thr->busy = BUCKETDOINGTERM;
1325  UNLOCK(thr->lock);
1326 /*
1327  }
1328  else {
1329  thr->busy = BUCKETDOINGTERM;
1330  }
1331 */
1332  AN.RepPoint = AT.RepCount + 1;
1333 
1334  if ( ( e->vflags & ISFACTORIZED ) != 0 && term[1] == HAAKJE ) {
1335  StoreTerm(BHEAD term);
1336  }
1337  else {
1338  if ( AR.DeferFlag ) {
1339  AR.CurDum = AN.IndDum = Expressions[AR.CurExpr].numdummies + AM.IndDum;
1340  }
1341  else {
1342  AN.IndDum = AM.IndDum;
1343  AR.CurDum = ReNumber(BHEAD term);
1344  }
1345  if ( AC.SymChangeFlag ) MarkDirty(term,DIRTYSYMFLAG);
1346  if ( AN.ncmod ) {
1347  if ( ( AC.modmode & ALSOFUNARGS ) != 0 ) MarkDirty(term,DIRTYFLAG);
1348  else if ( AR.PolyFun ) PolyFunDirty(BHEAD term);
1349  }
1350  if ( ( AP.PreDebug & THREADSDEBUG ) != 0 ) {
1351  MLOCK(ErrorMessageLock);
1352  MesPrint("Thread %w executing term:");
1353  PrintTerm(term,"LLG");
1354  MUNLOCK(ErrorMessageLock);
1355  }
1356  if ( ( AR.PolyFunType == 2 ) && ( AC.PolyRatFunChanged == 0 )
1357  && ( e->status == LOCALEXPRESSION || e->status == GLOBALEXPRESSION ) ) {
1358  PolyFunClean(BHEAD term);
1359  }
1360  if ( Generator(BHEAD term,0) ) {
1361  LowerSortLevel();
1362  MLOCK(ErrorMessageLock);
1363  MesPrint("Error in processing one term in thread %d in module %d",identity,AC.CModule);
1364  MUNLOCK(ErrorMessageLock);
1365  Terminate(-1);
1366  }
1367  AN.ninterms++;
1368  }
1369 /* if ( AT.LoadBalancing ) { */
1370  LOCK(thr->lock);
1371  thr->busy = BUCKETPREPARINGTERM;
1372  UNLOCK(thr->lock);
1373 /*
1374  }
1375  else {
1376  thr->busy = BUCKETPREPARINGTERM;
1377  }
1378 */
1379  if ( thr->free == BUCKETTERMINATED ) {
1380  if ( thr->usenum == thr->totnum ) {
1381  thr->free = BUCKETCOMINGFREE;
1382  }
1383  else {
1384  thr->free = BUCKETRELEASED;
1385  tobereleased = 1;
1386  }
1387  }
1388  if ( tobereleased ) goto bucketstolen;
1389  } while ( *ttin );
1390  thr->free = BUCKETCOMINGFREE;
1391 bucketstolen:;
1392 /* if ( AT.LoadBalancing ) { */
1393  LOCK(thr->lock);
1394  thr->busy = BUCKETTOBERELEASED;
1395  UNLOCK(thr->lock);
1396 /* }
1397  else {
1398  thr->busy = BUCKETTOBERELEASED;
1399  }
1400 */
1401  AT.WorkPointer = term;
1402  break;
1403 /*
1404  #] LOWESTLEVELGENERATION :
1405  #[ FINISHEXPRESSION :
1406 */
1407 #ifdef WITHSORTBOTS
1408  case CLAIMOUTPUT:
1409  LOCK(AT.SB.MasterBlockLock[1]);
1410  break;
1411 #endif
1412  case FINISHEXPRESSION:
1413 /*
1414  Finish the sort
1415 
1416  Start with claiming the first block
1417  Once we have claimed it we can let the master know that
1418  everything is all right.
1419 */
1420  LOCK(AT.SB.MasterBlockLock[1]);
1421  ThreadClaimedBlock(identity);
1422 /*
1423  Entry for when we work with sortbots
1424 */
1425 #ifdef WITHSORTBOTS
1426  case FINISHEXPRESSION2:
1427 #endif
1428 /*
1429  Now we may need here an fsync on the sort file
1430 */
1431  if ( AC.ThreadSortFileSynch ) {
1432  if ( AT.S0->file.handle >= 0 ) {
1433  SynchFile(AT.S0->file.handle);
1434  }
1435  }
1436  AT.SB.FillBlock = 1;
1437  AT.SB.MasterFill[1] = AT.SB.MasterStart[1];
1438  errorcode = EndSort(BHEAD AT.S0->sBuffer,0);
1439  UNLOCK(AT.SB.MasterBlockLock[AT.SB.FillBlock]);
1440  UpdateMaxSize();
1441  if ( errorcode ) {
1442  MLOCK(ErrorMessageLock);
1443  MesPrint("Error terminating sort in thread %d in module %d",identity,AC.CModule);
1444  MUNLOCK(ErrorMessageLock);
1445  Terminate(-1);
1446  }
1447  break;
1448 /*
1449  #] FINISHEXPRESSION :
1450  #[ CLEANUPEXPRESSION :
1451 */
1452  case CLEANUPEXPRESSION:
1453 /*
1454  Cleanup everything and wait for the next expression
1455 */
1456  if ( AR.outfile->handle >= 0 ) {
1457  CloseFile(AR.outfile->handle);
1458  AR.outfile->handle = -1;
1459  remove(AR.outfile->name);
1460  AR.outfile->POfill = AR.outfile->POfull = AR.outfile->PObuffer;
1461  PUTZERO(AR.outfile->POposition);
1462  PUTZERO(AR.outfile->filesize);
1463  }
1464  else {
1465  AR.outfile->POfill = AR.outfile->POfull = AR.outfile->PObuffer;
1466  PUTZERO(AR.outfile->POposition);
1467  PUTZERO(AR.outfile->filesize);
1468  }
1469  {
1470  CBUF *C = cbuf+AT.ebufnum;
1471  WORD **w, ii;
1472  if ( C->numrhs > 0 || C->numlhs > 0 ) {
1473  if ( C->rhs ) {
1474  w = C->rhs; ii = C->numrhs;
1475  do { *w++ = 0; } while ( --ii > 0 );
1476  }
1477  if ( C->lhs ) {
1478  w = C->lhs; ii = C->numlhs;
1479  do { *w++ = 0; } while ( --ii > 0 );
1480  }
1481  C->numlhs = C->numrhs = 0;
1482  ClearTree(AT.ebufnum);
1483  C->Pointer = C->Buffer;
1484  }
1485  }
1486  break;
1487 /*
1488  #] CLEANUPEXPRESSION :
1489  #[ HIGHERLEVELGENERATION :
1490 */
1491  case HIGHERLEVELGENERATION:
1492 /*
1493  When foliating halfway the tree.
1494  This should only be needed in a second level load balancing
1495 */
1496  term = AT.WorkSpace; AT.WorkPointer = term + *term;
1497  if ( Generator(BHEAD term,AR.level) ) {
1498  LowerSortLevel();
1499  MLOCK(ErrorMessageLock);
1500  MesPrint("Error in load balancing one term at level %d in thread %d in module %d",AR.level,AT.identity,AC.CModule);
1501  MUNLOCK(ErrorMessageLock);
1502  Terminate(-1);
1503  }
1504  AT.WorkPointer = term;
1505  break;
1506 /*
1507  #] HIGHERLEVELGENERATION :
1508  #[ STARTNEWMODULE :
1509 */
1510  case STARTNEWMODULE:
1511 /*
1512  For resetting variables.
1513 */
1514  SpecialCleanup(B);
1515  break;
1516 /*
1517  #] STARTNEWMODULE :
1518  #[ TERMINATETHREAD :
1519 */
1520  case TERMINATETHREAD:
1521  goto EndOfThread;
1522 /*
1523  #] TERMINATETHREAD :
1524  #[ DOONEEXPRESSION :
1525 
1526  When a thread has to do a complete (not too big) expression.
1527  The number of the expression to be done is in AR.exprtodo.
1528  The code is mostly taken from Processor. The only difference
1529  is with what to do with the output.
1530  The output should go to the scratch buffer of the worker
1531  (which is free at the right moment). If this buffer is too
1532  small we have a problem. We could write to file or give the
1533  master what we have and from now on the master has to collect
1534  pieces until things are complete.
1535  Note: this assumes that the expressions don't keep their order.
1536  If they have to keep their order, don't use this feature.
1537 */
1538  case DOONEEXPRESSION: {
1539 
1540  POSITION position, outposition;
1541  FILEHANDLE *fi, *fout, *oldoutfile;
1542  LONG dd = 0;
1543  WORD oldBracketOn = AR.BracketOn;
1544  WORD *oldBrackBuf = AT.BrackBuf;
1545  WORD oldbracketindexflag = AT.bracketindexflag;
1546  e = Expressions + AR.exprtodo;
1547  i = AR.exprtodo;
1548  AR.CurExpr = i;
1549  AR.SortType = AC.SortType;
1550  AR.expchanged = 0;
1551  if ( ( e->vflags & ISFACTORIZED ) != 0 ) {
1552  AR.BracketOn = 1;
1553  AT.BrackBuf = AM.BracketFactors;
1554  AT.bracketindexflag = 1;
1555  }
1556 
1557  position = AS.OldOnFile[i];
1558  if ( e->status == HIDDENLEXPRESSION || e->status == HIDDENGEXPRESSION ) {
1559  AR.GetFile = 2; fi = AR.hidefile;
1560  }
1561  else {
1562  AR.GetFile = 0; fi = AR.infile;
1563  }
1564 /*
1565  PUTZERO(fi->POposition);
1566  if ( fi->handle >= 0 ) {
1567  fi->POfill = fi->POfull = fi->PObuffer;
1568  }
1569 */
1570  SetScratch(fi,&position);
1571  term = oldwork = AT.WorkPointer;
1572  AR.CompressPointer = AR.CompressBuffer;
1573  AR.CompressPointer[0] = 0;
1574  AR.KeptInHold = 0;
1575  if ( GetTerm(BHEAD term) <= 0 ) {
1576  MLOCK(ErrorMessageLock);
1577  MesPrint("Expression %d has problems in scratchfile (t)",i);
1578  MUNLOCK(ErrorMessageLock);
1579  Terminate(-1);
1580  }
1581  if ( AT.bracketindexflag > 0 ) OpenBracketIndex(i);
1582  term[3] = i;
1583  PUTZERO(outposition);
1584  fout = AR.outfile;
1585  fout->POfill = fout->POfull = fout->PObuffer;
1586  fout->POposition = outposition;
1587  if ( fout->handle >= 0 ) {
1588  fout->POposition = outposition;
1589  }
1590 /*
1591  The next statement is needed because we need the system
1592  to believe that the expression is at position zero for
1593  the moment. In this worker, with no memory of other expressions,
1594  it is. This is needed for when a bracket index is made
1595  because there e->onfile is an offset. Afterwards, when the
1596  expression is written to its final location in the masters
1597  output e->onfile will get its real value.
1598 */
1599  PUTZERO(e->onfile);
1600  if ( PutOut(BHEAD term,&outposition,fout,0) < 0 ) goto ProcErr;
1601 
1602  AR.DeferFlag = AC.ComDefer;
1603 
1604  AR.sLevel = AB[0]->R.sLevel;
1605  term = AT.WorkPointer;
1606  NewSort(BHEAD0);
1607  AR.MaxDum = AM.IndDum;
1608  AN.ninterms = 0;
1609  while ( GetTerm(BHEAD term) ) {
1610  SeekScratch(fi,&position);
1611  AN.ninterms++; dd = AN.deferskipped;
1612  if ( ( e->vflags & ISFACTORIZED ) != 0 && term[1] == HAAKJE ) {
1613  StoreTerm(BHEAD term);
1614  }
1615  else {
1616  if ( AC.CollectFun && *term <= (AM.MaxTer/(2*(LONG)sizeof(WORD))) ) {
1617  if ( GetMoreTerms(term) < 0 ) {
1618  LowerSortLevel(); goto ProcErr;
1619  }
1620  SeekScratch(fi,&position);
1621  }
1622  AT.WorkPointer = term + *term;
1623  AN.RepPoint = AT.RepCount + 1;
1624  if ( AR.DeferFlag ) {
1625  AR.CurDum = AN.IndDum = Expressions[AR.exprtodo].numdummies;
1626  }
1627  else {
1628  AN.IndDum = AM.IndDum;
1629  AR.CurDum = ReNumber(BHEAD term);
1630  }
1631  if ( AC.SymChangeFlag ) MarkDirty(term,DIRTYSYMFLAG);
1632  if ( AN.ncmod ) {
1633  if ( ( AC.modmode & ALSOFUNARGS ) != 0 ) MarkDirty(term,DIRTYFLAG);
1634  else if ( AR.PolyFun ) PolyFunDirty(BHEAD term);
1635  }
1636  if ( ( AR.PolyFunType == 2 ) && ( AC.PolyRatFunChanged == 0 )
1637  && ( e->status == LOCALEXPRESSION || e->status == GLOBALEXPRESSION ) ) {
1638  PolyFunClean(BHEAD term);
1639  }
1640  if ( Generator(BHEAD term,0) ) {
1641  LowerSortLevel(); goto ProcErr;
1642  }
1643  AN.ninterms += dd;
1644  }
1645  SetScratch(fi,&position);
1646  if ( fi == AR.hidefile ) {
1647  AR.InHiBuf = (fi->POfull-fi->PObuffer)
1648  -DIFBASE(position,fi->POposition)/sizeof(WORD);
1649  }
1650  else {
1651  AR.InInBuf = (fi->POfull-fi->PObuffer)
1652  -DIFBASE(position,fi->POposition)/sizeof(WORD);
1653  }
1654  }
1655  AN.ninterms += dd;
1656  if ( EndSort(BHEAD AT.S0->sBuffer,0) < 0 ) goto ProcErr;
1657  e->numdummies = AR.MaxDum - AM.IndDum;
1658  AR.BracketOn = oldBracketOn;
1659  AT.BrackBuf = oldBrackBuf;
1660  if ( ( e->vflags & TOBEFACTORED ) != 0 )
1661  poly_factorize_expression(e);
1662  else if ( ( ( e->vflags & TOBEUNFACTORED ) != 0 )
1663  && ( ( e->vflags & ISFACTORIZED ) != 0 ) )
1664  poly_unfactorize_expression(e);
1665  if ( AT.S0->TermsLeft ) e->vflags &= ~ISZERO;
1666  else e->vflags |= ISZERO;
1667  if ( AR.expchanged == 0 ) e->vflags |= ISUNMODIFIED;
1668  if ( AT.S0->TermsLeft ) AR.expflags |= ISZERO;
1669  if ( AR.expchanged ) AR.expflags |= ISUNMODIFIED;
1670  AR.GetFile = 0;
1671  AT.bracketindexflag = oldbracketindexflag;
1672 /*
1673  Now copy the whole thing from fout to AR0.outfile
1674  Do this in one go to keep the lock occupied as short as possible
1675 */
1676  SeekScratch(fout,&outposition);
1677  LOCK(AS.outputslock);
1678  oldoutfile = AB[0]->R.outfile;
1679  if ( e->status == INTOHIDELEXPRESSION || e->status == INTOHIDEGEXPRESSION ) {
1680  AB[0]->R.outfile = AB[0]->R.hidefile;
1681  }
1682  SeekScratch(AB[0]->R.outfile,&position);
1683  e->onfile = position;
1684  if ( CopyExpression(fout,AB[0]->R.outfile) < 0 ) {
1685  AB[0]->R.outfile = oldoutfile;
1686  UNLOCK(AS.outputslock);
1687  MLOCK(ErrorMessageLock);
1688  MesPrint("Error copying output of 'InParallel' expression to master. Thread: %d",identity);
1689  MUNLOCK(ErrorMessageLock);
1690  goto ProcErr;
1691  }
1692  AB[0]->R.outfile = oldoutfile;
1693  AB[0]->R.hidefile->POfull = AB[0]->R.hidefile->POfill;
1694  UNLOCK(AS.outputslock);
1695 
1696  if ( fout->handle >= 0 ) { /* Now get rid of the file */
1697  CloseFile(fout->handle);
1698  fout->handle = -1;
1699  remove(fout->name);
1700  PUTZERO(fout->POposition);
1701  PUTZERO(fout->filesize);
1702  fout->POfill = fout->POfull = fout->PObuffer;
1703  }
1704  UpdateMaxSize();
1705 
1706  AT.WorkPointer = oldwork;
1707 
1708  } break;
1709 /*
1710  #] DOONEEXPRESSION :
1711  #[ DOBRACKETS :
1712 
1713  In case we have a bracket index we can have the worker treat
1714  one or more of the entries in the bracket index.
1715  The advantage is that identical terms will meet each other
1716  sooner in the sorting and hence fewer compares will be needed.
1717  Also this way the master doesn't need to fill the buckets.
1718  The main problem is the load balancing which can become very
1719  bad when there is a long tail without things outside the bracket.
1720 
1721  We get sent:
1722  1: The number of the first bracket to be done
1723  2: The number of the last bracket to be done
1724 */
1725  case DOBRACKETS: {
1726  BRACKETINFO *binfo;
1727  BRACKETINDEX *bi;
1728  FILEHANDLE *fi;
1729  POSITION stoppos,where;
1730  e = Expressions + AR.CurExpr;
1731  binfo = e->bracketinfo;
1732  thr = AN.threadbuck;
1733  bi = &(binfo->indexbuffer[thr->firstbracket]);
1734  if ( AR.GetFile == 2 ) fi = AR.hidefile;
1735  else fi = AR.infile;
1736  where = bi->start;
1737  ADD2POS(where,AS.OldOnFile[AR.CurExpr]);
1738  SetScratch(fi,&(where));
1739  stoppos = binfo->indexbuffer[thr->lastbracket].next;
1740  ADD2POS(stoppos,AS.OldOnFile[AR.CurExpr]);
1741  AN.ninterms = thr->firstterm;
1742 /*
1743  Now we have to put the 'value' of the bracket in the
1744  Compress buffer.
1745 */
1746  ttco = AR.CompressBuffer;
1747  tt = binfo->bracketbuffer + bi->bracket;
1748  i = *tt;
1749  NCOPY(ttco,tt,i)
1750  AR.CompressPointer = ttco;
1751  term = AT.WorkPointer;
1752  while ( GetTerm(BHEAD term) ) {
1753  AT.WorkPointer = term + *term;
1754  AN.IndDum = AM.IndDum;
1755  AR.CurDum = ReNumber(BHEAD term);
1756  if ( AC.SymChangeFlag ) MarkDirty(term,DIRTYSYMFLAG);
1757  if ( AN.ncmod ) {
1758  if ( ( AC.modmode & ALSOFUNARGS ) != 0 ) MarkDirty(term,DIRTYFLAG);
1759  else if ( AR.PolyFun ) PolyFunDirty(BHEAD term);
1760  }
1761  if ( ( AR.PolyFunType == 2 ) && ( AC.PolyRatFunChanged == 0 )
1762  && ( e->status == LOCALEXPRESSION || e->status == GLOBALEXPRESSION ) ) {
1763  PolyFunClean(BHEAD term);
1764  }
1765  if ( ( AP.PreDebug & THREADSDEBUG ) != 0 ) {
1766  MLOCK(ErrorMessageLock);
1767  MesPrint("Thread %w executing term:");
1768  PrintTerm(term,"DoBrackets");
1769  MUNLOCK(ErrorMessageLock);
1770  }
1771  AT.WorkPointer = term + *term;
1772  if ( Generator(BHEAD term,0) ) {
1773  LowerSortLevel();
1774  MLOCK(ErrorMessageLock);
1775  MesPrint("Error in processing one term in thread %d in module %d",identity,AC.CModule);
1776  MUNLOCK(ErrorMessageLock);
1777  Terminate(-1);
1778  }
1779  AN.ninterms++;
1780  SeekScratch(fi,&where);
1781  if ( ISGEPOS(where,stoppos) ) break;
1782  }
1783  AT.WorkPointer = term;
1784  thr->free = BUCKETCOMINGFREE;
1785  break;
1786  }
1787 /*
1788  #] DOBRACKETS :
1789  #[ CLEARCLOCK :
1790 
1791  The program only comes here after a .clear
1792 */
1793  case CLEARCLOCK:
1794 /* LOCK(clearclocklock); */
1795  sumtimerinfo[identity] += TimeCPU(1);
1796  timerinfo[identity] = TimeCPU(0);
1797 /* UNLOCK(clearclocklock); */
1798  break;
1799 /*
1800  #] CLEARCLOCK :
1801 */
1802  case MCTSEXPANDTREE:
1803  find_Horner_MCTS_expand_tree();
1804  break;
1805 
1806  case OPTIMIZEEXPRESSION:
1807  optimize_expression_given_Horner();
1808  break;
1809 
1810  default:
1811  MLOCK(ErrorMessageLock);
1812  MesPrint("Illegal wakeup signal %d for thread %d",wakeupsignal,identity);
1813  MUNLOCK(ErrorMessageLock);
1814  Terminate(-1);
1815  break;
1816  }
1817  /* we need the following update in case we are using checkpoints. then we
1818  need to readjust the clocks when recovering using this information */
1819  timerinfo[identity] = TimeCPU(1);
1820  }
1821 EndOfThread:;
1822 /*
1823  This is the end of the thread. We cleanup and exit.
1824 */
1825  FinalizeOneThread(identity);
1826  return(0);
1827 ProcErr:
1828  Terminate(-1);
1829  return(0);
1830 }
1831 
1832 /*
1833  #] RunThread :
1834  #[ RunSortBot :
1835 */
1843 #ifdef WITHSORTBOTS
1844 
1845 void *RunSortBot(void *dummy)
1846 {
1847  int identity, wakeupsignal, identityretv;
1848  ALLPRIVATES *B, *BB;
1849  DUMMYUSE(dummy);
1850  identity = SetIdentity(&identityretv);
1851  threadpointers[identity] = pthread_self();
1852  B = InitializeOneThread(identity);
1853  while ( ( wakeupsignal = SortBotWait(identity) ) > 0 ) {
1854  switch ( wakeupsignal ) {
1855 /*
1856  #[ INISORTBOT :
1857 */
1858  case INISORTBOT:
1859  AR.CurExpr = AB[0]->R.CurExpr;
1860  AR.PolyFun = AB[0]->R.PolyFun;
1861  AR.PolyFunType = AB[0]->R.PolyFunType;
1862  AR.SortType = AC.SortType;
1863  AT.SS->PolyFlag = AR.PolyFun ? AR.PolyFunType: 0;
1864  AT.SS->PolyWise = 0;
1865  AN.ncmod = AC.ncmod;
1866  LOCK(AT.SB.MasterBlockLock[1]);
1867  BB = AB[AT.SortBotIn1];
1868  LOCK(BB->T.SB.MasterBlockLock[BB->T.SB.MasterNumBlocks]);
1869  BB = AB[AT.SortBotIn2];
1870  LOCK(BB->T.SB.MasterBlockLock[BB->T.SB.MasterNumBlocks]);
1871  AT.SB.FillBlock = 1;
1872  AT.SB.MasterFill[1] = AT.SB.MasterStart[1];
1873  SETBASEPOSITION(AN.theposition,0);
1874  break;
1875 /*
1876  #] INISORTBOT :
1877  #[ RUNSORTBOT :
1878 */
1879  case RUNSORTBOT:
1880  SortBotMerge(B);
1881  break;
1882 /*
1883  #] RUNSORTBOT :
1884  #[ TERMINATETHREAD :
1885 */
1886  case TERMINATETHREAD:
1887  goto EndOfThread;
1888 /*
1889  #] TERMINATETHREAD :
1890  #[ CLEARCLOCK :
1891 
1892  The program only comes here after a .clear
1893 */
1894  case CLEARCLOCK:
1895 /* LOCK(clearclocklock); */
1896  sumtimerinfo[identity] += TimeCPU(1);
1897  timerinfo[identity] = TimeCPU(0);
1898 /* UNLOCK(clearclocklock); */
1899  break;
1900 /*
1901  #] CLEARCLOCK :
1902 */
1903  default:
1904  MLOCK(ErrorMessageLock);
1905  MesPrint("Illegal wakeup signal %d for thread %d",wakeupsignal,identity);
1906  MUNLOCK(ErrorMessageLock);
1907  Terminate(-1);
1908  break;
1909  }
1910  }
1911 EndOfThread:;
1912 /*
1913  This is the end of the thread. We cleanup and exit.
1914 */
1915  FinalizeOneThread(identity);
1916  return(0);
1917 }
1918 
1919 #endif
1920 
1921 /*
1922  #] RunSortBot :
1923  #[ IAmAvailable :
1924 */
1936 void IAmAvailable(int identity)
1937 {
1938  int top;
1939  LOCK(availabilitylock);
1940  top = topofavailables;
1941  listofavailables[topofavailables++] = identity;
1942  if ( top == 0 ) {
1943  UNLOCK(availabilitylock);
1944  LOCK(wakeupmasterlock);
1945  wakeupmaster = identity;
1946  pthread_cond_signal(&wakeupmasterconditions);
1947  UNLOCK(wakeupmasterlock);
1948  }
1949  else {
1950  UNLOCK(availabilitylock);
1951  }
1952 }
1953 
1954 /*
1955  #] IAmAvailable :
1956  #[ GetAvailableThread :
1957 */
1966 int GetAvailableThread()
1967 {
1968  int retval = -1;
1969  LOCK(availabilitylock);
1970  if ( topofavailables > 0 ) retval = listofavailables[--topofavailables];
1971  UNLOCK(availabilitylock);
1972  if ( retval >= 0 ) {
1973 /*
1974  Make sure the thread is indeed waiting and not between
1975  saying that it is available and starting to wait.
1976 */
1977  LOCK(wakeuplocks[retval]);
1978  UNLOCK(wakeuplocks[retval]);
1979  }
1980  return(retval);
1981 }
1982 
1983 /*
1984  #] GetAvailableThread :
1985  #[ ConditionalGetAvailableThread :
1986 */
1994 int ConditionalGetAvailableThread()
1995 {
1996  int retval = -1;
1997  if ( topofavailables > 0 ) {
1998  LOCK(availabilitylock);
1999  if ( topofavailables > 0 ) {
2000  retval = listofavailables[--topofavailables];
2001  }
2002  UNLOCK(availabilitylock);
2003  if ( retval >= 0 ) {
2004 /*
2005  Make sure the thread is indeed waiting and not between
2006  saying that it is available and starting to wait.
2007 */
2008  LOCK(wakeuplocks[retval]);
2009  UNLOCK(wakeuplocks[retval]);
2010  }
2011  }
2012  return(retval);
2013 }
2014 
2015 /*
2016  #] ConditionalGetAvailableThread :
2017  #[ GetThread :
2018 */
2028 int GetThread(int identity)
2029 {
2030  int retval = -1, j;
2031  LOCK(availabilitylock);
2032  for ( j = 0; j < topofavailables; j++ ) {
2033  if ( identity == listofavailables[j] ) break;
2034  }
2035  if ( j < topofavailables ) {
2036  --topofavailables;
2037  for ( ; j < topofavailables; j++ ) {
2038  listofavailables[j] = listofavailables[j+1];
2039  }
2040  retval = identity;
2041  }
2042  UNLOCK(availabilitylock);
2043  return(retval);
2044 }
2045 
2046 /*
2047  #] GetThread :
2048  #[ ThreadWait :
2049 */
2059 int ThreadWait(int identity)
2060 {
2061  int retval, top, j;
2062  LOCK(wakeuplocks[identity]);
2063  LOCK(availabilitylock);
2064  top = topofavailables;
2065  for ( j = topofavailables; j > 0; j-- )
2066  listofavailables[j] = listofavailables[j-1];
2067  listofavailables[0] = identity;
2068  topofavailables++;
2069  if ( top == 0 || topofavailables == numberofworkers ) {
2070  UNLOCK(availabilitylock);
2071  LOCK(wakeupmasterlock);
2072  wakeupmaster = identity;
2073  pthread_cond_signal(&wakeupmasterconditions);
2074  UNLOCK(wakeupmasterlock);
2075  }
2076  else {
2077  UNLOCK(availabilitylock);
2078  }
2079  while ( wakeup[identity] == 0 ) {
2080  pthread_cond_wait(&(wakeupconditions[identity]),&(wakeuplocks[identity]));
2081  }
2082  retval = wakeup[identity];
2083  wakeup[identity] = 0;
2084  UNLOCK(wakeuplocks[identity]);
2085  return(retval);
2086 }
2087 
2088 /*
2089  #] ThreadWait :
2090  #[ SortBotWait :
2091 */
2092 
2093 #ifdef WITHSORTBOTS
2094 
2103 int SortBotWait(int identity)
2104 {
2105  int retval;
2106  LOCK(wakeuplocks[identity]);
2107  LOCK(availabilitylock);
2108  topsortbotavailables++;
2109  if ( topsortbotavailables >= numberofsortbots ) {
2110  UNLOCK(availabilitylock);
2111  LOCK(wakeupsortbotlock);
2112  wakeupmaster = identity;
2113  pthread_cond_signal(&wakeupsortbotconditions);
2114  UNLOCK(wakeupsortbotlock);
2115  }
2116  else {
2117  UNLOCK(availabilitylock);
2118  }
2119  while ( wakeup[identity] == 0 ) {
2120  pthread_cond_wait(&(wakeupconditions[identity]),&(wakeuplocks[identity]));
2121  }
2122  retval = wakeup[identity];
2123  wakeup[identity] = 0;
2124  UNLOCK(wakeuplocks[identity]);
2125  return(retval);
2126 }
2127 
2128 #endif
2129 
2130 /*
2131  #] SortBotWait :
2132  #[ ThreadClaimedBlock :
2133 */
2144 int ThreadClaimedBlock(int identity)
2145 {
2146  LOCK(availabilitylock);
2147  numberclaimed++;
2148  if ( numberclaimed >= numberofworkers ) {
2149  UNLOCK(availabilitylock);
2150  LOCK(wakeupmasterlock);
2151  wakeupmaster = identity;
2152  pthread_cond_signal(&wakeupmasterconditions);
2153  UNLOCK(wakeupmasterlock);
2154  }
2155  else {
2156  UNLOCK(availabilitylock);
2157  }
2158  return(0);
2159 }
2160 
2161 /*
2162  #] ThreadClaimedBlock :
2163  #[ MasterWait :
2164 */
2172 int MasterWait()
2173 {
2174  int retval;
2175  LOCK(wakeupmasterlock);
2176  while ( wakeupmaster == 0 ) {
2177  pthread_cond_wait(&wakeupmasterconditions,&wakeupmasterlock);
2178  }
2179  retval = wakeupmaster;
2180  wakeupmaster = 0;
2181  UNLOCK(wakeupmasterlock);
2182  return(retval);
2183 }
2184 
2185 /*
2186  #] MasterWait :
2187  #[ MasterWaitThread :
2188 */
2195 int MasterWaitThread(int identity)
2196 {
2197  int retval;
2198  LOCK(wakeupmasterthreadlocks[identity]);
2199  while ( wakeupmasterthread[identity] == 0 ) {
2200  pthread_cond_wait(&(wakeupmasterthreadconditions[identity])
2201  ,&(wakeupmasterthreadlocks[identity]));
2202  }
2203  retval = wakeupmasterthread[identity];
2204  wakeupmasterthread[identity] = 0;
2205  UNLOCK(wakeupmasterthreadlocks[identity]);
2206  return(retval);
2207 }
2208 
2209 /*
2210  #] MasterWaitThread :
2211  #[ MasterWaitAll :
2212 */
2219 void MasterWaitAll()
2220 {
2221  LOCK(wakeupmasterlock);
2222  while ( topofavailables < numberofworkers ) {
2223  pthread_cond_wait(&wakeupmasterconditions,&wakeupmasterlock);
2224  }
2225  UNLOCK(wakeupmasterlock);
2226  return;
2227 }
2228 
2229 /*
2230  #] MasterWaitAll :
2231  #[ MasterWaitAllSortBots :
2232 */
2233 
2234 #ifdef WITHSORTBOTS
2235 
2241 void MasterWaitAllSortBots()
2242 {
2243  LOCK(wakeupsortbotlock);
2244  while ( topsortbotavailables < numberofsortbots ) {
2245  pthread_cond_wait(&wakeupsortbotconditions,&wakeupsortbotlock);
2246  }
2247  UNLOCK(wakeupsortbotlock);
2248  return;
2249 }
2250 
2251 #endif
2252 
2253 /*
2254  #] MasterWaitAllSortBots :
2255  #[ MasterWaitAllBlocks :
2256 */
2263 void MasterWaitAllBlocks()
2264 {
2265  LOCK(wakeupmasterlock);
2266  while ( numberclaimed < numberofworkers ) {
2267  pthread_cond_wait(&wakeupmasterconditions,&wakeupmasterlock);
2268  }
2269  UNLOCK(wakeupmasterlock);
2270  return;
2271 }
2272 
2273 /*
2274  #] MasterWaitAllBlocks :
2275  #[ WakeupThread :
2276 */
2285 void WakeupThread(int identity, int signalnumber)
2286 {
2287  if ( signalnumber == 0 ) {
2288  MLOCK(ErrorMessageLock);
2289  MesPrint("Illegal wakeup signal for thread %d",identity);
2290  MUNLOCK(ErrorMessageLock);
2291  Terminate(-1);
2292  }
2293  LOCK(wakeuplocks[identity]);
2294  wakeup[identity] = signalnumber;
2295  pthread_cond_signal(&(wakeupconditions[identity]));
2296  UNLOCK(wakeuplocks[identity]);
2297 }
2298 
2299 /*
2300  #] WakeupThread :
2301  #[ WakeupMasterFromThread :
2302 */
2311 void WakeupMasterFromThread(int identity, int signalnumber)
2312 {
2313  if ( signalnumber == 0 ) {
2314  MLOCK(ErrorMessageLock);
2315  MesPrint("Illegal wakeup signal for master %d",identity);
2316  MUNLOCK(ErrorMessageLock);
2317  Terminate(-1);
2318  }
2319  LOCK(wakeupmasterthreadlocks[identity]);
2320  wakeupmasterthread[identity] = signalnumber;
2321  pthread_cond_signal(&(wakeupmasterthreadconditions[identity]));
2322  UNLOCK(wakeupmasterthreadlocks[identity]);
2323 }
2324 
2325 /*
2326  #] WakeupMasterFromThread :
2327  #[ SendOneBucket :
2328 */
2334 int SendOneBucket(int type)
2335 {
2336  ALLPRIVATES *B0 = AB[0];
2337  THREADBUCKET *thr = 0;
2338  int j, k, id;
2339  for ( j = 0; j < numthreadbuckets; j++ ) {
2340  if ( threadbuckets[j]->free == BUCKETFILLED ) {
2341  thr = threadbuckets[j];
2342  for ( k = j+1; k < numthreadbuckets; k++ )
2343  threadbuckets[k-1] = threadbuckets[k];
2344  threadbuckets[numthreadbuckets-1] = thr;
2345  break;
2346  }
2347  }
2348  AN0.ninterms++;
2349  while ( ( id = GetAvailableThread() ) < 0 ) { MasterWait(); }
2350 /*
2351  Prepare the thread. Give it the term and variables.
2352 */
2353  LoadOneThread(0,id,thr,0);
2354  thr->busy = BUCKETASSIGNED;
2355  thr->free = BUCKETINUSE;
2356  numberoffullbuckets--;
2357 /*
2358  And signal the thread to run.
2359  Form now on we may only interfere with this bucket
2360  1: after it has been marked BUCKETCOMINGFREE
2361  2: when thr->busy == BUCKETDOINGTERM and then only when protected by
2362  thr->lock. This would be for load balancing.
2363 */
2364  WakeupThread(id,type);
2365 /* AN0.ninterms += thr->ddterms; */
2366  return(0);
2367 }
2368 
2369 /*
2370  #] SendOneBucket :
2371  #[ InParallelProcessor :
2372 */
2392 int InParallelProcessor()
2393 {
2394  GETIDENTITY
2395  int i, id, retval = 0, num = 0;
2396  EXPRESSIONS e;
2397  if ( numberofworkers >= 2 ) {
2398  SetWorkerFiles();
2399  for ( i = 0; i < NumExpressions; i++ ) {
2400  e = Expressions+i;
2401  if ( e->partodo <= 0 ) continue;
2402  if ( e->status == LOCALEXPRESSION || e->status == GLOBALEXPRESSION
2403  || e->status == UNHIDELEXPRESSION || e->status == UNHIDEGEXPRESSION
2404  || e->status == INTOHIDELEXPRESSION || e->status == INTOHIDEGEXPRESSION ) {
2405  }
2406  else {
2407  e->partodo = 0;
2408  continue;
2409  }
2410  if ( e->counter == 0 ) { /* Expression with zero terms */
2411  e->partodo = 0;
2412  continue;
2413  }
2414 /*
2415  This expression should go to an idle worker
2416 */
2417  while ( ( id = GetAvailableThread() ) < 0 ) { MasterWait(); }
2418  LoadOneThread(0,id,0,-1);
2419  AB[id]->R.exprtodo = i;
2420  WakeupThread(id,DOONEEXPRESSION);
2421  num++;
2422  }
2423 /*
2424  Now we have to wait for all workers to finish
2425 */
2426  if ( num > 0 ) MasterWaitAll();
2427 
2428  if ( AC.CollectFun ) AR.DeferFlag = 0;
2429  }
2430  else {
2431  for ( i = 0; i < NumExpressions; i++ ) {
2432  Expressions[i].partodo = 0;
2433  }
2434  }
2435  return(retval);
2436 }
2437 
2438 /*
2439  #] InParallelProcessor :
2440  #[ ThreadsProcessor :
2441 */
2466 int ThreadsProcessor(EXPRESSIONS e, WORD LastExpression)
2467 {
2468  ALLPRIVATES *B0 = AB[0], *B = B0;
2469  int id, oldgzipCompress, endofinput = 0, j, still, k, defcount = 0, bra = 0, first = 1;
2470  LONG dd = 0, ddd, thrbufsiz, thrbufsiz0, thrbufsiz2, numbucket = 0, numpasses;
2471  LONG num, i;
2472  WORD *oldworkpointer = AT0.WorkPointer, *tt, *ttco = 0, *t1 = 0, ter, *tstop = 0, *t2;
2473  THREADBUCKET *thr = 0;
2474  FILEHANDLE *oldoutfile = AR0.outfile;
2475  GETTERM GetTermP = &GetTerm;
2476  POSITION eonfile = AS.OldOnFile[e-Expressions];
2477  numberoffullbuckets = 0;
2478 /*
2479  Start up all threads. The lock needs to be around the whole loop
2480  to keep processes from terminating quickly and putting themselves
2481  in the list of available threads again.
2482 */
2483  AM.tracebackflag = 1;
2484 
2485  AS.sLevel = AR0.sLevel;
2486  LOCK(availabilitylock);
2487  topofavailables = 0;
2488  for ( id = 1; id <= numberofworkers; id++ ) {
2489  WakeupThread(id,STARTNEWEXPRESSION);
2490  }
2491  UNLOCK(availabilitylock);
2492  NewSort(BHEAD0);
2493  AN0.ninterms = 1;
2494 /*
2495  Now for redefine
2496 */
2497  if ( AC.numpfirstnum > 0 ) {
2498  for ( j = 0; j < AC.numpfirstnum; j++ ) {
2499  AC.inputnumbers[j] = -1;
2500  }
2501  }
2502  MasterWaitAll();
2503 /*
2504  Determine a reasonable bucketsize.
2505  This is based on the value of AC.ThreadBucketSize and the number
2506  of terms. We want at least 5 buckets per worker at the moment.
2507  Some research should show whether this is reasonable.
2508 
2509  The number of terms in the expression is in e->counter
2510 */
2511  thrbufsiz2 = thrbufsiz = AC.ThreadBucketSize-1;
2512  if ( ( e->counter / ( numberofworkers * 5 ) ) < thrbufsiz ) {
2513  thrbufsiz = e->counter / ( numberofworkers * 5 ) - 1;
2514  if ( thrbufsiz < 0 ) thrbufsiz = 0;
2515  }
2516  thrbufsiz0 = thrbufsiz;
2517  numpasses = 5; /* this is just for trying */
2518  thrbufsiz = thrbufsiz0 / (2 << numpasses);
2519 /*
2520  Mark all buckets as free and take the first.
2521 */
2522  for ( j = 0; j < numthreadbuckets; j++ )
2523  threadbuckets[j]->free = BUCKETFREE;
2524  thr = threadbuckets[0];
2525 /*
2526  #[ Whole brackets :
2527 
2528  First we look whether we have to work with entire brackets
2529  This is the case when there is a non-NULL address in e->bracketinfo.
2530  Of course we shouldn't have interference from a collect or keep statement.
2531 */
2532 #ifdef WHOLEBRACKETS
2533  if ( e->bracketinfo && AC.CollectFun == 0 && AR0.DeferFlag == 0 ) {
2534  FILEHANDLE *curfile;
2535  int didone = 0;
2536  LONG num, n;
2537  AN0.expr = e;
2538  for ( n = 0; n < e->bracketinfo->indexfill; n++ ) {
2539  num = TreatIndexEntry(B0,n);
2540  if ( num > 0 ) {
2541  didone = 1;
2542 /*
2543  This bracket can be sent off.
2544  1: Look for an empty bucket
2545 */
2546 ReTry:;
2547  for ( j = 0; j < numthreadbuckets; j++ ) {
2548  switch ( threadbuckets[j]->free ) {
2549  case BUCKETFREE:
2550  thr = threadbuckets[j];
2551  goto Found1;
2552  case BUCKETCOMINGFREE:
2553  thr = threadbuckets[j];
2554  thr->free = BUCKETFREE;
2555  for ( k = j+1; k < numthreadbuckets; k++ )
2556  threadbuckets[k-1] = threadbuckets[k];
2557  threadbuckets[numthreadbuckets-1] = thr;
2558  j--;
2559  break;
2560  default:
2561  break;
2562  }
2563  }
2564 Found1:;
2565  if ( j < numthreadbuckets ) {
2566 /*
2567  Found an empty bucket. Fill it.
2568 */
2569  thr->firstbracket = n;
2570  thr->lastbracket = n + num - 1;
2571  thr->type = BUCKETDOINGBRACKET;
2572  thr->free = BUCKETFILLED;
2573  thr->firstterm = AN0.ninterms;
2574  for ( j = n; j < n+num; j++ ) {
2575  AN0.ninterms += e->bracketinfo->indexbuffer[j].termsinbracket;
2576  }
2577  n += num-1;
2578  numberoffullbuckets++;
2579  if ( topofavailables > 0 ) {
2580  SendOneBucket(DOBRACKETS);
2581  }
2582  }
2583 /*
2584  All buckets are in use.
2585  Look/wait for an idle worker. Give it a bucket.
2586  After that, retry for a bucket
2587 */
2588  else {
2589  while ( topofavailables <= 0 ) {
2590  MasterWait();
2591  }
2592  SendOneBucket(DOBRACKETS);
2593  goto ReTry;
2594  }
2595  }
2596  }
2597  if ( didone ) {
2598 /*
2599  And now put the input back in the original position.
2600 */
2601  switch ( e->status ) {
2602  case UNHIDELEXPRESSION:
2603  case UNHIDEGEXPRESSION:
2604  case DROPHLEXPRESSION:
2605  case DROPHGEXPRESSION:
2606  case HIDDENLEXPRESSION:
2607  case HIDDENGEXPRESSION:
2608  curfile = AR0.hidefile;
2609  break;
2610  default:
2611  curfile = AR0.infile;
2612  break;
2613  }
2614  SetScratch(curfile,&eonfile);
2615  GetTerm(B0,AT0.WorkPointer);
2616 /*
2617  Now we point the GetTerm that is used to the one that is selective
2618 */
2619  GetTermP = &GetTerm2;
2620 /*
2621  Next wait till there is a bucket available and initialize thr to it.
2622 */
2623  for(;;) {
2624  for ( j = 0; j < numthreadbuckets; j++ ) {
2625  switch ( threadbuckets[j]->free ) {
2626  case BUCKETFREE:
2627  thr = threadbuckets[j];
2628  goto Found2;
2629  case BUCKETCOMINGFREE:
2630  thr = threadbuckets[j];
2631  thr->free = BUCKETFREE;
2632  for ( k = j+1; k < numthreadbuckets; k++ )
2633  threadbuckets[k-1] = threadbuckets[k];
2634  threadbuckets[numthreadbuckets-1] = thr;
2635  j--;
2636  break;
2637  default:
2638  break;
2639  }
2640  }
2641  while ( topofavailables <= 0 ) {
2642  MasterWait();
2643  }
2644  while ( topofavailables > 0 && numberoffullbuckets > 0 ) {
2645  SendOneBucket(DOBRACKETS);
2646  }
2647  }
2648 Found2:;
2649  while ( numberoffullbuckets > 0 ) {
2650  while ( topofavailables <= 0 ) {
2651  MasterWait();
2652  }
2653  while ( topofavailables > 0 && numberoffullbuckets > 0 ) {
2654  SendOneBucket(DOBRACKETS);
2655  }
2656  }
2657 /*
2658  Disable the 'warming up' with smaller buckets.
2659 
2660  numpasses = 0;
2661  thrbufsiz = thrbufsiz0;
2662 */
2663  AN0.lastinindex = -1;
2664  }
2665  MasterWaitAll();
2666  }
2667 #endif
2668 /*
2669  #] Whole brackets :
2670 
2671  Now the loop to start a bucket
2672 */
2673  while ( ( ter = GetTermP(B0,thr->threadbuffer) ) >= 0 ) {
2674  if ( ter == 0 ) { endofinput = 1; goto Finalize; }
2675  dd = AN0.deferskipped;
2676  if ( AR0.DeferFlag ) {
2677  defcount = 0;
2678  thr->deferbuffer[defcount++] = AR0.DefPosition;
2679  ttco = thr->compressbuffer; t1 = AR0.CompressBuffer; j = *t1;
2680  NCOPY(ttco,t1,j);
2681  }
2682  else if ( first && ( AC.CollectFun == 0 ) ) { /* Brackets ? */
2683  first = 0;
2684  t1 = tstop = thr->threadbuffer;
2685  tstop += *tstop; tstop -= ABS(tstop[-1]);
2686  t1++;
2687  while ( t1 < tstop ) {
2688  if ( t1[0] == HAAKJE ) { bra = 1; break; }
2689  t1 += t1[1];
2690  }
2691  t1 = thr->threadbuffer;
2692  }
2693 /*
2694  Check whether we have a collect,function going. If so execute it.
2695 */
2696  if ( AC.CollectFun && *(thr->threadbuffer) < (AM.MaxTer/((LONG)sizeof(WORD))-10) ) {
2697  if ( ( dd = GetMoreTerms(thr->threadbuffer) ) < 0 ) {
2698  LowerSortLevel(); goto ProcErr;
2699  }
2700  }
2701 /*
2702  Check whether we have a priority task:
2703 */
2704  if ( topofavailables > 0 && numberoffullbuckets > 0 ) SendOneBucket(LOWESTLEVELGENERATION);
2705 /*
2706  Now put more terms in the bucket. Position tt after the first term
2707 */
2708  tt = thr->threadbuffer; tt += *tt;
2709  thr->totnum = 1;
2710  thr->usenum = 0;
2711 /*
2712  Next we worry about the 'slow startup' in which we make the initial
2713  buckets smaller, so that we get all threads busy as soon as possible.
2714 */
2715  if ( numpasses > 0 ) {
2716  numbucket++;
2717  if ( numbucket >= numberofworkers ) {
2718  numbucket = 0;
2719  numpasses--;
2720  if ( numpasses == 0 ) thrbufsiz = thrbufsiz0;
2721  else thrbufsiz = thrbufsiz0 / (2 << numpasses);
2722  }
2723  thrbufsiz2 = thrbufsiz + thrbufsiz/5; /* for completing brackets */
2724  }
2725 /*
2726  we have already 1+dd terms
2727 */
2728  while ( ( dd < thrbufsiz ) &&
2729  ( tt - thr->threadbuffer ) < ( thr->threadbuffersize - AM.MaxTer - 2 ) ) {
2730 /*
2731  First check:
2732 */
2733  if ( topofavailables > 0 && numberoffullbuckets > 0 ) SendOneBucket(LOWESTLEVELGENERATION);
2734 /*
2735  There is room in the bucket. Fill yet another term.
2736 */
2737  if ( GetTermP(B0,tt) == 0 ) { endofinput = 1; break; }
2738  dd++;
2739  thr->totnum++;
2740  dd += AN0.deferskipped;
2741  if ( AR0.DeferFlag ) {
2742  thr->deferbuffer[defcount++] = AR0.DefPosition;
2743  t1 = AR0.CompressBuffer; j = *t1;
2744  NCOPY(ttco,t1,j);
2745  }
2746  if ( AC.CollectFun && *tt < (AM.MaxTer/((LONG)sizeof(WORD))-10) ) {
2747  if ( ( ddd = GetMoreTerms(tt) ) < 0 ) {
2748  LowerSortLevel(); goto ProcErr;
2749  }
2750  dd += ddd;
2751  }
2752  t1 = tt;
2753  tt += *tt;
2754  }
2755 /*
2756  Check whether there are regular brackets and if we have no DeferFlag
2757  and no collect, we try to add more terms till we finish the current
2758  bracket. We should however not overdo it. Let us say: up to 20%
2759  more terms are allowed.
2760 */
2761  if ( bra ) {
2762  tstop = t1 + *t1; tstop -= ABS(tstop[-1]);
2763  t2 = t1+1;
2764  while ( t2 < tstop ) {
2765  if ( t2[0] == HAAKJE ) { break; }
2766  t2 += t2[1];
2767  }
2768  if ( t2[0] == HAAKJE ) {
2769  t2 += t2[1]; num = t2 - t1;
2770  while ( ( dd < thrbufsiz2 ) &&
2771  ( tt - thr->threadbuffer ) < ( thr->threadbuffersize - AM.MaxTer - 2 ) ) {
2772 /*
2773  First check:
2774 */
2775  if ( topofavailables > 0 && numberoffullbuckets > 0 ) SendOneBucket(LOWESTLEVELGENERATION);
2776 /*
2777  There is room in the bucket. Fill yet another term.
2778 */
2779  if ( GetTermP(B0,tt) == 0 ) { endofinput = 1; break; }
2780 /*
2781  Same bracket?
2782 */
2783  tstop = tt + *tt; tstop -= ABS(tstop[-1]);
2784  if ( tstop-tt < num ) { /* Different: abort */
2785  AR0.KeptInHold = 1;
2786  break;
2787  }
2788  for ( i = 1; i < num; i++ ) {
2789  if ( t1[i] != tt[i] ) break;
2790  }
2791  if ( i < num ) { /* Different: abort */
2792  AR0.KeptInHold = 1;
2793  break;
2794  }
2795 /*
2796  Same bracket. We need this term.
2797 */
2798  dd++;
2799  thr->totnum++;
2800  tt += *tt;
2801  }
2802  }
2803  }
2804  thr->ddterms = dd; /* total number of terms including keep brackets */
2805  thr->firstterm = AN0.ninterms;
2806  AN0.ninterms += dd;
2807  *tt = 0; /* mark end of bucket */
2808  thr->free = BUCKETFILLED;
2809  thr->type = BUCKETDOINGTERMS;
2810  numberoffullbuckets++;
2811  if ( topofavailables <= 0 && endofinput == 0 ) {
2812 /*
2813  Problem: topofavailables may already be > 0, but the
2814  thread has not yet gone into waiting. Can the signal get lost?
2815  How can we tell that a thread is waiting for a signal?
2816 
2817  All threads are busy. Try to load up another bucket.
2818  In the future we could be more sophisticated.
2819  At the moment we load a complete bucket which could be
2820  1000 terms or even more.
2821  In principle it is better to keep a full bucket ready
2822  and check after each term we put in the next bucket. That
2823  way we don't waste time of the workers.
2824 */
2825  for ( j = 0; j < numthreadbuckets; j++ ) {
2826  switch ( threadbuckets[j]->free ) {
2827  case BUCKETFREE:
2828  thr = threadbuckets[j];
2829  if ( !endofinput ) goto NextBucket;
2830 /*
2831  If we are at the end of the input we mark
2832  the free buckets in a special way. That way
2833  we don't keep running into them.
2834 */
2835  thr->free = BUCKETATEND;
2836  break;
2837  case BUCKETCOMINGFREE:
2838  thr = threadbuckets[j];
2839  thr->free = BUCKETFREE;
2840 /*
2841  Bucket has just been finished.
2842  Put at the end of the list. We don't want
2843  an early bucket to wait to be treated last.
2844 */
2845  for ( k = j+1; k < numthreadbuckets; k++ )
2846  threadbuckets[k-1] = threadbuckets[k];
2847  threadbuckets[numthreadbuckets-1] = thr;
2848  j--; /* we have to redo the same number j. */
2849  break;
2850  default:
2851  break;
2852  }
2853  }
2854 /*
2855  We have no free bucket or we are at the end.
2856  The only thing we can do now is wait for a worker to come free,
2857  provided there are still buckets to send.
2858 */
2859  }
2860 /*
2861  Look for the next bucket to send. There is at least one full bucket!
2862 */
2863  for ( j = 0; j < numthreadbuckets; j++ ) {
2864  if ( threadbuckets[j]->free == BUCKETFILLED ) {
2865  thr = threadbuckets[j];
2866  for ( k = j+1; k < numthreadbuckets; k++ )
2867  threadbuckets[k-1] = threadbuckets[k];
2868  threadbuckets[numthreadbuckets-1] = thr;
2869  break;
2870  }
2871  }
2872 /*
2873  Wait for a thread to become available
2874  The bucket we are going to use is in thr.
2875 */
2876 DoBucket:;
2877  AN0.ninterms++;
2878  while ( ( id = GetAvailableThread() ) < 0 ) { MasterWait(); }
2879 /*
2880  Prepare the thread. Give it the term and variables.
2881 */
2882  LoadOneThread(0,id,thr,0);
2883  LOCK(thr->lock);
2884  thr->busy = BUCKETASSIGNED;
2885  UNLOCK(thr->lock);
2886  thr->free = BUCKETINUSE;
2887  numberoffullbuckets--;
2888 /*
2889  And signal the thread to run.
2890  Form now on we may only interfere with this bucket
2891  1: after it has been marked BUCKETCOMINGFREE
2892  2: when thr->busy == BUCKETDOINGTERM and then only when protected by
2893  thr->lock. This would be for load balancing.
2894 */
2895  WakeupThread(id,LOWESTLEVELGENERATION);
2896 /* AN0.ninterms += thr->ddterms; */
2897 /*
2898  Now look whether there is another bucket filled and a worker available
2899 */
2900  if ( topofavailables > 0 ) { /* there is a worker */
2901  for ( j = 0; j < numthreadbuckets; j++ ) {
2902  if ( threadbuckets[j]->free == BUCKETFILLED ) {
2903  thr = threadbuckets[j];
2904  for ( k = j+1; k < numthreadbuckets; k++ )
2905  threadbuckets[k-1] = threadbuckets[k];
2906  threadbuckets[numthreadbuckets-1] = thr;
2907  goto DoBucket; /* and we found a bucket */
2908  }
2909  }
2910 /*
2911  no bucket is loaded but there is a thread available
2912  find a bucket to load. If there is none (all are USED or ATEND)
2913  we jump out of the loop.
2914 */
2915  for ( j = 0; j < numthreadbuckets; j++ ) {
2916  switch ( threadbuckets[j]->free ) {
2917  case BUCKETFREE:
2918  thr = threadbuckets[j];
2919  if ( !endofinput ) goto NextBucket;
2920  thr->free = BUCKETATEND;
2921  break;
2922  case BUCKETCOMINGFREE:
2923  thr = threadbuckets[j];
2924  if ( endofinput ) {
2925  thr->free = BUCKETATEND;
2926  }
2927  else {
2928  thr->free = BUCKETFREE;
2929  for ( k = j+1; k < numthreadbuckets; k++ )
2930  threadbuckets[k-1] = threadbuckets[k];
2931  threadbuckets[numthreadbuckets-1] = thr;
2932  j--;
2933  }
2934  break;
2935  default:
2936  break;
2937  }
2938  }
2939  if ( j >= numthreadbuckets ) break;
2940  }
2941  else {
2942 /*
2943  No worker available.
2944  Look for a bucket to load.
2945  Its number will be in "still"
2946 */
2947 Finalize:;
2948  still = -1;
2949  for ( j = 0; j < numthreadbuckets; j++ ) {
2950  switch ( threadbuckets[j]->free ) {
2951  case BUCKETFREE:
2952  thr = threadbuckets[j];
2953  if ( !endofinput ) goto NextBucket;
2954  thr->free = BUCKETATEND;
2955  break;
2956  case BUCKETCOMINGFREE:
2957  thr = threadbuckets[j];
2958  if ( endofinput ) thr->free = BUCKETATEND;
2959  else {
2960  thr->free = BUCKETFREE;
2961  for ( k = j+1; k < numthreadbuckets; k++ )
2962  threadbuckets[k-1] = threadbuckets[k];
2963  threadbuckets[numthreadbuckets-1] = thr;
2964  j--;
2965  }
2966  break;
2967  case BUCKETFILLED:
2968  if ( still < 0 ) still = j;
2969  break;
2970  default:
2971  break;
2972  }
2973  }
2974  if ( still < 0 ) {
2975 /*
2976  No buckets to be executed and no buckets FREE.
2977  We must be at the end. Break out of the loop.
2978 */
2979  break;
2980  }
2981  thr = threadbuckets[still];
2982  for ( k = still+1; k < numthreadbuckets; k++ )
2983  threadbuckets[k-1] = threadbuckets[k];
2984  threadbuckets[numthreadbuckets-1] = thr;
2985  goto DoBucket;
2986  }
2987 NextBucket:;
2988  }
2989 /*
2990  Now the stage one load balancing.
2991  If the load has been readjusted we have again filled buckets.
2992  In that case we jump back in the loop.
2993 
2994  Tricky point: when do the workers see the new value of AT.LoadBalancing?
2995  It should activate the locks on thr->busy
2996 */
2997  if ( AC.ThreadBalancing ) {
2998  for ( id = 1; id <= numberofworkers; id++ ) {
2999  AB[id]->T.LoadBalancing = 1;
3000  }
3001  if ( LoadReadjusted() ) goto Finalize;
3002  for ( id = 1; id <= numberofworkers; id++ ) {
3003  AB[id]->T.LoadBalancing = 0;
3004  }
3005  }
3006  if ( AC.ThreadBalancing ) {
3007 /*
3008  The AS.Balancing flag should have Generator look for
3009  free workers and apply the "buro" method.
3010 
3011  There is still a serious problem.
3012  When for instance a sum_, there may be space created in a local
3013  compiler buffer for a wildcard substitution or whatever.
3014  Compiler buffer execution scribble space.....
3015  This isn't copied along?
3016  Look up ebufnum. There are 12 places with AddRHS!
3017  Problem: one process alloces in ebuf. Then term is given to
3018  other process. It would like to use from this ebuf, but the sender
3019  finishes first and removes the ebuf (and/or overwrites it).
3020 
3021  Other problem: local $ variables aren't copied along.
3022 */
3023  AS.Balancing = 0;
3024  }
3025  MasterWaitAll();
3026  AS.Balancing = 0;
3027 /*
3028  When we deal with the last expression we can now remove the input
3029  scratch file. This saves potentially much disk space (up to 1/3)
3030 */
3031  if ( LastExpression ) {
3032  UpdateMaxSize();
3033  if ( AR0.infile->handle >= 0 ) {
3034  CloseFile(AR0.infile->handle);
3035  AR0.infile->handle = -1;
3036  remove(AR0.infile->name);
3037  PUTZERO(AR0.infile->POposition);
3038  AR0.infile->POfill = AR0.infile->POfull = AR0.infile->PObuffer;
3039  }
3040  }
3041 /*
3042  We order the threads to finish in the MasterMerge routine
3043  It will start with waiting for all threads to finish.
3044  One could make an administration in which threads that have
3045  finished can start already with the final sort but
3046  1: The load balancing should not make this super urgent
3047  2: It would definitely not be very compatible with the second
3048  stage load balancing.
3049 */
3050  oldgzipCompress = AR0.gzipCompress;
3051  AR0.gzipCompress = 0;
3052  if ( AR0.outtohide ) AR0.outfile = AR0.hidefile;
3053  if ( MasterMerge() < 0 ) {
3054  if ( AR0.outtohide ) AR0.outfile = oldoutfile;
3055  AR0.gzipCompress = oldgzipCompress;
3056  goto ProcErr;
3057  }
3058  if ( AR0.outtohide ) AR0.outfile = oldoutfile;
3059  AR0.gzipCompress = oldgzipCompress;
3060 /*
3061  Now wait for all threads to be ready to give them the cleaning up signal.
3062  With the new MasterMerge routine we can do the cleanup already automatically
3063  avoiding having to send these signals.
3064 */
3065  MasterWaitAll();
3066  AR0.sLevel--;
3067  for ( id = 1; id < AM.totalnumberofthreads; id++ ) {
3068  if ( GetThread(id) > 0 ) WakeupThread(id,CLEANUPEXPRESSION);
3069  }
3070  e->numdummies = 0;
3071  for ( id = 1; id < AM.totalnumberofthreads; id++ ) {
3072  if ( AB[id]->R.MaxDum - AM.IndDum > e->numdummies )
3073  e->numdummies = AB[id]->R.MaxDum - AM.IndDum;
3074  AR0.expchanged |= AB[id]->R.expchanged;
3075  }
3076 /*
3077  And wait for all to be clean.
3078 */
3079  MasterWaitAll();
3080  AT0.WorkPointer = oldworkpointer;
3081  return(0);
3082 ProcErr:;
3083  return(-1);
3084 }
3085 
3086 /*
3087  #] ThreadsProcessor :
3088  #[ LoadReadjusted :
3089 */
3108 int LoadReadjusted()
3109 {
3110  ALLPRIVATES *B0 = AB[0];
3111  THREADBUCKET *thr = 0, *thrtogo = 0;
3112  int numtogo, numfree, numbusy, n, nperbucket, extra, i, j, u, bus;
3113  LONG numinput;
3114  WORD *t1, *c1, *t2, *c2, *t3;
3115 /*
3116  Start with waiting for at least one free processor.
3117  We don't want the master competing for time when all are busy.
3118 */
3119  while ( topofavailables <= 0 ) MasterWait();
3120 /*
3121  Now look for the fullest bucket and make a list of free buckets
3122  The bad part is that most numbers can change at any moment.
3123 */
3124 restart:;
3125  numtogo = 0;
3126  numfree = 0;
3127  numbusy = 0;
3128  for ( j = 0; j < numthreadbuckets; j++ ) {
3129  thr = threadbuckets[j];
3130  if ( thr->free == BUCKETFREE || thr->free == BUCKETATEND
3131  || thr->free == BUCKETCOMINGFREE ) {
3132  freebuckets[numfree++] = thr;
3133  }
3134  else if ( thr->type != BUCKETDOINGTERMS ) {}
3135  else if ( thr->totnum > 1 ) { /* never steal from a bucket with one term */
3136  LOCK(thr->lock);
3137  bus = thr->busy;
3138  UNLOCK(thr->lock);
3139  if ( thr->free == BUCKETINUSE ) {
3140  n = thr->totnum-thr->usenum;
3141  if ( bus == BUCKETASSIGNED ) numbusy++;
3142  else if ( ( bus != BUCKETASSIGNED )
3143  && ( n > numtogo ) ) {
3144  numtogo = n;
3145  thrtogo = thr;
3146  }
3147  }
3148  else if ( bus == BUCKETTOBERELEASED
3149  && thr->free == BUCKETRELEASED ) {
3150  freebuckets[numfree++] = thr;
3151  thr->free = BUCKETATEND;
3152  LOCK(thr->lock);
3153  thr->busy = BUCKETPREPARINGTERM;
3154  UNLOCK(thr->lock);
3155  }
3156  }
3157  }
3158  if ( numfree == 0 ) return(0); /* serious problem */
3159  if ( numtogo > 0 ) { /* provisionally there is something to be stolen */
3160  thr = thrtogo;
3161 /*
3162  If the number has changed there is good progress.
3163  Maybe there is another thread that needs assistence.
3164  We start all over.
3165 */
3166  if ( thr->totnum-thr->usenum < numtogo ) goto restart;
3167 /*
3168  If the thread is in the term loading phace
3169  (thr->busy == BUCKETPREPARINGTERM) we better stay away from it.
3170  We wait now for the thread to be busy, and don't allow it
3171  now to drop out of this state till we are done here.
3172  This all depends on whether AT.LoadBalancing == 1 is seen by
3173  the thread.
3174 */
3175  LOCK(thr->lock);
3176  if ( thr->busy != BUCKETDOINGTERM ) {
3177  UNLOCK(thr->lock);
3178  goto restart;
3179  }
3180  if ( thr->totnum-thr->usenum < numtogo ) {
3181  UNLOCK(thr->lock);
3182  goto restart;
3183  }
3184  thr->free = BUCKETTERMINATED;
3185 /*
3186  The above will signal the thread we want to terminate.
3187  Next all effort goes into making sure the landing is soft.
3188  Unfortunately we don't want to wait for a signal, because the thread
3189  may be working for a long time on a single term.
3190 */
3191  if ( thr->usenum == thr->totnum ) {
3192 /*
3193  Terminated in the mean time or by now working on the
3194  last term. Try again.
3195 */
3196  thr->free = BUCKETATEND;
3197  UNLOCK(thr->lock);
3198  goto restart;
3199  }
3200  goto intercepted;
3201  }
3202 /* UNLOCK(thr->lock); */
3203  if ( numbusy > 0 ) return(1); /* Wait a bit.... */
3204  return(0);
3205 intercepted:;
3206 /*
3207  We intercepted one successfully. Now it becomes interesting. Action:
3208  1: determine how many terms per free bucket.
3209  2: find the first untreated term.
3210  3: put the terms in the free buckets.
3211 
3212  Remember: we have the lock to avoid interference from the thread
3213  that is being robbed.
3214 */
3215  numinput = thr->firstterm + thr->usenum;
3216  nperbucket = numtogo / numfree;
3217  extra = numtogo - nperbucket*numfree;
3218  if ( AR0.DeferFlag ) {
3219  t1 = thr->threadbuffer; c1 = thr->compressbuffer; u = thr->usenum;
3220  for ( n = 0; n < thr->usenum; n++ ) { t1 += *t1; c1 += *c1; }
3221  t3 = t1;
3222  if ( extra > 0 ) {
3223  for ( i = 0; i < extra; i++ ) {
3224  thrtogo = freebuckets[i];
3225  t2 = thrtogo->threadbuffer;
3226  c2 = thrtogo->compressbuffer;
3227  thrtogo->free = BUCKETFILLED;
3228  thrtogo->type = BUCKETDOINGTERMS;
3229  thrtogo->totnum = nperbucket+1;
3230  thrtogo->ddterms = 0;
3231  thrtogo->usenum = 0;
3232  thrtogo->busy = BUCKETASSIGNED;
3233  thrtogo->firstterm = numinput;
3234  numinput += nperbucket+1;
3235  for ( n = 0; n <= nperbucket; n++ ) {
3236  j = *t1; NCOPY(t2,t1,j);
3237  j = *c1; NCOPY(c2,c1,j);
3238  thrtogo->deferbuffer[n] = thr->deferbuffer[u++];
3239  }
3240  *t2 = *c2 = 0;
3241  }
3242  }
3243  if ( nperbucket > 0 ) {
3244  for ( i = extra; i < numfree; i++ ) {
3245  thrtogo = freebuckets[i];
3246  t2 = thrtogo->threadbuffer;
3247  c2 = thrtogo->compressbuffer;
3248  thrtogo->free = BUCKETFILLED;
3249  thrtogo->type = BUCKETDOINGTERMS;
3250  thrtogo->totnum = nperbucket;
3251  thrtogo->ddterms = 0;
3252  thrtogo->usenum = 0;
3253  thrtogo->busy = BUCKETASSIGNED;
3254  thrtogo->firstterm = numinput;
3255  numinput += nperbucket;
3256  for ( n = 0; n < nperbucket; n++ ) {
3257  j = *t1; NCOPY(t2,t1,j);
3258  j = *c1; NCOPY(c2,c1,j);
3259  thrtogo->deferbuffer[n] = thr->deferbuffer[u++];
3260  }
3261  *t2 = *c2 = 0;
3262  }
3263  }
3264  }
3265  else {
3266  t1 = thr->threadbuffer;
3267  for ( n = 0; n < thr->usenum; n++ ) { t1 += *t1; }
3268  t3 = t1;
3269  if ( extra > 0 ) {
3270  for ( i = 0; i < extra; i++ ) {
3271  thrtogo = freebuckets[i];
3272  t2 = thrtogo->threadbuffer;
3273  thrtogo->free = BUCKETFILLED;
3274  thrtogo->type = BUCKETDOINGTERMS;
3275  thrtogo->totnum = nperbucket+1;
3276  thrtogo->ddterms = 0;
3277  thrtogo->usenum = 0;
3278  thrtogo->busy = BUCKETASSIGNED;
3279  thrtogo->firstterm = numinput;
3280  numinput += nperbucket+1;
3281  for ( n = 0; n <= nperbucket; n++ ) {
3282  j = *t1; NCOPY(t2,t1,j);
3283  }
3284  *t2 = 0;
3285  }
3286  }
3287  if ( nperbucket > 0 ) {
3288  for ( i = extra; i < numfree; i++ ) {
3289  thrtogo = freebuckets[i];
3290  t2 = thrtogo->threadbuffer;
3291  thrtogo->free = BUCKETFILLED;
3292  thrtogo->type = BUCKETDOINGTERMS;
3293  thrtogo->totnum = nperbucket;
3294  thrtogo->ddterms = 0;
3295  thrtogo->usenum = 0;
3296  thrtogo->busy = BUCKETASSIGNED;
3297  thrtogo->firstterm = numinput;
3298  numinput += nperbucket;
3299  for ( n = 0; n < nperbucket; n++ ) {
3300  j = *t1; NCOPY(t2,t1,j);
3301  }
3302  *t2 = 0;
3303  }
3304  }
3305  }
3306  *t3 = 0; /* This is some form of extra insurance */
3307  if ( thr->free == BUCKETRELEASED && thr->busy == BUCKETTOBERELEASED ) {
3308  thr->free = BUCKETATEND; thr->busy = BUCKETPREPARINGTERM;
3309  }
3310  UNLOCK(thr->lock);
3311  return(1);
3312 }
3313 
3314 /*
3315  #] LoadReadjusted :
3316  #[ SortStrategy :
3317 */
3350 /*
3351  #] SortStrategy :
3352  #[ PutToMaster :
3353 */
3376 int PutToMaster(PHEAD WORD *term)
3377 {
3378  int i,j,nexti,ret = 0;
3379  WORD *t, *fill, *top, zero = 0;
3380  if ( term == 0 ) { /* Mark the end of the expression */
3381  t = &zero; j = 1;
3382  }
3383  else {
3384  t = term; ret = j = *term;
3385  if ( j == 0 ) { j = 1; } /* Just in case there is a spurious end */
3386  }
3387  i = AT.SB.FillBlock; /* The block we are working at */
3388  fill = AT.SB.MasterFill[i]; /* Where we are filling */
3389  top = AT.SB.MasterStop[i]; /* End of the block */
3390  while ( j > 0 ) {
3391  while ( j > 0 && fill < top ) {
3392  *fill++ = *t++; j--;
3393  }
3394  if ( j > 0 ) {
3395 /*
3396  We reached the end of the block.
3397  Get the next block and release this block.
3398  The order is important. This is why there should be at least
3399  4 blocks or deadlocks can occur.
3400 */
3401  nexti = i+1;
3402  if ( nexti > AT.SB.MasterNumBlocks ) {
3403  nexti = 1;
3404  }
3405  LOCK(AT.SB.MasterBlockLock[nexti]);
3406  UNLOCK(AT.SB.MasterBlockLock[i]);
3407  AT.SB.MasterFill[i] = AT.SB.MasterStart[i];
3408  AT.SB.FillBlock = i = nexti;
3409  fill = AT.SB.MasterStart[i];
3410  top = AT.SB.MasterStop[i];
3411  }
3412  }
3413  AT.SB.MasterFill[i] = fill;
3414  return(ret);
3415 }
3416 
3417 /*
3418  #] PutToMaster :
3419  #[ SortBotOut :
3420 */
3421 
3422 #ifdef WITHSORTBOTS
3423 
3432 int
3433 SortBotOut(PHEAD WORD *term)
3434 {
3435  WORD im;
3436 
3437  if ( AT.identity != 0 ) return(PutToMaster(BHEAD term));
3438 
3439  if ( term == 0 ) {
3440  if ( FlushOut(&SortBotPosition,AR.outfile,1) ) return(-1);
3441  ADDPOS(AT.SS->SizeInFile[0],1);
3442  return(0);
3443  }
3444  else {
3445  numberofterms++;
3446  if ( ( im = PutOut(BHEAD term,&SortBotPosition,AR.outfile,1) ) < 0 ) {
3447  MLOCK(ErrorMessageLock);
3448  MesPrint("Called from MasterMerge/SortBotOut");
3449  MUNLOCK(ErrorMessageLock);
3450  return(-1);
3451  }
3452  ADDPOS(AT.SS->SizeInFile[0],im);
3453  return(im);
3454  }
3455 }
3456 
3457 #endif
3458 
3459 /*
3460  #] SortBotOut :
3461  #[ MasterMerge :
3462 */
3480 int MasterMerge()
3481 {
3482  ALLPRIVATES *B0 = AB[0], *B = 0;
3483  SORTING *S = AT0.SS;
3484  WORD **poin, **poin2, ul, k, i, im, *m1, j;
3485  WORD lpat, mpat, level, l1, l2, r1, r2, r3, c;
3486  WORD *m2, *m3, r31, r33, ki, *rr;
3487  UWORD *coef;
3488  POSITION position;
3489  FILEHANDLE *fin, *fout;
3490 #ifdef WITHSORTBOTS
3491  if ( numberofworkers > 2 ) return(SortBotMasterMerge());
3492 #endif
3493  fin = &S->file;
3494  S->PolyFlag = AR0.PolyFun ? AR0.PolyFunType: 0;
3495  S->TermsLeft = 0;
3496  coef = AN0.SoScratC;
3497  poin = S->poina; poin2 = S->poin2a;
3498  rr = AR0.CompressPointer;
3499  *rr = 0;
3500 /*
3501  #[ Setup :
3502 */
3503  S->inNum = numberofthreads;
3504  fout = AR0.outfile;
3505 /*
3506  Load the patches. The threads have to finish their sort first.
3507 */
3508  S->lPatch = S->inNum - 1;
3509 /*
3510  Claim all zero blocks. We need them anyway.
3511  In principle the workers should never get into these.
3512  We also claim all last blocks. This is a safety procedure that
3513  should prevent the workers from working their way around the clock
3514  before the master gets started again.
3515 */
3516  AS.MasterSort = 1;
3517  numberclaimed = 0;
3518  for ( i = 1; i <= S->lPatch; i++ ) {
3519  B = AB[i];
3520  LOCK(AT.SB.MasterBlockLock[0]);
3521  LOCK(AT.SB.MasterBlockLock[AT.SB.MasterNumBlocks]);
3522  }
3523 /*
3524  Now wake up the threads and have them start their final sorting.
3525  They should start with claiming their block and the master is
3526  not allowed to continue until that has been done.
3527  This waiting of the master will be done below in MasterWaitAllBlocks
3528 */
3529  for ( i = 0; i < S->lPatch; i++ ) {
3530  GetThread(i+1);
3531  WakeupThread(i+1,FINISHEXPRESSION);
3532  }
3533 /*
3534  Prepare the output file.
3535 */
3536  if ( fout->handle >= 0 ) {
3537  PUTZERO(position);
3538  SeekFile(fout->handle,&position,SEEK_END);
3539  ADDPOS(position,((fout->POfill-fout->PObuffer)*sizeof(WORD)));
3540  }
3541  else {
3542  SETBASEPOSITION(position,(fout->POfill-fout->PObuffer)*sizeof(WORD));
3543  }
3544 /*
3545  Wait for all threads to finish loading their first block.
3546 */
3547  MasterWaitAllBlocks();
3548 /*
3549  Claim all first blocks.
3550  We don't release the last blocks.
3551  The strategy is that we always keep the previous block.
3552  In principle it looks like it isn't needed for the last block but
3553  actually it is to keep the front from overrunning the tail when writing.
3554 */
3555  for ( i = 1; i <= S->lPatch; i++ ) {
3556  B = AB[i];
3557  LOCK(AT.SB.MasterBlockLock[1]);
3558  AT.SB.MasterBlock = 1;
3559  }
3560 /*
3561  #] Setup :
3562 
3563  Now construct the tree:
3564 */
3565  lpat = 1;
3566  do { lpat <<= 1; } while ( lpat < S->lPatch );
3567  mpat = ( lpat >> 1 ) - 1;
3568  k = lpat - S->lPatch;
3569 /*
3570  k is the number of empty places in the tree. they will
3571  be at the even positions from 2 to 2*k
3572 */
3573  for ( i = 1; i < lpat; i++ ) { S->tree[i] = -1; }
3574  for ( i = 1; i <= k; i++ ) {
3575  im = ( i << 1 ) - 1;
3576  poin[im] = AB[i]->T.SB.MasterStart[AB[i]->T.SB.MasterBlock];
3577  poin2[im] = poin[im] + *(poin[im]);
3578  S->used[i] = im;
3579  S->ktoi[im] = i-1;
3580  S->tree[mpat+i] = 0;
3581  poin[im-1] = poin2[im-1] = 0;
3582  }
3583  for ( i = (k<<1)+1; i <= lpat; i++ ) {
3584  S->used[i-k] = i;
3585  S->ktoi[i] = i-k-1;
3586  poin[i] = AB[i-k]->T.SB.MasterStart[AB[i-k]->T.SB.MasterBlock];
3587  poin2[i] = poin[i] + *(poin[i]);
3588  }
3589 /*
3590  the array poin tells the position of the i-th element of the S->tree
3591  'S->used' is a stack with the S->tree elements that need to be entered
3592  into the S->tree. at the beginning this is S->lPatch. during the
3593  sort there will be only very few elements.
3594  poin2 is the next value of poin. it has to be determined
3595  before the comparisons as the position or the size of the
3596  term indicated by poin may change.
3597  S->ktoi translates a S->tree element back to its stream number.
3598 
3599  start the sort
3600 */
3601  level = S->lPatch;
3602 /*
3603  introduce one term
3604 */
3605 OneTerm:
3606  k = S->used[level];
3607  i = k + lpat - 1;
3608  if ( !*(poin[k]) ) {
3609  do { if ( !( i >>= 1 ) ) goto EndOfMerge; } while ( !S->tree[i] );
3610  if ( S->tree[i] == -1 ) {
3611  S->tree[i] = 0;
3612  level--;
3613  goto OneTerm;
3614  }
3615  k = S->tree[i];
3616  S->used[level] = k;
3617  S->tree[i] = 0;
3618  }
3619 /*
3620  move terms down the tree
3621 */
3622  while ( i >>= 1 ) {
3623  if ( S->tree[i] > 0 ) {
3624  if ( ( c = CompareTerms(B0,poin[S->tree[i]],poin[k],(WORD)0) ) > 0 ) {
3625 /*
3626  S->tree[i] is the smaller. Exchange and go on.
3627 */
3628  S->used[level] = S->tree[i];
3629  S->tree[i] = k;
3630  k = S->used[level];
3631  }
3632  else if ( !c ) { /* Terms are equal */
3633 /*
3634  S->TermsLeft--;
3635  Here the terms are equal and their coefficients
3636  have to be added.
3637 */
3638  l1 = *( m1 = poin[S->tree[i]] );
3639  l2 = *( m2 = poin[k] );
3640  if ( S->PolyWise ) { /* Here we work with PolyFun */
3641  WORD *tt1, *w;
3642  tt1 = m1;
3643  m1 += S->PolyWise;
3644  m2 += S->PolyWise;
3645  if ( S->PolyFlag == 2 ) {
3646  w = poly_ratfun_add(B0,m1,m2);
3647  if ( *tt1 + w[1] - m1[1] > AM.MaxTer/((LONG)sizeof(WORD)) ) {
3648  MLOCK(ErrorMessageLock);
3649  MesPrint("Term too complex in PolyRatFun addition. MaxTermSize of %10l is too small",AM.MaxTer);
3650  MUNLOCK(ErrorMessageLock);
3651  Terminate(-1);
3652  }
3653  AT0.WorkPointer = w;
3654  if ( w[FUNHEAD] == -SNUMBER && w[FUNHEAD+1] == 0 && w[1] > FUNHEAD ) {
3655  goto cancelled;
3656  }
3657  }
3658  else {
3659  w = AT0.WorkPointer;
3660  if ( w + m1[1] + m2[1] > AT0.WorkTop ) {
3661  MLOCK(ErrorMessageLock);
3662  MesPrint("MasterMerge: A WorkSpace of %10l is too small",AM.WorkSize);
3663  MUNLOCK(ErrorMessageLock);
3664  Terminate(-1);
3665  }
3666  AddArgs(B0,m1,m2,w);
3667  }
3668  r1 = w[1];
3669  if ( r1 <= FUNHEAD
3670  || ( w[FUNHEAD] == -SNUMBER && w[FUNHEAD+1] == 0 ) )
3671  { goto cancelled; }
3672  if ( r1 == m1[1] ) {
3673  NCOPY(m1,w,r1);
3674  }
3675  else if ( r1 < m1[1] ) {
3676  r2 = m1[1] - r1;
3677  m2 = w + r1;
3678  m1 += m1[1];
3679  while ( --r1 >= 0 ) *--m1 = *--m2;
3680  m2 = m1 - r2;
3681  r1 = S->PolyWise;
3682  while ( --r1 >= 0 ) *--m1 = *--m2;
3683  *m1 -= r2;
3684  poin[S->tree[i]] = m1;
3685  }
3686  else {
3687  r2 = r1 - m1[1];
3688  m2 = tt1 - r2;
3689  r1 = S->PolyWise;
3690  m1 = tt1;
3691  *m1 += r2;
3692  poin[S->tree[i]] = m2;
3693  NCOPY(m2,m1,r1);
3694  r1 = w[1];
3695  NCOPY(m2,w,r1);
3696  }
3697  }
3698  else {
3699  r1 = *( m1 += l1 - 1 );
3700  m1 -= ABS(r1) - 1;
3701  r1 = ( ( r1 > 0 ) ? (r1-1) : (r1+1) ) >> 1;
3702  r2 = *( m2 += l2 - 1 );
3703  m2 -= ABS(r2) - 1;
3704  r2 = ( ( r2 > 0 ) ? (r2-1) : (r2+1) ) >> 1;
3705 
3706  if ( AddRat(B0,(UWORD *)m1,r1,(UWORD *)m2,r2,coef,&r3) ) {
3707  MLOCK(ErrorMessageLock);
3708  MesCall("MasterMerge");
3709  MUNLOCK(ErrorMessageLock);
3710  SETERROR(-1)
3711  }
3712 
3713  if ( AN.ncmod != 0 ) {
3714  if ( ( AC.modmode & POSNEG ) != 0 ) {
3715  NormalModulus(coef,&r3);
3716  }
3717  else if ( BigLong(coef,r3,(UWORD *)AC.cmod,ABS(AN.ncmod)) >= 0 ) {
3718  WORD ii;
3719  SubPLon(coef,r3,(UWORD *)AC.cmod,ABS(AN.ncmod),coef,&r3);
3720  coef[r3] = 1;
3721  for ( ii = 1; ii < r3; ii++ ) coef[r3+ii] = 0;
3722  }
3723  }
3724  r3 <<= 1;
3725  r33 = ( r3 > 0 ) ? ( r3 + 1 ) : ( r3 - 1 );
3726  if ( r3 < 0 ) r3 = -r3;
3727  if ( r1 < 0 ) r1 = -r1;
3728  r1 <<= 1;
3729  r31 = r3 - r1;
3730  if ( !r3 ) { /* Terms cancel */
3731 cancelled:
3732  ul = S->used[level] = S->tree[i];
3733  S->tree[i] = -1;
3734 /*
3735  We skip to the next term in stream ul
3736 */
3737  im = *poin2[ul];
3738  poin[ul] = poin2[ul];
3739  ki = S->ktoi[ul];
3740  if ( (poin[ul] + im + COMPINC) >=
3741  AB[ki+1]->T.SB.MasterStop[AB[ki+1]->T.SB.MasterBlock]
3742  && im > 0 ) {
3743 /*
3744  We made it to the end of the block. We have to
3745  release the previous block and claim the next.
3746 */
3747  B = AB[ki+1];
3748  i = AT.SB.MasterBlock;
3749  if ( i == 1 ) {
3750  UNLOCK(AT.SB.MasterBlockLock[AT.SB.MasterNumBlocks]);
3751  }
3752  else {
3753  UNLOCK(AT.SB.MasterBlockLock[i-1]);
3754  }
3755  if ( i == AT.SB.MasterNumBlocks ) {
3756 /*
3757  Move the remainder down into block 0
3758 */
3759  WORD *from, *to;
3760  to = AT.SB.MasterStart[1];
3761  from = AT.SB.MasterStop[i];
3762  while ( from > poin[ul] ) *--to = *--from;
3763  poin[ul] = to;
3764  i = 1;
3765  }
3766  else { i++; }
3767  LOCK(AT.SB.MasterBlockLock[i]);
3768  AT.SB.MasterBlock = i;
3769  poin2[ul] = poin[ul] + im;
3770  }
3771  else {
3772  poin2[ul] += im;
3773  }
3774  S->used[++level] = k;
3775 /* S->TermsLeft--; */
3776  }
3777  else if ( !r31 ) { /* copy coef into term1 */
3778  goto CopCof2;
3779  }
3780  else if ( r31 < 0 ) { /* copy coef into term1
3781  and adjust the length of term1 */
3782  goto CopCoef;
3783  }
3784  else {
3785 /*
3786  this is the dreaded calamity.
3787  is there enough space?
3788 */
3789  if( (poin[S->tree[i]]+l1+r31) >= poin2[S->tree[i]] ) {
3790 /*
3791  no space! now the special trick for which
3792  we left 2*maxlng spaces open at the beginning
3793  of each patch.
3794 */
3795  if ( (l1 + r31)*((LONG)sizeof(WORD)) >= AM.MaxTer ) {
3796  MLOCK(ErrorMessageLock);
3797  MesPrint("MasterMerge: Coefficient overflow during sort");
3798  MUNLOCK(ErrorMessageLock);
3799  goto ReturnError;
3800  }
3801  m2 = poin[S->tree[i]];
3802  m3 = ( poin[S->tree[i]] -= r31 );
3803  do { *m3++ = *m2++; } while ( m2 < m1 );
3804  m1 = m3;
3805  }
3806 CopCoef:
3807  *(poin[S->tree[i]]) += r31;
3808 CopCof2:
3809  m2 = (WORD *)coef; im = r3;
3810  NCOPY(m1,m2,im);
3811  *m1 = r33;
3812  }
3813  }
3814 /*
3815  Now skip to the next term in stream k.
3816 */
3817 NextTerm:
3818  im = poin2[k][0];
3819  poin[k] = poin2[k];
3820  ki = S->ktoi[k];
3821  if ( (poin[k] + im + COMPINC) >=
3822  AB[ki+1]->T.SB.MasterStop[AB[ki+1]->T.SB.MasterBlock]
3823  && im > 0 ) {
3824 /*
3825  We made it to the end of the block. We have to
3826  release the previous block and claim the next.
3827 */
3828  B = AB[ki+1];
3829  i = AT.SB.MasterBlock;
3830  if ( i == 1 ) {
3831  UNLOCK(AT.SB.MasterBlockLock[AT.SB.MasterNumBlocks]);
3832  }
3833  else {
3834  UNLOCK(AT.SB.MasterBlockLock[i-1]);
3835  }
3836  if ( i == AT.SB.MasterNumBlocks ) {
3837 /*
3838  Move the remainder down into block 0
3839 */
3840  WORD *from, *to;
3841  to = AT.SB.MasterStart[1];
3842  from = AT.SB.MasterStop[i];
3843  while ( from > poin[k] ) *--to = *--from;
3844  poin[k] = to;
3845  i = 1;
3846  }
3847  else { i++; }
3848  LOCK(AT.SB.MasterBlockLock[i]);
3849  AT.SB.MasterBlock = i;
3850  poin2[k] = poin[k] + im;
3851  }
3852  else {
3853  poin2[k] += im;
3854  }
3855  goto OneTerm;
3856  }
3857  }
3858  else if ( S->tree[i] < 0 ) {
3859  S->tree[i] = k;
3860  level--;
3861  goto OneTerm;
3862  }
3863  }
3864 /*
3865  found the smallest in the set. indicated by k.
3866  write to its destination.
3867 */
3868  S->TermsLeft++;
3869  if ( ( im = PutOut(B0,poin[k],&position,fout,1) ) < 0 ) {
3870  MLOCK(ErrorMessageLock);
3871  MesPrint("Called from MasterMerge with k = %d (stream %d)",k,S->ktoi[k]);
3872  MUNLOCK(ErrorMessageLock);
3873  goto ReturnError;
3874  }
3875  ADDPOS(S->SizeInFile[0],im);
3876  goto NextTerm;
3877 EndOfMerge:
3878  if ( FlushOut(&position,fout,1) ) goto ReturnError;
3879  ADDPOS(S->SizeInFile[0],1);
3880  CloseFile(fin->handle);
3881  remove(fin->name);
3882  fin->handle = -1;
3883  position = S->SizeInFile[0];
3884  MULPOS(position,sizeof(WORD));
3885  S->GenTerms = 0;
3886  for ( j = 1; j <= numberofworkers; j++ ) {
3887  S->GenTerms += AB[j]->T.SS->GenTerms;
3888  }
3889  WriteStats(&position,2);
3890  Expressions[AR0.CurExpr].counter = S->TermsLeft;
3891 /*
3892  Release all locks
3893 */
3894  for ( i = 1; i <= S->lPatch; i++ ) {
3895  B = AB[i];
3896  UNLOCK(AT.SB.MasterBlockLock[0]);
3897  if ( AT.SB.MasterBlock == 1 ) {
3898  UNLOCK(AT.SB.MasterBlockLock[AT.SB.MasterNumBlocks]);
3899  }
3900  else {
3901  UNLOCK(AT.SB.MasterBlockLock[AT.SB.MasterBlock-1]);
3902  }
3903  UNLOCK(AT.SB.MasterBlockLock[AT.SB.MasterBlock]);
3904  }
3905  AS.MasterSort = 0;
3906  return(0);
3907 ReturnError:
3908  for ( i = 1; i <= S->lPatch; i++ ) {
3909  B = AB[i];
3910  UNLOCK(AT.SB.MasterBlockLock[0]);
3911  if ( AT.SB.MasterBlock == 1 ) {
3912  UNLOCK(AT.SB.MasterBlockLock[AT.SB.MasterNumBlocks]);
3913  }
3914  else {
3915  UNLOCK(AT.SB.MasterBlockLock[AT.SB.MasterBlock-1]);
3916  }
3917  UNLOCK(AT.SB.MasterBlockLock[AT.SB.MasterBlock]);
3918  }
3919  AS.MasterSort = 0;
3920  return(-1);
3921 }
3922 
3923 /*
3924  #] MasterMerge :
3925  #[ SortBotMasterMerge :
3926 */
3927 
3928 #ifdef WITHSORTBOTS
3929 
3945 int SortBotMasterMerge()
3946 {
3947  FILEHANDLE *fin, *fout;
3948  ALLPRIVATES *B = AB[0], *BB;
3949  POSITION position;
3950  SORTING *S = AT.SS;
3951  int i, j;
3952 /*
3953  Get the sortbots get to claim their writing blocks.
3954  We have to wait till all have been claimed because they also have to
3955  claim the last writing blocks of the workers to prevent the head of
3956  the circular buffer to overrun the tail.
3957 
3958  Before waiting we can do some needed initializations.
3959  Also the master has to claim the last writing blocks of its input.
3960 */
3961  topsortbotavailables = 0;
3962  for ( i = numberofworkers+1; i <= numberofworkers+numberofsortbots; i++ ) {
3963  WakeupThread(i,INISORTBOT);
3964  }
3965 
3966  AS.MasterSort = 1;
3967  fout = AR.outfile;
3968  numberofterms = 0;
3969  AR.CompressPointer[0] = 0;
3970  numberclaimed = 0;
3971  BB = AB[AT.SortBotIn1];
3972  LOCK(BB->T.SB.MasterBlockLock[BB->T.SB.MasterNumBlocks]);
3973  BB = AB[AT.SortBotIn2];
3974  LOCK(BB->T.SB.MasterBlockLock[BB->T.SB.MasterNumBlocks]);
3975 
3976  MasterWaitAllSortBots();
3977 /*
3978  Now we can start up the workers. They will claim their writing blocks.
3979  Here the master will wait till all writing blocks have been claimed.
3980 */
3981  for ( i = 1; i <= numberofworkers; i++ ) {
3982  j = GetThread(i);
3983  WakeupThread(i,FINISHEXPRESSION);
3984  }
3985 /*
3986  Prepare the output file in the mean time.
3987 */
3988  if ( fout->handle >= 0 ) {
3989  PUTZERO(SortBotPosition);
3990  SeekFile(fout->handle,&SortBotPosition,SEEK_END);
3991  ADDPOS(SortBotPosition,((fout->POfill-fout->PObuffer)*sizeof(WORD)));
3992  }
3993  else {
3994  SETBASEPOSITION(SortBotPosition,(fout->POfill-fout->PObuffer)*sizeof(WORD));
3995  }
3996  MasterWaitAllBlocks();
3997 /*
3998  Now we can start the sortbots after which the master goes in
3999  sortbot mode to do its part of the job (the very final merge and
4000  the writing to output file).
4001 */
4002  topsortbotavailables = 0;
4003  for ( i = numberofworkers+1; i <= numberofworkers+numberofsortbots; i++ ) {
4004  WakeupThread(i,RUNSORTBOT);
4005  }
4006  if ( SortBotMerge(BHEAD0) ) {
4007  MLOCK(ErrorMessageLock);
4008  MesPrint("Called from SortBotMasterMerge");
4009  MUNLOCK(ErrorMessageLock);
4010  AS.MasterSort = 0;
4011  return(-1);
4012  }
4013 /*
4014  And next the cleanup
4015 */
4016  if ( S->file.handle >= 0 )
4017  {
4018  fin = &S->file;
4019  CloseFile(fin->handle);
4020  remove(fin->name);
4021  fin->handle = -1;
4022  }
4023  position = S->SizeInFile[0];
4024  MULPOS(position,sizeof(WORD));
4025  S->GenTerms = 0;
4026  for ( j = 1; j <= numberofworkers; j++ ) {
4027  S->GenTerms += AB[j]->T.SS->GenTerms;
4028  }
4029  S->TermsLeft = numberofterms;
4030  WriteStats(&position,2);
4031  Expressions[AR.CurExpr].counter = S->TermsLeft;
4032  AS.MasterSort = 0;
4033 /*
4034  The next statement is to prevent one of the sortbots not having
4035  completely cleaned up before the next module starts.
4036  If this statement is omitted every once in a while one of the sortbots
4037  is still running when the next expression starts and misses its
4038  initialization. The result is usually disastrous.
4039 */
4040  MasterWaitAllSortBots();
4041 
4042  return(0);
4043 }
4044 
4045 #endif
4046 
4047 /*
4048  #] SortBotMasterMerge :
4049  #[ SortBotMerge :
4050 */
4051 
4052 #ifdef WITHSORTBOTS
4053 
4059 int SortBotMerge(PHEAD0)
4060 {
4061  GETBIDENTITY
4062  ALLPRIVATES *Bin1 = AB[AT.SortBotIn1],*Bin2 = AB[AT.SortBotIn2];
4063  WORD *term1, *term2, *next, *wp;
4064  int blin1, blin2; /* Current block numbers */
4065  int error = 0;
4066  WORD l1, l2, *m1, *m2, *w, r1, r2, r3, r33, r31, *tt1, ii;
4067  WORD *to, *from, im, c;
4068  UWORD *coef;
4069  SORTING *S = AT.SS;
4070 /*
4071  Set the pointers to the input terms and the output space
4072 */
4073  coef = AN.SoScratC;
4074  blin1 = 1;
4075  blin2 = 1;
4076  if ( AT.identity == 0 ) {
4077  wp = AT.WorkPointer;
4078  }
4079  else {
4080  wp = AT.WorkPointer = AT.WorkSpace;
4081  }
4082 /*
4083  Get the locks for reading the input
4084  This means that we can start once these locks have been cleared
4085  which means that there will be input.
4086 */
4087  LOCK(Bin1->T.SB.MasterBlockLock[blin1]);
4088  LOCK(Bin2->T.SB.MasterBlockLock[blin2]);
4089 
4090  term1 = Bin1->T.SB.MasterStart[blin1];
4091  term2 = Bin2->T.SB.MasterStart[blin2];
4092  AT.SB.FillBlock = 1;
4093 /*
4094  Now the main loop. Keep going until one of the two hits the end.
4095 */
4096  while ( *term1 && *term2 ) {
4097  if ( ( c = CompareTerms(BHEAD term1,term2,(WORD)0) ) > 0 ) {
4098 /*
4099  #[ One is smallest :
4100 */
4101  if ( SortBotOut(BHEAD term1) < 0 ) {
4102  MLOCK(ErrorMessageLock);
4103  MesPrint("Called from SortBotMerge with thread = %d",AT.identity);
4104  MUNLOCK(ErrorMessageLock);
4105  error = -1;
4106  goto ReturnError;
4107  }
4108  im = *term1;
4109  next = term1 + im;
4110  if ( next >= Bin1->T.SB.MasterStop[blin1] || ( *next &&
4111  next+*next+COMPINC > Bin1->T.SB.MasterStop[blin1] ) ) {
4112  if ( blin1 == 1 ) {
4113  UNLOCK(Bin1->T.SB.MasterBlockLock[Bin1->T.SB.MasterNumBlocks]);
4114  }
4115  else {
4116  UNLOCK(Bin1->T.SB.MasterBlockLock[blin1-1]);
4117  }
4118  if ( blin1 == Bin1->T.SB.MasterNumBlocks ) {
4119 /*
4120  Move the remainder down into block 0
4121 */
4122  to = Bin1->T.SB.MasterStart[1];
4123  from = Bin1->T.SB.MasterStop[Bin1->T.SB.MasterNumBlocks];
4124  while ( from > term1 ) *--to = *--from;
4125  term1 = to;
4126  blin1 = 1;
4127  }
4128  else {
4129  blin1++;
4130  }
4131  LOCK(Bin1->T.SB.MasterBlockLock[blin1]);
4132  Bin1->T.SB.MasterBlock = blin1;
4133  }
4134  term1 += im;
4135 /*
4136  #] One is smallest :
4137 */
4138  }
4139  else if ( c < 0 ) {
4140 /*
4141  #[ Two is smallest :
4142 */
4143  if ( SortBotOut(BHEAD term2) < 0 ) {
4144  MLOCK(ErrorMessageLock);
4145  MesPrint("Called from SortBotMerge with thread = %d",AT.identity);
4146  MUNLOCK(ErrorMessageLock);
4147  error = -1;
4148  goto ReturnError;
4149  }
4150 next2: im = *term2;
4151  next = term2 + im;
4152  if ( next >= Bin2->T.SB.MasterStop[blin2] || ( *next
4153  && next+*next+COMPINC > Bin2->T.SB.MasterStop[blin2] ) ) {
4154  if ( blin2 == 1 ) {
4155  UNLOCK(Bin2->T.SB.MasterBlockLock[Bin2->T.SB.MasterNumBlocks]);
4156  }
4157  else {
4158  UNLOCK(Bin2->T.SB.MasterBlockLock[blin2-1]);
4159  }
4160  if ( blin2 == Bin2->T.SB.MasterNumBlocks ) {
4161 /*
4162  Move the remainder down into block 0
4163 */
4164  to = Bin2->T.SB.MasterStart[1];
4165  from = Bin2->T.SB.MasterStop[Bin2->T.SB.MasterNumBlocks];
4166  while ( from > term2 ) *--to = *--from;
4167  term2 = to;
4168  blin2 = 1;
4169  }
4170  else {
4171  blin2++;
4172  }
4173  LOCK(Bin2->T.SB.MasterBlockLock[blin2]);
4174  Bin2->T.SB.MasterBlock = blin2;
4175  }
4176  term2 += im;
4177 /*
4178  #] Two is smallest :
4179 */
4180  }
4181  else {
4182 /*
4183  #[ Equal :
4184 */
4185  l1 = *( m1 = term1 );
4186  l2 = *( m2 = term2 );
4187  if ( S->PolyWise ) { /* Here we work with PolyFun */
4188  tt1 = m1;
4189  m1 += S->PolyWise;
4190  m2 += S->PolyWise;
4191  if ( S->PolyFlag == 2 ) {
4192  AT.WorkPointer = wp;
4193  w = poly_ratfun_add(BHEAD m1,m2);
4194  if ( *tt1 + w[1] - m1[1] > AM.MaxTer/((LONG)sizeof(WORD)) ) {
4195  MLOCK(ErrorMessageLock);
4196  MesPrint("Term too complex in PolyRatFun addition. MaxTermSize of %10l is too small",AM.MaxTer);
4197  MUNLOCK(ErrorMessageLock);
4198  Terminate(-1);
4199  }
4200  AT.WorkPointer = wp;
4201  if ( w[FUNHEAD] == -SNUMBER && w[FUNHEAD+1] == 0 && w[1] > FUNHEAD ) {
4202  goto cancelled;
4203  }
4204  }
4205  else {
4206  w = wp;
4207  if ( w + m1[1] + m2[1] > AT.WorkTop ) {
4208  MLOCK(ErrorMessageLock);
4209  MesPrint("SortBotMerge(%d): A Maxtermsize of %10l is too small",
4210  AT.identity,AM.MaxTer/sizeof(WORD));
4211  MesPrint("m1[1] = %d, m2[1] = %d, Space = %l",m1[1],m2[1],(LONG)(AT.WorkTop-wp));
4212  PrintTerm(term1,"term1");
4213  PrintTerm(term2,"term2");
4214  MesPrint("PolyWise = %d",S->PolyWise);
4215  MUNLOCK(ErrorMessageLock);
4216  Terminate(-1);
4217  }
4218  AddArgs(BHEAD m1,m2,w);
4219  }
4220  r1 = w[1];
4221  if ( r1 <= FUNHEAD
4222  || ( w[FUNHEAD] == -SNUMBER && w[FUNHEAD+1] == 0 ) )
4223  { goto cancelled; }
4224  if ( r1 == m1[1] ) {
4225  NCOPY(m1,w,r1);
4226  }
4227  else if ( r1 < m1[1] ) {
4228  r2 = m1[1] - r1;
4229  m2 = w + r1;
4230  m1 += m1[1];
4231  while ( --r1 >= 0 ) *--m1 = *--m2;
4232  m2 = m1 - r2;
4233  r1 = S->PolyWise;
4234  while ( --r1 >= 0 ) *--m1 = *--m2;
4235  *m1 -= r2;
4236  term1 = m1;
4237  }
4238  else {
4239  r2 = r1 - m1[1];
4240  m2 = tt1 - r2;
4241  r1 = S->PolyWise;
4242  m1 = tt1;
4243  *m1 += r2;
4244  term1 = m2;
4245  NCOPY(m2,m1,r1);
4246  r1 = w[1];
4247  NCOPY(m2,w,r1);
4248  }
4249  }
4250  else {
4251  r1 = *( m1 += l1 - 1 );
4252  m1 -= ABS(r1) - 1;
4253  r1 = ( ( r1 > 0 ) ? (r1-1) : (r1+1) ) >> 1;
4254  r2 = *( m2 += l2 - 1 );
4255  m2 -= ABS(r2) - 1;
4256  r2 = ( ( r2 > 0 ) ? (r2-1) : (r2+1) ) >> 1;
4257 
4258  if ( AddRat(BHEAD (UWORD *)m1,r1,(UWORD *)m2,r2,coef,&r3) ) {
4259  MLOCK(ErrorMessageLock);
4260  MesCall("SortBotMerge");
4261  MUNLOCK(ErrorMessageLock);
4262  SETERROR(-1)
4263  }
4264 
4265  if ( AN.ncmod != 0 ) {
4266  if ( ( AC.modmode & POSNEG ) != 0 ) {
4267  NormalModulus(coef,&r3);
4268  }
4269  else if ( BigLong(coef,r3,(UWORD *)AC.cmod,ABS(AN.ncmod)) >= 0 ) {
4270  SubPLon(coef,r3,(UWORD *)AC.cmod,ABS(AN.ncmod),coef,&r3);
4271  coef[r3] = 1;
4272  for ( ii = 1; ii < r3; ii++ ) coef[r3+ii] = 0;
4273  }
4274  }
4275  if ( !r3 ) { goto cancelled; }
4276  r3 <<= 1;
4277  r33 = ( r3 > 0 ) ? ( r3 + 1 ) : ( r3 - 1 );
4278  if ( r3 < 0 ) r3 = -r3;
4279  if ( r1 < 0 ) r1 = -r1;
4280  r1 <<= 1;
4281  r31 = r3 - r1;
4282  if ( !r31 ) { /* copy coef into term1 */
4283  m2 = (WORD *)coef; im = r3;
4284  NCOPY(m1,m2,im);
4285  *m1 = r33;
4286  }
4287 /*
4288  else if ( r31 < 0 ) {
4289  *term1 += r31;
4290  m2 = (WORD *)coef; im = r3;
4291  NCOPY(m1,m2,im);
4292  *m1 = r33;
4293  }
4294 */
4295  else {
4296  to = wp; from = term1;
4297  while ( from < m1 ) *to++ = *from++;
4298  from = (WORD *)coef; im = r3;
4299  NCOPY(to,from,im);
4300  *to++ = r33;
4301  wp[0] = to - wp;
4302  if ( SortBotOut(BHEAD wp) < 0 ) {
4303  MLOCK(ErrorMessageLock);
4304  MesPrint("Called from SortBotMerge with thread = %d",AT.identity);
4305  MUNLOCK(ErrorMessageLock);
4306  error = -1;
4307  goto ReturnError;
4308  }
4309  goto cancelled;
4310  }
4311  }
4312  if ( SortBotOut(BHEAD term1) < 0 ) {
4313  MLOCK(ErrorMessageLock);
4314  MesPrint("Called from SortBotMerge with thread = %d",AT.identity);
4315  MUNLOCK(ErrorMessageLock);
4316  error = -1;
4317  goto ReturnError;
4318  }
4319 cancelled:; /* Now we need two new terms */
4320  im = *term1;
4321  next = term1 + im;
4322  if ( next >= Bin1->T.SB.MasterStop[blin1] || ( *next &&
4323  next+*next+COMPINC > Bin1->T.SB.MasterStop[blin1] ) ) {
4324  if ( blin1 == 1 ) {
4325  UNLOCK(Bin1->T.SB.MasterBlockLock[Bin1->T.SB.MasterNumBlocks]);
4326  }
4327  else {
4328  UNLOCK(Bin1->T.SB.MasterBlockLock[blin1-1]);
4329  }
4330  if ( blin1 == Bin1->T.SB.MasterNumBlocks ) {
4331 /*
4332  Move the remainder down into block 0
4333 */
4334  to = Bin1->T.SB.MasterStart[1];
4335  from = Bin1->T.SB.MasterStop[Bin1->T.SB.MasterNumBlocks];
4336  while ( from > term1 ) *--to = *--from;
4337  term1 = to;
4338  blin1 = 1;
4339  }
4340  else {
4341  blin1++;
4342  }
4343  LOCK(Bin1->T.SB.MasterBlockLock[blin1]);
4344  Bin1->T.SB.MasterBlock = blin1;
4345  }
4346  term1 += im;
4347  goto next2;
4348 /*
4349  #] Equal :
4350 */
4351  }
4352  }
4353 /*
4354  Copy the tail
4355 */
4356  if ( *term1 ) {
4357 /*
4358  #[ Tail in one :
4359 */
4360  while ( *term1 ) {
4361  if ( SortBotOut(BHEAD term1) < 0 ) {
4362  MLOCK(ErrorMessageLock);
4363  MesPrint("Called from SortBotMerge with thread = %d",AT.identity);
4364  MUNLOCK(ErrorMessageLock);
4365  error = -1;
4366  goto ReturnError;
4367  }
4368  im = *term1;
4369  next = term1 + im;
4370  if ( next >= Bin1->T.SB.MasterStop[blin1] || ( *next &&
4371  next+*next+COMPINC > Bin1->T.SB.MasterStop[blin1] ) ) {
4372  if ( blin1 == 1 ) {
4373  UNLOCK(Bin1->T.SB.MasterBlockLock[Bin1->T.SB.MasterNumBlocks]);
4374  }
4375  else {
4376  UNLOCK(Bin1->T.SB.MasterBlockLock[blin1-1]);
4377  }
4378  if ( blin1 == Bin1->T.SB.MasterNumBlocks ) {
4379 /*
4380  Move the remainder down into block 0
4381 */
4382  to = Bin1->T.SB.MasterStart[1];
4383  from = Bin1->T.SB.MasterStop[Bin1->T.SB.MasterNumBlocks];
4384  while ( from > term1 ) *--to = *--from;
4385  term1 = to;
4386  blin1 = 1;
4387  }
4388  else {
4389  blin1++;
4390  }
4391  LOCK(Bin1->T.SB.MasterBlockLock[blin1]);
4392  Bin1->T.SB.MasterBlock = blin1;
4393  }
4394  term1 += im;
4395  }
4396 /*
4397  #] Tail in one :
4398 */
4399  }
4400  else if ( *term2 ) {
4401 /*
4402  #[ Tail in two :
4403 */
4404  while ( *term2 ) {
4405  if ( SortBotOut(BHEAD term2) < 0 ) {
4406  MLOCK(ErrorMessageLock);
4407  MesPrint("Called from SortBotMerge with thread = %d",AT.identity);
4408  MUNLOCK(ErrorMessageLock);
4409  error = -1;
4410  goto ReturnError;
4411  }
4412  im = *term2;
4413  next = term2 + im;
4414  if ( next >= Bin2->T.SB.MasterStop[blin2] || ( *next
4415  && next+*next+COMPINC > Bin2->T.SB.MasterStop[blin2] ) ) {
4416  if ( blin2 == 1 ) {
4417  UNLOCK(Bin2->T.SB.MasterBlockLock[Bin2->T.SB.MasterNumBlocks]);
4418  }
4419  else {
4420  UNLOCK(Bin2->T.SB.MasterBlockLock[blin2-1]);
4421  }
4422  if ( blin2 == Bin2->T.SB.MasterNumBlocks ) {
4423 /*
4424  Move the remainder down into block 0
4425 */
4426  to = Bin2->T.SB.MasterStart[1];
4427  from = Bin2->T.SB.MasterStop[Bin2->T.SB.MasterNumBlocks];
4428  while ( from > term2 ) *--to = *--from;
4429  term2 = to;
4430  blin2 = 1;
4431  }
4432  else {
4433  blin2++;
4434  }
4435  LOCK(Bin2->T.SB.MasterBlockLock[blin2]);
4436  Bin2->T.SB.MasterBlock = blin2;
4437  }
4438  term2 += im;
4439  }
4440 /*
4441  #] Tail in two :
4442 */
4443  }
4444  SortBotOut(BHEAD 0);
4445 ReturnError:;
4446 /*
4447  Release all locks
4448 */
4449  UNLOCK(Bin1->T.SB.MasterBlockLock[blin1]);
4450  if ( blin1 > 1 ) {
4451  UNLOCK(Bin1->T.SB.MasterBlockLock[blin1-1]);
4452  }
4453  else {
4454  UNLOCK(Bin1->T.SB.MasterBlockLock[Bin1->T.SB.MasterNumBlocks]);
4455  }
4456  UNLOCK(Bin2->T.SB.MasterBlockLock[blin2]);
4457  if ( blin2 > 1 ) {
4458  UNLOCK(Bin2->T.SB.MasterBlockLock[blin2-1]);
4459  }
4460  else {
4461  UNLOCK(Bin2->T.SB.MasterBlockLock[Bin2->T.SB.MasterNumBlocks]);
4462  }
4463  if ( AT.identity > 0 ) {
4464  UNLOCK(AT.SB.MasterBlockLock[AT.SB.FillBlock]);
4465  }
4466 /*
4467  And that was all folks
4468 */
4469  return(error);
4470 }
4471 
4472 #endif
4473 
4474 /*
4475  #] SortBotMerge :
4476  #[ IniSortBlocks :
4477 */
4478 
4479 static int SortBlocksInitialized = 0;
4480 
4487 int IniSortBlocks(int numworkers)
4488 {
4489  ALLPRIVATES *B;
4490  SORTING *S;
4491  LONG totalsize, workersize, blocksize, numberofterms;
4492  int maxter, id, j;
4493  int numberofblocks = NUMBEROFBLOCKSINSORT, numparts;
4494  WORD *w;
4495 
4496  if ( SortBlocksInitialized ) return(0);
4497  SortBlocksInitialized = 1;
4498  if ( numworkers == 0 ) return(0);
4499 
4500 #ifdef WITHSORTBOTS
4501  if ( numworkers > 2 ) {
4502  numparts = 2*numworkers - 2;
4503  numberofblocks = numberofblocks/2;
4504  }
4505  else {
4506  numparts = numworkers;
4507  }
4508 #else
4509  numparts = numworkers;
4510 #endif
4511  S = AM.S0;
4512  totalsize = S->LargeSize + S->SmallEsize;
4513  workersize = totalsize / numparts;
4514  maxter = AM.MaxTer/sizeof(WORD);
4515  blocksize = ( workersize - maxter )/numberofblocks;
4516  numberofterms = blocksize / maxter;
4517  if ( numberofterms < MINIMUMNUMBEROFTERMS ) {
4518 /*
4519  This should have been taken care of in RecalcSetups.
4520 */
4521  MesPrint("We have a problem with the size of the blocks in IniSortBlocks");
4522  Terminate(-1);
4523  }
4524 /*
4525  Layout: For each worker
4526  block 0: size is maxter WORDS
4527  numberofblocks blocks of size blocksize WORDS
4528 */
4529  w = S->lBuffer;
4530  if ( w == 0 ) w = S->sBuffer;
4531  for ( id = 1; id <= numparts; id++ ) {
4532  B = AB[id];
4533  AT.SB.MasterBlockLock = (pthread_mutex_t *)Malloc1(
4534  sizeof(pthread_mutex_t)*(numberofblocks+1),"MasterBlockLock");
4535  AT.SB.MasterStart = (WORD **)Malloc1(sizeof(WORD *)*(numberofblocks+1)*3,"MasterBlock");
4536  AT.SB.MasterFill = AT.SB.MasterStart + (numberofblocks+1);
4537  AT.SB.MasterStop = AT.SB.MasterFill + (numberofblocks+1);
4538  AT.SB.MasterNumBlocks = numberofblocks;
4539  AT.SB.MasterBlock = 0;
4540  AT.SB.FillBlock = 0;
4541  AT.SB.MasterFill[0] = AT.SB.MasterStart[0] = w;
4542  w += maxter;
4543  AT.SB.MasterStop[0] = w;
4544  AT.SB.MasterBlockLock[0] = dummylock;
4545  for ( j = 1; j <= numberofblocks; j++ ) {
4546  AT.SB.MasterFill[j] = AT.SB.MasterStart[j] = w;
4547  w += blocksize;
4548  AT.SB.MasterStop[j] = w;
4549  AT.SB.MasterBlockLock[j] = dummylock;
4550  }
4551  }
4552  if ( w > S->sTop2 ) {
4553  MesPrint("Counting problem in IniSortBlocks");
4554  Terminate(-1);
4555  }
4556  return(0);
4557 }
4558 
4559 /*
4560  #] IniSortBlocks :
4561  #[ DefineSortBotTree :
4562 */
4563 
4564 #ifdef WITHSORTBOTS
4565 
4571 void DefineSortBotTree()
4572 {
4573  ALLPRIVATES *B;
4574  int n, i, from;
4575  if ( numberofworkers <= 2 ) return;
4576  n = numberofworkers*2-2;
4577  for ( i = numberofworkers+1, from = 1; i <= n; i++ ) {
4578  B = AB[i];
4579  AT.SortBotIn1 = from++;
4580  AT.SortBotIn2 = from++;
4581  }
4582  B = AB[0];
4583  AT.SortBotIn1 = from++;
4584  AT.SortBotIn2 = from++;
4585 }
4586 
4587 #endif
4588 
4589 /*
4590  #] DefineSortBotTree :
4591  #[ GetTerm2 :
4592 
4593  Routine does a GetTerm but only when a bracket index is involved and
4594  only from brackets that have been judged not suitable for treatment
4595  as complete brackets by a single worker. Whether or not a bracket should
4596  be treated by a single worker is decided by TreatIndexEntry
4597 */
4598 
4599 WORD GetTerm2(PHEAD WORD *term)
4600 {
4601  WORD *ttco, *tt, retval;
4602  LONG n,i;
4603  FILEHANDLE *fi;
4604  EXPRESSIONS e = AN.expr;
4605  BRACKETINFO *b = e->bracketinfo;
4606  BRACKETINDEX *bi = b->indexbuffer;
4607  POSITION where, eonfile = AS.OldOnFile[e-Expressions], bstart, bnext;
4608 /*
4609  1: Get the current position.
4610 */
4611  switch ( e->status ) {
4612  case UNHIDELEXPRESSION:
4613  case UNHIDEGEXPRESSION:
4614  case DROPHLEXPRESSION:
4615  case DROPHGEXPRESSION:
4616  case HIDDENLEXPRESSION:
4617  case HIDDENGEXPRESSION:
4618  fi = AR.hidefile;
4619  break;
4620  default:
4621  fi = AR.infile;
4622  break;
4623  }
4624  if ( AR.KeptInHold ) {
4625  retval = GetTerm(BHEAD term);
4626  return(retval);
4627  }
4628  SeekScratch(fi,&where);
4629  if ( AN.lastinindex < 0 ) {
4630 /*
4631  We have to test whether we have to do the first bracket
4632 */
4633  if ( ( n = TreatIndexEntry(BHEAD 0) ) <= 0 ) {
4634  AN.lastinindex = n;
4635  where = bi[n].start;
4636  ADD2POS(where,eonfile);
4637  SetScratch(fi,&where);
4638 /*
4639  Put the bracket in the Compress buffer.
4640 */
4641  ttco = AR.CompressBuffer;
4642  tt = b->bracketbuffer + bi[0].bracket;
4643  i = *tt;
4644  NCOPY(ttco,tt,i)
4645  AR.CompressPointer = ttco;
4646  retval = GetTerm(BHEAD term);
4647  return(retval);
4648  }
4649  else AN.lastinindex = n-1;
4650  }
4651 /*
4652  2: Find the corresponding index number
4653  a: test whether it is in the current bracket
4654 */
4655  n = AN.lastinindex;
4656  bstart = bi[n].start;
4657  ADD2POS(bstart,eonfile);
4658  bnext = bi[n].next;
4659  ADD2POS(bnext,eonfile);
4660  if ( ISLESSPOS(bstart,where) && ISLESSPOS(where,bnext) ) {
4661  retval = GetTerm(BHEAD term);
4662  return(retval);
4663  }
4664  for ( n++ ; n < b->indexfill; n++ ) {
4665  i = TreatIndexEntry(BHEAD n);
4666  if ( i <= 0 ) {
4667 /*
4668  Put the bracket in the Compress buffer.
4669 */
4670  ttco = AR.CompressBuffer;
4671  tt = b->bracketbuffer + bi[n].bracket;
4672  i = *tt;
4673  NCOPY(ttco,tt,i)
4674  AR.CompressPointer = ttco;
4675  AN.lastinindex = n;
4676  where = bi[n].start;
4677  ADD2POS(where,eonfile);
4678  SetScratch(fi,&(where));
4679  retval = GetTerm(BHEAD term);
4680  return(retval);
4681  }
4682  else n += i - 1;
4683  }
4684  return(0);
4685 }
4686 
4687 /*
4688  #] GetTerm2 :
4689  #[ TreatIndexEntry :
4690 */
4699 int TreatIndexEntry(PHEAD LONG n)
4700 {
4701  BRACKETINFO *b = AN.expr->bracketinfo;
4702  LONG numbra = b->indexfill - 1, i;
4703  LONG totterms;
4704  BRACKETINDEX *bi;
4705  POSITION pos1, average;
4706 /*
4707  1: number of the bracket should be such that there is one bucket
4708  for each worker remaining.
4709 */
4710  if ( ( numbra - n ) <= numberofworkers ) return(0);
4711 /*
4712  2: size of the bracket contents should be less than what remains in
4713  the expression divided by the number of workers.
4714 */
4715  bi = b->indexbuffer;
4716  DIFPOS(pos1,bi[numbra].next,bi[n].next); /* Size of what remains */
4717  BASEPOSITION(average) = DIVPOS(pos1,(3*numberofworkers));
4718  DIFPOS(pos1,bi[n].next,bi[n].start); /* Size of the current bracket */
4719  if ( ISLESSPOS(average,pos1) ) return(0);
4720 /*
4721  It passes.
4722  Now check whether we can do more brackets
4723 */
4724  totterms = bi->termsinbracket;
4725  if ( totterms > 2*AC.ThreadBucketSize ) return(1);
4726  for ( i = 1; i < numbra-n; i++ ) {
4727  DIFPOS(pos1,bi[n+i].next,bi[n].start); /* Size of the combined brackets */
4728  if ( ISLESSPOS(average,pos1) ) return(i);
4729  totterms += bi->termsinbracket;
4730  if ( totterms > 2*AC.ThreadBucketSize ) return(i+1);
4731  }
4732 /*
4733  We have a problem at the end of the system. Just do one.
4734 */
4735  return(1);
4736 }
4737 
4738 /*
4739  #] TreatIndexEntry :
4740  #[ SetHideFiles :
4741 */
4742 
4743 void SetHideFiles() {
4744  int i;
4745  ALLPRIVATES *B, *B0 = AB[0];
4746  for ( i = 1; i <= numberofworkers; i++ ) {
4747  B = AB[i];
4748  if ( AR.hidefile->handle < 0 ) {
4749  AR.hidefile->PObuffer = AR0.hidefile->PObuffer;
4750  AR.hidefile->POstop = AR0.hidefile->POstop;
4751  AR.hidefile->POfill = AR0.hidefile->POfill;
4752  AR.hidefile->POfull = AR0.hidefile->POfull;
4753  AR.hidefile->POsize = AR0.hidefile->POsize;
4754  AR.hidefile->POposition = AR0.hidefile->POposition;
4755  AR.hidefile->filesize = AR0.hidefile->filesize;
4756  }
4757  else {
4758  AR.hidefile->PObuffer = AR.hidefile->wPObuffer;
4759  AR.hidefile->POstop = AR.hidefile->wPOstop;
4760  AR.hidefile->POfill = AR.hidefile->wPOfill;
4761  AR.hidefile->POfull = AR.hidefile->wPOfull;
4762  AR.hidefile->POsize = AR.hidefile->wPOsize;
4763  PUTZERO(AR.hidefile->POposition);
4764  }
4765  }
4766 }
4767 
4768 /*
4769  #] SetHideFiles :
4770  #[ IniFbufs :
4771 */
4772 
4773 void IniFbufs(VOID)
4774 {
4775  int i;
4776  for ( i = 0; i < AM.totalnumberofthreads; i++ ) {
4777  IniFbuffer(AB[i]->T.fbufnum);
4778  }
4779 }
4780 
4781 /*
4782  #] IniFbufs :
4783  #[ SetMods :
4784 */
4785 
4786 void SetMods()
4787 {
4788  ALLPRIVATES *B;
4789  int i, n, j;
4790  for ( j = 0; j < AM.totalnumberofthreads; j++ ) {
4791  B = AB[j];
4792  AN.ncmod = AC.ncmod;
4793  if ( AN.cmod != 0 ) M_free(AN.cmod,"AN.cmod");
4794  n = ABS(AN.ncmod);
4795  AN.cmod = (UWORD *)Malloc1(sizeof(WORD)*n,"AN.cmod");
4796  for ( i = 0; i < n; i++ ) AN.cmod[i] = AC.cmod[i];
4797  }
4798 }
4799 
4800 /*
4801  #] SetMods :
4802  #[ UnSetMods :
4803 */
4804 
4805 void UnSetMods()
4806 {
4807  ALLPRIVATES *B;
4808  int j;
4809  for ( j = 0; j < AM.totalnumberofthreads; j++ ) {
4810  B = AB[j];
4811  if ( AN.cmod != 0 ) M_free(AN.cmod,"AN.cmod");
4812  AN.cmod = 0;
4813  }
4814 }
4815 
4816 /*
4817  #] UnSetMods :
4818 */
4819 
4820 void find_Horner_MCTS_expand_tree_threaded() {
4821  int id;
4822  while (( id = GetAvailableThread() ) < 0)
4823  MasterWait();
4824  WakeupThread(id,MCTSEXPANDTREE);
4825 }
4826 
4827 extern void optimize_expression_given_Horner_threaded() {
4828  int id;
4829  while (( id = GetAvailableThread() ) < 0)
4830  MasterWait();
4831  WakeupThread(id,OPTIMIZEEXPRESSION);
4832 }
4833 
4834 #endif
int NormalModulus(UWORD *, WORD *)
Definition: reken.c:1370
VOID AddArgs(PHEAD WORD *, WORD *, WORD *)
Definition: sort.c:2115
WORD * bracketbuffer
Definition: structs.h:318
Definition: structs.h:618
VOID WriteStats(POSITION *, WORD)
Definition: sort.c:91
#define PHEAD
Definition: ftypes.h:56
WORD ** lhs
Definition: structs.h:912
Definition: structs.h:908
WORD * Pointer
Definition: structs.h:911
WORD StoreTerm(PHEAD WORD *)
Definition: sort.c:4070
WORD ** rhs
Definition: structs.h:913
WORD Compare1(PHEAD WORD *, WORD *, WORD)
Definition: sort.c:2397
Definition: structs.h:1028
struct NeStInG * NESTING
struct StOrEcAcHe * STORECACHE
VOID LowerSortLevel()
Definition: sort.c:4435
BRACKETINDEX * indexbuffer
Definition: structs.h:317
WORD PutOut(PHEAD WORD *, POSITION *, FILEHANDLE *, WORD)
Definition: sort.c:1300
WORD * Buffer
Definition: structs.h:909
WORD NewSort(PHEAD0)
Definition: sort.c:553
WORD Generator(PHEAD WORD *, WORD)
Definition: proces.c:2865
WORD FlushOut(POSITION *, FILEHANDLE *, int)
Definition: sort.c:1621
int handle
Definition: structs.h:646
LONG EndSort(PHEAD WORD *, int)
Definition: sort.c:632
int IniFbuffer(WORD bufnum)
Definition: comtool.c:558