OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / libgomp / env.c
1 /* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
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     {
112       gomp_global_icv.run_sched_modifier
113         = gomp_global_icv.run_sched_var != GFS_STATIC;
114       return;
115     }
116   if (*env++ != ',')
117     goto unknown;
118   while (isspace ((unsigned char) *env))
119     ++env;
120   if (*env == '\0')
121     goto invalid;
122
123   errno = 0;
124   value = strtoul (env, &end, 10);
125   if (errno)
126     goto invalid;
127
128   while (isspace ((unsigned char) *end))
129     ++end;
130   if (*end != '\0')
131     goto invalid;
132
133   if ((int)value != value)
134     goto invalid;
135
136   if (value == 0 && gomp_global_icv.run_sched_var != GFS_STATIC)
137     value = 1;
138   gomp_global_icv.run_sched_modifier = value;
139   return;
140
141  unknown:
142   gomp_error ("Unknown value for environment variable OMP_SCHEDULE");
143   return;
144
145  invalid:
146   gomp_error ("Invalid value for chunk size in "
147               "environment variable OMP_SCHEDULE");
148   return;
149 }
150
151 /* Parse an unsigned long environment variable.  Return true if one was
152    present and it was successfully parsed.  */
153
154 static bool
155 parse_unsigned_long (const char *name, unsigned long *pvalue, bool allow_zero)
156 {
157   char *env, *end;
158   unsigned long value;
159
160   env = getenv (name);
161   if (env == NULL)
162     return false;
163
164   while (isspace ((unsigned char) *env))
165     ++env;
166   if (*env == '\0')
167     goto invalid;
168
169   errno = 0;
170   value = strtoul (env, &end, 10);
171   if (errno || (long) value <= 0 - allow_zero)
172     goto invalid;
173
174   while (isspace ((unsigned char) *end))
175     ++end;
176   if (*end != '\0')
177     goto invalid;
178
179   *pvalue = value;
180   return true;
181
182  invalid:
183   gomp_error ("Invalid value for environment variable %s", name);
184   return false;
185 }
186
187 /* Parse the OMP_STACKSIZE environment varible.  Return true if one was
188    present and it was successfully parsed.  */
189
190 static bool
191 parse_stacksize (const char *name, unsigned long *pvalue)
192 {
193   char *env, *end;
194   unsigned long value, shift = 10;
195
196   env = getenv (name);
197   if (env == NULL)
198     return false;
199
200   while (isspace ((unsigned char) *env))
201     ++env;
202   if (*env == '\0')
203     goto invalid;
204
205   errno = 0;
206   value = strtoul (env, &end, 10);
207   if (errno)
208     goto invalid;
209
210   while (isspace ((unsigned char) *end))
211     ++end;
212   if (*end != '\0')
213     {
214       switch (tolower ((unsigned char) *end))
215         {
216         case 'b':
217           shift = 0;
218           break;
219         case 'k':
220           break;
221         case 'm':
222           shift = 20;
223           break;
224         case 'g':
225           shift = 30;
226           break;
227         default:
228           goto invalid;
229         }
230       ++end;
231       while (isspace ((unsigned char) *end))
232         ++end;
233       if (*end != '\0')
234         goto invalid;
235     }
236
237   if (((value << shift) >> shift) != value)
238     goto invalid;
239
240   *pvalue = value << shift;
241   return true;
242
243  invalid:
244   gomp_error ("Invalid value for environment variable %s", name);
245   return false;
246 }
247
248 /* Parse the GOMP_SPINCOUNT environment varible.  Return true if one was
249    present and it was successfully parsed.  */
250
251 static bool
252 parse_spincount (const char *name, unsigned long long *pvalue)
253 {
254   char *env, *end;
255   unsigned long long value, mult = 1;
256
257   env = getenv (name);
258   if (env == NULL)
259     return false;
260
261   while (isspace ((unsigned char) *env))
262     ++env;
263   if (*env == '\0')
264     goto invalid;
265
266   if (strncasecmp (env, "infinite", 8) == 0
267       || strncasecmp (env, "infinity", 8) == 0)
268     {
269       value = ~0ULL;
270       end = env + 8;
271       goto check_tail;
272     }
273
274   errno = 0;
275   value = strtoull (env, &end, 10);
276   if (errno)
277     goto invalid;
278
279   while (isspace ((unsigned char) *end))
280     ++end;
281   if (*end != '\0')
282     {
283       switch (tolower ((unsigned char) *end))
284         {
285         case 'k':
286           mult = 1000LL;
287           break;
288         case 'm':
289           mult = 1000LL * 1000LL;
290           break;
291         case 'g':
292           mult = 1000LL * 1000LL * 1000LL;
293           break;
294         case 't':
295           mult = 1000LL * 1000LL * 1000LL * 1000LL;
296           break;
297         default:
298           goto invalid;
299         }
300       ++end;
301      check_tail:
302       while (isspace ((unsigned char) *end))
303         ++end;
304       if (*end != '\0')
305         goto invalid;
306     }
307
308   if (value > ~0ULL / mult)
309     value = ~0ULL;
310   else
311     value *= mult;
312
313   *pvalue = value;
314   return true;
315
316  invalid:
317   gomp_error ("Invalid value for environment variable %s", name);
318   return false;
319 }
320
321 /* Parse a boolean value for environment variable NAME and store the
322    result in VALUE.  */
323
324 static void
325 parse_boolean (const char *name, bool *value)
326 {
327   const char *env;
328
329   env = getenv (name);
330   if (env == NULL)
331     return;
332
333   while (isspace ((unsigned char) *env))
334     ++env;
335   if (strncasecmp (env, "true", 4) == 0)
336     {
337       *value = true;
338       env += 4;
339     }
340   else if (strncasecmp (env, "false", 5) == 0)
341     {
342       *value = false;
343       env += 5;
344     }
345   else
346     env = "X";
347   while (isspace ((unsigned char) *env))
348     ++env;
349   if (*env != '\0')
350     gomp_error ("Invalid value for environment variable %s", name);
351 }
352
353 /* Parse the OMP_WAIT_POLICY environment variable and store the
354    result in gomp_active_wait_policy.  */
355
356 static int
357 parse_wait_policy (void)
358 {
359   const char *env;
360   int ret = -1;
361
362   env = getenv ("OMP_WAIT_POLICY");
363   if (env == NULL)
364     return -1;
365
366   while (isspace ((unsigned char) *env))
367     ++env;
368   if (strncasecmp (env, "active", 6) == 0)
369     {
370       ret = 1;
371       env += 6;
372     }
373   else if (strncasecmp (env, "passive", 7) == 0)
374     {
375       ret = 0;
376       env += 7;
377     }
378   else
379     env = "X";
380   while (isspace ((unsigned char) *env))
381     ++env;
382   if (*env == '\0')
383     return ret;
384   gomp_error ("Invalid value for environment variable OMP_WAIT_POLICY");
385   return -1;
386 }
387
388 /* Parse the GOMP_CPU_AFFINITY environment varible.  Return true if one was
389    present and it was successfully parsed.  */
390
391 static bool
392 parse_affinity (void)
393 {
394   char *env, *end;
395   unsigned long cpu_beg, cpu_end, cpu_stride;
396   unsigned short *cpus = NULL;
397   size_t allocated = 0, used = 0, needed;
398
399   env = getenv ("GOMP_CPU_AFFINITY");
400   if (env == NULL)
401     return false;
402
403   do
404     {
405       while (*env == ' ' || *env == '\t')
406         env++;
407
408       cpu_beg = strtoul (env, &end, 0);
409       cpu_end = cpu_beg;
410       cpu_stride = 1;
411       if (env == end || cpu_beg >= 65536)
412         goto invalid;
413
414       env = end;
415       if (*env == '-')
416         {
417           cpu_end = strtoul (++env, &end, 0);
418           if (env == end || cpu_end >= 65536 || cpu_end < cpu_beg)
419             goto invalid;
420
421           env = end;
422           if (*env == ':')
423             {
424               cpu_stride = strtoul (++env, &end, 0);
425               if (env == end || cpu_stride == 0 || cpu_stride >= 65536)
426                 goto invalid;
427
428               env = end;
429             }
430         }
431
432       needed = (cpu_end - cpu_beg) / cpu_stride + 1;
433       if (used + needed >= allocated)
434         {
435           unsigned short *new_cpus;
436
437           if (allocated < 64)
438             allocated = 64;
439           if (allocated > needed)
440             allocated <<= 1;
441           else
442             allocated += 2 * needed;
443           new_cpus = realloc (cpus, allocated * sizeof (unsigned short));
444           if (new_cpus == NULL)
445             {
446               free (cpus);
447               gomp_error ("not enough memory to store GOMP_CPU_AFFINITY list");
448               return false;
449             }
450
451           cpus = new_cpus;
452         }
453
454       while (needed--)
455         {
456           cpus[used++] = cpu_beg;
457           cpu_beg += cpu_stride;
458         }
459
460       while (*env == ' ' || *env == '\t')
461         env++;
462
463       if (*env == ',')
464         env++;
465       else if (*env == '\0')
466         break;
467     }
468   while (1);
469
470   gomp_cpu_affinity = cpus;
471   gomp_cpu_affinity_len = used;
472   return true;
473
474  invalid:
475   gomp_error ("Invalid value for enviroment variable GOMP_CPU_AFFINITY");
476   return false;
477 }
478
479 static void __attribute__((constructor))
480 initialize_env (void)
481 {
482   unsigned long stacksize;
483   int wait_policy;
484
485   /* Do a compile time check that mkomp_h.pl did good job.  */
486   omp_check_defines ();
487
488   parse_schedule ();
489   parse_boolean ("OMP_DYNAMIC", &gomp_global_icv.dyn_var);
490   parse_boolean ("OMP_NESTED", &gomp_global_icv.nest_var);
491   parse_unsigned_long ("OMP_MAX_ACTIVE_LEVELS", &gomp_max_active_levels_var,
492                        true);
493   parse_unsigned_long ("OMP_THREAD_LIMIT", &gomp_thread_limit_var, false);
494   if (gomp_thread_limit_var != ULONG_MAX)
495     gomp_remaining_threads_count = gomp_thread_limit_var - 1;
496 #ifndef HAVE_SYNC_BUILTINS
497   gomp_mutex_init (&gomp_remaining_threads_lock);
498 #endif
499   gomp_init_num_threads ();
500   gomp_available_cpus = gomp_global_icv.nthreads_var;
501   if (!parse_unsigned_long ("OMP_NUM_THREADS", &gomp_global_icv.nthreads_var,
502                             false))
503     gomp_global_icv.nthreads_var = gomp_available_cpus;
504   if (parse_affinity ())
505     gomp_init_affinity ();
506   wait_policy = parse_wait_policy ();
507   if (!parse_spincount ("GOMP_SPINCOUNT", &gomp_spin_count_var))
508     {
509       /* Using a rough estimation of 100000 spins per msec,
510          use 5 min blocking for OMP_WAIT_POLICY=active,
511          3 msec blocking when OMP_WAIT_POLICY is not specificed
512          and 0 when OMP_WAIT_POLICY=passive.
513          Depending on the CPU speed, this can be e.g. 5 times longer
514          or 5 times shorter.  */
515       if (wait_policy > 0)
516         gomp_spin_count_var = 30000000000LL;
517       else if (wait_policy < 0)
518         gomp_spin_count_var = 300000LL;
519     }
520   /* gomp_throttled_spin_count_var is used when there are more libgomp
521      managed threads than available CPUs.  Use very short spinning.  */
522   if (wait_policy > 0)
523     gomp_throttled_spin_count_var = 1000LL;
524   else if (wait_policy < 0)
525     gomp_throttled_spin_count_var = 100LL;
526   if (gomp_throttled_spin_count_var > gomp_spin_count_var)
527     gomp_throttled_spin_count_var = gomp_spin_count_var;
528
529   /* Not strictly environment related, but ordering constructors is tricky.  */
530   pthread_attr_init (&gomp_thread_attr);
531   pthread_attr_setdetachstate (&gomp_thread_attr, PTHREAD_CREATE_DETACHED);
532
533   if (parse_stacksize ("OMP_STACKSIZE", &stacksize)
534       || parse_stacksize ("GOMP_STACKSIZE", &stacksize))
535     {
536       int err;
537
538       err = pthread_attr_setstacksize (&gomp_thread_attr, stacksize);
539
540 #ifdef PTHREAD_STACK_MIN
541       if (err == EINVAL)
542         {
543           if (stacksize < PTHREAD_STACK_MIN)
544             gomp_error ("Stack size less than minimum of %luk",
545                         PTHREAD_STACK_MIN / 1024ul
546                         + (PTHREAD_STACK_MIN % 1024 != 0));
547           else
548             gomp_error ("Stack size larger than system limit");
549         }
550       else
551 #endif
552       if (err != 0)
553         gomp_error ("Stack size change failed: %s", strerror (err));
554     }
555 }
556
557 \f
558 /* The public OpenMP API routines that access these variables.  */
559
560 void
561 omp_set_num_threads (int n)
562 {
563   struct gomp_task_icv *icv = gomp_icv (true);
564   icv->nthreads_var = (n > 0 ? n : 1);
565 }
566
567 void
568 omp_set_dynamic (int val)
569 {
570   struct gomp_task_icv *icv = gomp_icv (true);
571   icv->dyn_var = val;
572 }
573
574 int
575 omp_get_dynamic (void)
576 {
577   struct gomp_task_icv *icv = gomp_icv (false);
578   return icv->dyn_var;
579 }
580
581 void
582 omp_set_nested (int val)
583 {
584   struct gomp_task_icv *icv = gomp_icv (true);
585   icv->nest_var = val;
586 }
587
588 int
589 omp_get_nested (void)
590 {
591   struct gomp_task_icv *icv = gomp_icv (false);
592   return icv->nest_var;
593 }
594
595 void
596 omp_set_schedule (omp_sched_t kind, int modifier)
597 {
598   struct gomp_task_icv *icv = gomp_icv (true);
599   switch (kind)
600     {
601     case omp_sched_static:
602       if (modifier < 1)
603         modifier = 0;
604       icv->run_sched_modifier = modifier;
605       break;
606     case omp_sched_dynamic:
607     case omp_sched_guided:
608       if (modifier < 1)
609         modifier = 1;
610       icv->run_sched_modifier = modifier;
611       break;
612     case omp_sched_auto:
613       break;
614     default:
615       return;
616     }
617   icv->run_sched_var = kind;
618 }
619
620 void
621 omp_get_schedule (omp_sched_t *kind, int *modifier)
622 {
623   struct gomp_task_icv *icv = gomp_icv (false);
624   *kind = icv->run_sched_var;
625   *modifier = icv->run_sched_modifier;
626 }
627
628 int
629 omp_get_max_threads (void)
630 {
631   struct gomp_task_icv *icv = gomp_icv (false);
632   return icv->nthreads_var;
633 }
634
635 int
636 omp_get_thread_limit (void)
637 {
638   return gomp_thread_limit_var > INT_MAX ? INT_MAX : gomp_thread_limit_var;
639 }
640
641 void
642 omp_set_max_active_levels (int max_levels)
643 {
644   if (max_levels >= 0)
645     gomp_max_active_levels_var = max_levels;
646 }
647
648 int
649 omp_get_max_active_levels (void)
650 {
651   return gomp_max_active_levels_var;
652 }
653
654 ialias (omp_set_dynamic)
655 ialias (omp_set_nested)
656 ialias (omp_set_num_threads)
657 ialias (omp_get_dynamic)
658 ialias (omp_get_nested)
659 ialias (omp_set_schedule)
660 ialias (omp_get_schedule)
661 ialias (omp_get_max_threads)
662 ialias (omp_get_thread_limit)
663 ialias (omp_set_max_active_levels)
664 ialias (omp_get_max_active_levels)