xref: /netbsd-src/external/gpl3/gcc.old/dist/libgomp/env.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
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 
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)
659