OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / libgomp / env.c
1 /* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
2    Free Software Foundation, Inc.
3    Contributed by Richard Henderson <rth@redhat.com>.
4
5    This file is part of the GNU OpenMP Library (libgomp).
6
7    Libgomp is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3, or (at your option)
10    any later version.
11
12    Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
13    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14    FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15    more details.
16
17    Under Section 7 of GPL version 3, you are granted additional
18    permissions described in the GCC Runtime Library Exception, version
19    3.1, as published by the Free Software Foundation.
20
21    You should have received a copy of the GNU General Public License and
22    a copy of the GCC Runtime Library Exception along with this program;
23    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24    <http://www.gnu.org/licenses/>.  */
25
26 /* This file defines the OpenMP internal control variables, and arranges
27    for them to be initialized from environment variables at startup.  */
28
29 #include "libgomp.h"
30 #include "libgomp_f.h"
31 #include <ctype.h>
32 #include <stdlib.h>
33 #ifdef STRING_WITH_STRINGS
34 # include <string.h>
35 # include <strings.h>
36 #else
37 # ifdef HAVE_STRING_H
38 #  include <string.h>
39 # else
40 #  ifdef HAVE_STRINGS_H
41 #   include <strings.h>
42 #  endif
43 # endif
44 #endif
45 #include <limits.h>
46 #include <errno.h>
47
48 #ifndef HAVE_STRTOULL
49 # define strtoull(ptr, eptr, base) strtoul (ptr, eptr, base)
50 #endif
51
52 struct gomp_task_icv gomp_global_icv = {
53   .nthreads_var = 1,
54   .run_sched_var = GFS_DYNAMIC,
55   .run_sched_modifier = 1,
56   .dyn_var = false,
57   .nest_var = false
58 };
59
60 unsigned short *gomp_cpu_affinity;
61 size_t gomp_cpu_affinity_len;
62 unsigned long gomp_max_active_levels_var = INT_MAX;
63 unsigned long gomp_thread_limit_var = ULONG_MAX;
64 unsigned long gomp_remaining_threads_count;
65 #ifndef HAVE_SYNC_BUILTINS
66 gomp_mutex_t gomp_remaining_threads_lock;
67 #endif
68 unsigned long gomp_available_cpus = 1, gomp_managed_threads = 1;
69 unsigned long long gomp_spin_count_var, gomp_throttled_spin_count_var;
70
71 /* Parse the OMP_SCHEDULE environment variable.  */
72
73 static void
74 parse_schedule (void)
75 {
76   char *env, *end;
77   unsigned long value;
78
79   env = getenv ("OMP_SCHEDULE");
80   if (env == NULL)
81     return;
82
83   while (isspace ((unsigned char) *env))
84     ++env;
85   if (strncasecmp (env, "static", 6) == 0)
86     {
87       gomp_global_icv.run_sched_var = GFS_STATIC;
88       env += 6;
89     }
90   else if (strncasecmp (env, "dynamic", 7) == 0)
91     {
92       gomp_global_icv.run_sched_var = GFS_DYNAMIC;
93       env += 7;
94     }
95   else if (strncasecmp (env, "guided", 6) == 0)
96     {
97       gomp_global_icv.run_sched_var = GFS_GUIDED;
98       env += 6;
99     }
100   else if (strncasecmp (env, "auto", 4) == 0)
101     {
102       gomp_global_icv.run_sched_var = GFS_AUTO;
103       env += 4;
104     }
105   else
106     goto unknown;
107
108   while (isspace ((unsigned char) *env))
109     ++env;
110   if (*env == '\0')
111     return;
112   if (*env++ != ',')
113     goto unknown;
114   while (isspace ((unsigned char) *env))
115     ++env;
116   if (*env == '\0')
117     goto invalid;
118
119   errno = 0;
120   value = strtoul (env, &end, 10);
121   if (errno)
122     goto invalid;
123
124   while (isspace ((unsigned char) *end))
125     ++end;
126   if (*end != '\0')
127     goto invalid;
128
129   if ((int)value != value)
130     goto invalid;
131
132   gomp_global_icv.run_sched_modifier = value;
133   return;
134
135  unknown:
136   gomp_error ("Unknown value for environment variable OMP_SCHEDULE");
137   return;
138
139  invalid:
140   gomp_error ("Invalid value for chunk size in "
141               "environment variable OMP_SCHEDULE");
142   return;
143 }
144
145 /* Parse an unsigned long environment variable.  Return true if one was
146    present and it was successfully parsed.  */
147
148 static bool
149 parse_unsigned_long (const char *name, unsigned long *pvalue, bool allow_zero)
150 {
151   char *env, *end;
152   unsigned long value;
153
154   env = getenv (name);
155   if (env == NULL)
156     return false;
157
158   while (isspace ((unsigned char) *env))
159     ++env;
160   if (*env == '\0')
161     goto invalid;
162
163   errno = 0;
164   value = strtoul (env, &end, 10);
165   if (errno || (long) value <= 0 - allow_zero)
166     goto invalid;
167
168   while (isspace ((unsigned char) *end))
169     ++end;
170   if (*end != '\0')
171     goto invalid;
172
173   *pvalue = value;
174   return true;
175
176  invalid:
177   gomp_error ("Invalid value for environment variable %s", name);
178   return false;
179 }
180
181 /* Parse the OMP_STACKSIZE environment varible.  Return true if one was
182    present and it was successfully parsed.  */
183
184 static bool
185 parse_stacksize (const char *name, unsigned long *pvalue)
186 {
187   char *env, *end;
188   unsigned long value, shift = 10;
189
190   env = getenv (name);
191   if (env == NULL)
192     return false;
193
194   while (isspace ((unsigned char) *env))
195     ++env;
196   if (*env == '\0')
197     goto invalid;
198
199   errno = 0;
200   value = strtoul (env, &end, 10);
201   if (errno)
202     goto invalid;
203
204   while (isspace ((unsigned char) *end))
205     ++end;
206   if (*end != '\0')
207     {
208       switch (tolower ((unsigned char) *end))
209         {
210         case 'b':
211           shift = 0;
212           break;
213         case 'k':
214           break;
215         case 'm':
216           shift = 20;
217           break;
218         case 'g':
219           shift = 30;
220           break;
221         default:
222           goto invalid;
223         }
224       ++end;
225       while (isspace ((unsigned char) *end))
226         ++end;
227       if (*end != '\0')
228         goto invalid;
229     }
230
231   if (((value << shift) >> shift) != value)
232     goto invalid;
233
234   *pvalue = value << shift;
235   return true;
236
237  invalid:
238   gomp_error ("Invalid value for environment variable %s", name);
239   return false;
240 }
241
242 /* Parse the GOMP_SPINCOUNT environment varible.  Return true if one was
243    present and it was successfully parsed.  */
244
245 static bool
246 parse_spincount (const char *name, unsigned long long *pvalue)
247 {
248   char *env, *end;
249   unsigned long long value, mult = 1;
250
251   env = getenv (name);
252   if (env == NULL)
253     return false;
254
255   while (isspace ((unsigned char) *env))
256     ++env;
257   if (*env == '\0')
258     goto invalid;
259
260   if (strncasecmp (env, "infinite", 8) == 0
261       || strncasecmp (env, "infinity", 8) == 0)
262     {
263       value = ~0ULL;
264       end = env + 8;
265       goto check_tail;
266     }
267
268   errno = 0;
269   value = strtoull (env, &end, 10);
270   if (errno)
271     goto invalid;
272
273   while (isspace ((unsigned char) *end))
274     ++end;
275   if (*end != '\0')
276     {
277       switch (tolower ((unsigned char) *end))
278         {
279         case 'k':
280           mult = 1000LL;
281           break;
282         case 'm':
283           mult = 1000LL * 1000LL;
284           break;
285         case 'g':
286           mult = 1000LL * 1000LL * 1000LL;
287           break;
288         case 't':
289           mult = 1000LL * 1000LL * 1000LL * 1000LL;
290           break;
291         default:
292           goto invalid;
293         }
294       ++end;
295      check_tail:
296       while (isspace ((unsigned char) *end))
297         ++end;
298       if (*end != '\0')
299         goto invalid;
300     }
301
302   if (value > ~0ULL / mult)
303     value = ~0ULL;
304   else
305     value *= mult;
306
307   *pvalue = value;
308   return true;
309
310  invalid:
311   gomp_error ("Invalid value for environment variable %s", name);
312   return false;
313 }
314
315 /* Parse a boolean value for environment variable NAME and store the
316    result in VALUE.  */
317
318 static void
319 parse_boolean (const char *name, bool *value)
320 {
321   const char *env;
322
323   env = getenv (name);
324   if (env == NULL)
325     return;
326
327   while (isspace ((unsigned char) *env))
328     ++env;
329   if (strncasecmp (env, "true", 4) == 0)
330     {
331       *value = true;
332       env += 4;
333     }
334   else if (strncasecmp (env, "false", 5) == 0)
335     {
336       *value = false;
337       env += 5;
338     }
339   else
340     env = "X";
341   while (isspace ((unsigned char) *env))
342     ++env;
343   if (*env != '\0')
344     gomp_error ("Invalid value for environment variable %s", name);
345 }
346
347 /* Parse the OMP_WAIT_POLICY environment variable and store the
348    result in gomp_active_wait_policy.  */
349
350 static int
351 parse_wait_policy (void)
352 {
353   const char *env;
354   int ret = -1;
355
356   env = getenv ("OMP_WAIT_POLICY");
357   if (env == NULL)
358     return -1;
359
360   while (isspace ((unsigned char) *env))
361     ++env;
362   if (strncasecmp (env, "active", 6) == 0)
363     {
364       ret = 1;
365       env += 6;
366     }
367   else if (strncasecmp (env, "passive", 7) == 0)
368     {
369       ret = 0;
370       env += 7;
371     }
372   else
373     env = "X";
374   while (isspace ((unsigned char) *env))
375     ++env;
376   if (*env == '\0')
377     return ret;
378   gomp_error ("Invalid value for environment variable OMP_WAIT_POLICY");
379   return -1;
380 }
381
382 /* Parse the GOMP_CPU_AFFINITY environment varible.  Return true if one was
383    present and it was successfully parsed.  */
384
385 static bool
386 parse_affinity (void)
387 {
388   char *env, *end;
389   unsigned long cpu_beg, cpu_end, cpu_stride;
390   unsigned short *cpus = NULL;
391   size_t allocated = 0, used = 0, needed;
392
393   env = getenv ("GOMP_CPU_AFFINITY");
394   if (env == NULL)
395     return false;
396
397   do
398     {
399       while (*env == ' ' || *env == '\t')
400         env++;
401
402       cpu_beg = strtoul (env, &end, 0);
403       cpu_end = cpu_beg;
404       cpu_stride = 1;
405       if (env == end || cpu_beg >= 65536)
406         goto invalid;
407
408       env = end;
409       if (*env == '-')
410         {
411           cpu_end = strtoul (++env, &end, 0);
412           if (env == end || cpu_end >= 65536 || cpu_end < cpu_beg)
413             goto invalid;
414
415           env = end;
416           if (*env == ':')
417             {
418               cpu_stride = strtoul (++env, &end, 0);
419               if (env == end || cpu_stride == 0 || cpu_stride >= 65536)
420                 goto invalid;
421
422               env = end;
423             }
424         }
425
426       needed = (cpu_end - cpu_beg) / cpu_stride + 1;
427       if (used + needed >= allocated)
428         {
429           unsigned short *new_cpus;
430
431           if (allocated < 64)
432             allocated = 64;
433           if (allocated > needed)
434             allocated <<= 1;
435           else
436             allocated += 2 * needed;
437           new_cpus = realloc (cpus, allocated * sizeof (unsigned short));
438           if (new_cpus == NULL)
439             {
440               free (cpus);
441               gomp_error ("not enough memory to store GOMP_CPU_AFFINITY list");
442               return false;
443             }
444
445           cpus = new_cpus;
446         }
447
448       while (needed--)
449         {
450           cpus[used++] = cpu_beg;
451           cpu_beg += cpu_stride;
452         }
453
454       while (*env == ' ' || *env == '\t')
455         env++;
456
457       if (*env == ',')
458         env++;
459       else if (*env == '\0')
460         break;
461     }
462   while (1);
463
464   gomp_cpu_affinity = cpus;
465   gomp_cpu_affinity_len = used;
466   return true;
467
468  invalid:
469   gomp_error ("Invalid value for enviroment variable GOMP_CPU_AFFINITY");
470   return false;
471 }
472
473 static void __attribute__((constructor))
474 initialize_env (void)
475 {
476   unsigned long stacksize;
477   int wait_policy;
478
479   /* Do a compile time check that mkomp_h.pl did good job.  */
480   omp_check_defines ();
481
482   parse_schedule ();
483   parse_boolean ("OMP_DYNAMIC", &gomp_global_icv.dyn_var);
484   parse_boolean ("OMP_NESTED", &gomp_global_icv.nest_var);
485   parse_unsigned_long ("OMP_MAX_ACTIVE_LEVELS", &gomp_max_active_levels_var,
486                        true);
487   parse_unsigned_long ("OMP_THREAD_LIMIT", &gomp_thread_limit_var, false);
488   if (gomp_thread_limit_var != ULONG_MAX)
489     gomp_remaining_threads_count = gomp_thread_limit_var - 1;
490 #ifndef HAVE_SYNC_BUILTINS
491   gomp_mutex_init (&gomp_remaining_threads_lock);
492 #endif
493   gomp_init_num_threads ();
494   gomp_available_cpus = gomp_global_icv.nthreads_var;
495   if (!parse_unsigned_long ("OMP_NUM_THREADS", &gomp_global_icv.nthreads_var,
496                             false))
497     gomp_global_icv.nthreads_var = gomp_available_cpus;
498   if (parse_affinity ())
499     gomp_init_affinity ();
500   wait_policy = parse_wait_policy ();
501   if (!parse_spincount ("GOMP_SPINCOUNT", &gomp_spin_count_var))
502     {
503       /* Using a rough estimation of 100000 spins per msec,
504          use 5 min blocking for OMP_WAIT_POLICY=active,
505          200 msec blocking when OMP_WAIT_POLICY is not specificed
506          and 0 when OMP_WAIT_POLICY=passive.
507          Depending on the CPU speed, this can be e.g. 5 times longer
508          or 5 times shorter.  */
509       if (wait_policy > 0)
510         gomp_spin_count_var = 30000000000LL;
511       else if (wait_policy < 0)
512         gomp_spin_count_var = 20000000LL;
513     }
514   /* gomp_throttled_spin_count_var is used when there are more libgomp
515      managed threads than available CPUs.  Use very short spinning.  */
516   if (wait_policy > 0)
517     gomp_throttled_spin_count_var = 1000LL;
518   else if (wait_policy < 0)
519     gomp_throttled_spin_count_var = 100LL;
520   if (gomp_throttled_spin_count_var > gomp_spin_count_var)
521     gomp_throttled_spin_count_var = gomp_spin_count_var;
522
523   /* Not strictly environment related, but ordering constructors is tricky.  */
524   pthread_attr_init (&gomp_thread_attr);
525   pthread_attr_setdetachstate (&gomp_thread_attr, PTHREAD_CREATE_DETACHED);
526
527   if (parse_stacksize ("OMP_STACKSIZE", &stacksize)
528       || parse_stacksize ("GOMP_STACKSIZE", &stacksize))
529     {
530       int err;
531
532       err = pthread_attr_setstacksize (&gomp_thread_attr, stacksize);
533
534 #ifdef PTHREAD_STACK_MIN
535       if (err == EINVAL)
536         {
537           if (stacksize < PTHREAD_STACK_MIN)
538             gomp_error ("Stack size less than minimum of %luk",
539                         PTHREAD_STACK_MIN / 1024ul
540                         + (PTHREAD_STACK_MIN % 1024 != 0));
541           else
542             gomp_error ("Stack size larger than system limit");
543         }
544       else
545 #endif
546       if (err != 0)
547         gomp_error ("Stack size change failed: %s", strerror (err));
548     }
549 }
550
551 \f
552 /* The public OpenMP API routines that access these variables.  */
553
554 void
555 omp_set_num_threads (int n)
556 {
557   struct gomp_task_icv *icv = gomp_icv (true);
558   icv->nthreads_var = (n > 0 ? n : 1);
559 }
560
561 void
562 omp_set_dynamic (int val)
563 {
564   struct gomp_task_icv *icv = gomp_icv (true);
565   icv->dyn_var = val;
566 }
567
568 int
569 omp_get_dynamic (void)
570 {
571   struct gomp_task_icv *icv = gomp_icv (false);
572   return icv->dyn_var;
573 }
574
575 void
576 omp_set_nested (int val)
577 {
578   struct gomp_task_icv *icv = gomp_icv (true);
579   icv->nest_var = val;
580 }
581
582 int
583 omp_get_nested (void)
584 {
585   struct gomp_task_icv *icv = gomp_icv (false);
586   return icv->nest_var;
587 }
588
589 void
590 omp_set_schedule (omp_sched_t kind, int modifier)
591 {
592   struct gomp_task_icv *icv = gomp_icv (true);
593   switch (kind)
594     {
595     case omp_sched_static:
596       if (modifier < 1)
597         modifier = 0;
598       icv->run_sched_modifier = modifier;
599       break;
600     case omp_sched_dynamic:
601     case omp_sched_guided:
602       if (modifier < 1)
603         modifier = 1;
604       icv->run_sched_modifier = modifier;
605       break;
606     case omp_sched_auto:
607       break;
608     default:
609       return;
610     }
611   icv->run_sched_var = kind;
612 }
613
614 void
615 omp_get_schedule (omp_sched_t *kind, int *modifier)
616 {
617   struct gomp_task_icv *icv = gomp_icv (false);
618   *kind = icv->run_sched_var;
619   *modifier = icv->run_sched_modifier;
620 }
621
622 int
623 omp_get_max_threads (void)
624 {
625   struct gomp_task_icv *icv = gomp_icv (false);
626   return icv->nthreads_var;
627 }
628
629 int
630 omp_get_thread_limit (void)
631 {
632   return gomp_thread_limit_var > INT_MAX ? INT_MAX : gomp_thread_limit_var;
633 }
634
635 void
636 omp_set_max_active_levels (int max_levels)
637 {
638   if (max_levels >= 0)
639     gomp_max_active_levels_var = max_levels;
640 }
641
642 int
643 omp_get_max_active_levels (void)
644 {
645   return gomp_max_active_levels_var;
646 }
647
648 ialias (omp_set_dynamic)
649 ialias (omp_set_nested)
650 ialias (omp_set_num_threads)
651 ialias (omp_get_dynamic)
652 ialias (omp_get_nested)
653 ialias (omp_set_schedule)
654 ialias (omp_get_schedule)
655 ialias (omp_get_max_threads)
656 ialias (omp_get_thread_limit)
657 ialias (omp_set_max_active_levels)
658 ialias (omp_get_max_active_levels)