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
52struct 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
60unsigned short *gomp_cpu_affinity;
61size_t gomp_cpu_affinity_len;
62unsigned long gomp_max_active_levels_var = INT_MAX;
63unsigned long gomp_thread_limit_var = ULONG_MAX;
64unsigned long gomp_remaining_threads_count;
65#ifndef HAVE_SYNC_BUILTINS
66gomp_mutex_t gomp_remaining_threads_lock;
67#endif
68unsigned long gomp_available_cpus = 1, gomp_managed_threads = 1;
69unsigned long long gomp_spin_count_var, gomp_throttled_spin_count_var;
70
71/* Parse the OMP_SCHEDULE environment variable.  */
72
73static void
74parse_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
148static bool
149parse_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
184static bool
185parse_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
245static bool
246parse_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
318static void
319parse_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
350static int
351parse_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
385static bool
386parse_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
473static void __attribute__((constructor))
474initialize_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
554void
555omp_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
561void
562omp_set_dynamic (int val)
563{
564  struct gomp_task_icv *icv = gomp_icv (true);
565  icv->dyn_var = val;
566}
567
568int
569omp_get_dynamic (void)
570{
571  struct gomp_task_icv *icv = gomp_icv (false);
572  return icv->dyn_var;
573}
574
575void
576omp_set_nested (int val)
577{
578  struct gomp_task_icv *icv = gomp_icv (true);
579  icv->nest_var = val;
580}
581
582int
583omp_get_nested (void)
584{
585  struct gomp_task_icv *icv = gomp_icv (false);
586  return icv->nest_var;
587}
588
589void
590omp_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
614void
615omp_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
622int
623omp_get_max_threads (void)
624{
625  struct gomp_task_icv *icv = gomp_icv (false);
626  return icv->nthreads_var;
627}
628
629int
630omp_get_thread_limit (void)
631{
632  return gomp_thread_limit_var > INT_MAX ? INT_MAX : gomp_thread_limit_var;
633}
634
635void
636omp_set_max_active_levels (int max_levels)
637{
638  if (max_levels >= 0)
639    gomp_max_active_levels_var = max_levels;
640}
641
642int
643omp_get_max_active_levels (void)
644{
645  return gomp_max_active_levels_var;
646}
647
648ialias (omp_set_dynamic)
649ialias (omp_set_nested)
650ialias (omp_set_num_threads)
651ialias (omp_get_dynamic)
652ialias (omp_get_nested)
653ialias (omp_set_schedule)
654ialias (omp_get_schedule)
655ialias (omp_get_max_threads)
656ialias (omp_get_thread_limit)
657ialias (omp_set_max_active_levels)
658ialias (omp_get_max_active_levels)
659