1282115Spfg/* Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc.
2169695Skan   Contributed by Richard Henderson <rth@redhat.com>.
3169695Skan
4169695Skan   This file is part of the GNU OpenMP Library (libgomp).
5169695Skan
6169695Skan   Libgomp is free software; you can redistribute it and/or modify it
7169695Skan   under the terms of the GNU Lesser General Public License as published by
8169695Skan   the Free Software Foundation; either version 2.1 of the License, or
9169695Skan   (at your option) any later version.
10169695Skan
11169695Skan   Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
12169695Skan   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13169695Skan   FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
14169695Skan   more details.
15169695Skan
16169695Skan   You should have received a copy of the GNU Lesser General Public License
17169695Skan   along with libgomp; see the file COPYING.LIB.  If not, write to the
18169695Skan   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19169695Skan   MA 02110-1301, USA.  */
20169695Skan
21169695Skan/* As a special exception, if you link this library with other files, some
22169695Skan   of which are compiled with GCC, to produce an executable, this library
23169695Skan   does not by itself cause the resulting executable to be covered by the
24169695Skan   GNU General Public License.  This exception does not however invalidate
25169695Skan   any other reasons why the executable file might be covered by the GNU
26169695Skan   General Public License.  */
27169695Skan
28169695Skan/* This file defines the OpenMP internal control variables, and arranges
29169695Skan   for them to be initialized from environment variables at startup.  */
30169695Skan
31169695Skan#include "libgomp.h"
32169695Skan#include "libgomp_f.h"
33169695Skan#include <ctype.h>
34169695Skan#include <stdlib.h>
35169695Skan#include <string.h>
36169695Skan#include <limits.h>
37169695Skan#include <errno.h>
38169695Skan
39169695Skan
40169695Skanunsigned long gomp_nthreads_var = 1;
41169695Skanbool gomp_dyn_var = false;
42169695Skanbool gomp_nest_var = false;
43169695Skanenum gomp_schedule_type gomp_run_sched_var = GFS_DYNAMIC;
44169695Skanunsigned long gomp_run_sched_chunk = 1;
45282115Spfgunsigned short *gomp_cpu_affinity;
46282115Spfgsize_t gomp_cpu_affinity_len;
47169695Skan
48169695Skan/* Parse the OMP_SCHEDULE environment variable.  */
49169695Skan
50169695Skanstatic void
51169695Skanparse_schedule (void)
52169695Skan{
53169695Skan  char *env, *end;
54169695Skan  unsigned long value;
55169695Skan
56169695Skan  env = getenv ("OMP_SCHEDULE");
57169695Skan  if (env == NULL)
58169695Skan    return;
59169695Skan
60169695Skan  while (isspace ((unsigned char) *env))
61169695Skan    ++env;
62169695Skan  if (strncasecmp (env, "static", 6) == 0)
63169695Skan    {
64169695Skan      gomp_run_sched_var = GFS_STATIC;
65169695Skan      env += 6;
66169695Skan    }
67169695Skan  else if (strncasecmp (env, "dynamic", 7) == 0)
68169695Skan    {
69169695Skan      gomp_run_sched_var = GFS_DYNAMIC;
70169695Skan      env += 7;
71169695Skan    }
72169695Skan  else if (strncasecmp (env, "guided", 6) == 0)
73169695Skan    {
74169695Skan      gomp_run_sched_var = GFS_GUIDED;
75169695Skan      env += 6;
76169695Skan    }
77169695Skan  else
78169695Skan    goto unknown;
79169695Skan
80169695Skan  while (isspace ((unsigned char) *env))
81169695Skan    ++env;
82169695Skan  if (*env == '\0')
83169695Skan    return;
84169695Skan  if (*env++ != ',')
85169695Skan    goto unknown;
86169695Skan  while (isspace ((unsigned char) *env))
87169695Skan    ++env;
88169695Skan  if (*env == '\0')
89169695Skan    goto invalid;
90169695Skan
91169695Skan  errno = 0;
92169695Skan  value = strtoul (env, &end, 10);
93169695Skan  if (errno)
94169695Skan    goto invalid;
95169695Skan
96169695Skan  while (isspace ((unsigned char) *end))
97169695Skan    ++end;
98169695Skan  if (*end != '\0')
99169695Skan    goto invalid;
100169695Skan
101169695Skan  gomp_run_sched_chunk = value;
102169695Skan  return;
103169695Skan
104169695Skan unknown:
105169695Skan  gomp_error ("Unknown value for environment variable OMP_SCHEDULE");
106169695Skan  return;
107169695Skan
108169695Skan invalid:
109169695Skan  gomp_error ("Invalid value for chunk size in "
110169695Skan	      "environment variable OMP_SCHEDULE");
111169695Skan  return;
112169695Skan}
113169695Skan
114169695Skan/* Parse an unsigned long environment varible.  Return true if one was
115169695Skan   present and it was successfully parsed.  */
116169695Skan
117169695Skanstatic bool
118169695Skanparse_unsigned_long (const char *name, unsigned long *pvalue)
119169695Skan{
120169695Skan  char *env, *end;
121169695Skan  unsigned long value;
122169695Skan
123169695Skan  env = getenv (name);
124169695Skan  if (env == NULL)
125169695Skan    return false;
126169695Skan
127169695Skan  while (isspace ((unsigned char) *env))
128169695Skan    ++env;
129169695Skan  if (*env == '\0')
130169695Skan    goto invalid;
131169695Skan
132169695Skan  errno = 0;
133169695Skan  value = strtoul (env, &end, 10);
134169695Skan  if (errno || (long) value <= 0)
135169695Skan    goto invalid;
136169695Skan
137169695Skan  while (isspace ((unsigned char) *end))
138169695Skan    ++end;
139169695Skan  if (*end != '\0')
140169695Skan    goto invalid;
141169695Skan
142169695Skan  *pvalue = value;
143169695Skan  return true;
144169695Skan
145169695Skan invalid:
146169695Skan  gomp_error ("Invalid value for environment variable %s", name);
147169695Skan  return false;
148169695Skan}
149169695Skan
150169695Skan/* Parse a boolean value for environment variable NAME and store the
151169695Skan   result in VALUE.  */
152169695Skan
153169695Skanstatic void
154169695Skanparse_boolean (const char *name, bool *value)
155169695Skan{
156169695Skan  const char *env;
157169695Skan
158169695Skan  env = getenv (name);
159169695Skan  if (env == NULL)
160169695Skan    return;
161169695Skan
162169695Skan  while (isspace ((unsigned char) *env))
163169695Skan    ++env;
164169695Skan  if (strncasecmp (env, "true", 4) == 0)
165169695Skan    {
166169695Skan      *value = true;
167169695Skan      env += 4;
168169695Skan    }
169169695Skan  else if (strncasecmp (env, "false", 5) == 0)
170169695Skan    {
171169695Skan      *value = false;
172169695Skan      env += 5;
173169695Skan    }
174169695Skan  else
175169695Skan    env = "X";
176169695Skan  while (isspace ((unsigned char) *env))
177169695Skan    ++env;
178169695Skan  if (*env != '\0')
179169695Skan    gomp_error ("Invalid value for environment variable %s", name);
180169695Skan}
181169695Skan
182282115Spfg/* Parse the GOMP_CPU_AFFINITY environment varible.  Return true if one was
183282115Spfg   present and it was successfully parsed.  */
184282115Spfg
185282115Spfgstatic bool
186282115Spfgparse_affinity (void)
187282115Spfg{
188282115Spfg  char *env, *end;
189282115Spfg  unsigned long cpu_beg, cpu_end, cpu_stride;
190282115Spfg  unsigned short *cpus = NULL;
191282115Spfg  size_t allocated = 0, used = 0, needed;
192282115Spfg
193282115Spfg  env = getenv ("GOMP_CPU_AFFINITY");
194282115Spfg  if (env == NULL)
195282115Spfg    return false;
196282115Spfg
197282115Spfg  do
198282115Spfg    {
199282115Spfg      while (*env == ' ' || *env == '\t')
200282115Spfg	env++;
201282115Spfg
202282115Spfg      cpu_beg = strtoul (env, &end, 0);
203282115Spfg      cpu_end = cpu_beg;
204282115Spfg      cpu_stride = 1;
205282115Spfg      if (env == end || cpu_beg >= 65536)
206282115Spfg	goto invalid;
207282115Spfg
208282115Spfg      env = end;
209282115Spfg      if (*env == '-')
210282115Spfg	{
211282115Spfg	  cpu_end = strtoul (++env, &end, 0);
212282115Spfg	  if (env == end || cpu_end >= 65536 || cpu_end < cpu_beg)
213282115Spfg	    goto invalid;
214282115Spfg
215282115Spfg	  env = end;
216282115Spfg	  if (*env == ':')
217282115Spfg	    {
218282115Spfg	      cpu_stride = strtoul (++env, &end, 0);
219282115Spfg	      if (env == end || cpu_stride == 0 || cpu_stride >= 65536)
220282115Spfg		goto invalid;
221282115Spfg
222282115Spfg	      env = end;
223282115Spfg	    }
224282115Spfg	}
225282115Spfg
226282115Spfg      needed = (cpu_end - cpu_beg) / cpu_stride + 1;
227282115Spfg      if (used + needed >= allocated)
228282115Spfg	{
229282115Spfg	  unsigned short *new_cpus;
230282115Spfg
231282115Spfg	  if (allocated < 64)
232282115Spfg	    allocated = 64;
233282115Spfg	  if (allocated > needed)
234282115Spfg	    allocated <<= 1;
235282115Spfg	  else
236282115Spfg	    allocated += 2 * needed;
237282115Spfg	  new_cpus = realloc (cpus, allocated * sizeof (unsigned short));
238282115Spfg	  if (new_cpus == NULL)
239282115Spfg	    {
240282115Spfg	      free (cpus);
241282115Spfg	      gomp_error ("not enough memory to store GOMP_CPU_AFFINITY list");
242282115Spfg	      return false;
243282115Spfg	    }
244282115Spfg
245282115Spfg	  cpus = new_cpus;
246282115Spfg	}
247282115Spfg
248282115Spfg      while (needed--)
249282115Spfg	{
250282115Spfg	  cpus[used++] = cpu_beg;
251282115Spfg	  cpu_beg += cpu_stride;
252282115Spfg	}
253282115Spfg
254282115Spfg      while (*env == ' ' || *env == '\t')
255282115Spfg	env++;
256282115Spfg
257282115Spfg      if (*env == ',')
258282115Spfg	env++;
259282115Spfg      else if (*env == '\0')
260282115Spfg	break;
261282115Spfg    }
262282115Spfg  while (1);
263282115Spfg
264282115Spfg  gomp_cpu_affinity = cpus;
265282115Spfg  gomp_cpu_affinity_len = used;
266282115Spfg  return true;
267282115Spfg
268282115Spfg invalid:
269282115Spfg  gomp_error ("Invalid value for enviroment variable GOMP_CPU_AFFINITY");
270282115Spfg  return false;
271282115Spfg}
272282115Spfg
273169695Skanstatic void __attribute__((constructor))
274169695Skaninitialize_env (void)
275169695Skan{
276169695Skan  unsigned long stacksize;
277169695Skan
278169695Skan  /* Do a compile time check that mkomp_h.pl did good job.  */
279169695Skan  omp_check_defines ();
280169695Skan
281169695Skan  parse_schedule ();
282169695Skan  parse_boolean ("OMP_DYNAMIC", &gomp_dyn_var);
283169695Skan  parse_boolean ("OMP_NESTED", &gomp_nest_var);
284169695Skan  if (!parse_unsigned_long ("OMP_NUM_THREADS", &gomp_nthreads_var))
285169695Skan    gomp_init_num_threads ();
286282115Spfg  if (parse_affinity ())
287282115Spfg    gomp_init_affinity ();
288169695Skan
289169695Skan  /* Not strictly environment related, but ordering constructors is tricky.  */
290169695Skan  pthread_attr_init (&gomp_thread_attr);
291169695Skan  pthread_attr_setdetachstate (&gomp_thread_attr, PTHREAD_CREATE_DETACHED);
292169695Skan
293169695Skan  if (parse_unsigned_long ("GOMP_STACKSIZE", &stacksize))
294169695Skan    {
295169695Skan      int err;
296169695Skan
297169695Skan      stacksize *= 1024;
298169695Skan      err = pthread_attr_setstacksize (&gomp_thread_attr, stacksize);
299169695Skan
300169695Skan#ifdef PTHREAD_STACK_MIN
301169695Skan      if (err == EINVAL)
302169695Skan	{
303169695Skan	  if (stacksize < PTHREAD_STACK_MIN)
304169695Skan	    gomp_error ("Stack size less than minimum of %luk",
305169695Skan			PTHREAD_STACK_MIN / 1024ul
306169695Skan			+ (PTHREAD_STACK_MIN % 1024 != 0));
307169695Skan	  else
308169695Skan	    gomp_error ("Stack size larger than system limit");
309169695Skan	}
310169695Skan      else
311169695Skan#endif
312169695Skan      if (err != 0)
313169695Skan	gomp_error ("Stack size change failed: %s", strerror (err));
314169695Skan    }
315169695Skan}
316169695Skan
317169695Skan
318169695Skan/* The public OpenMP API routines that access these variables.  */
319169695Skan
320169695Skanvoid
321169695Skanomp_set_num_threads (int n)
322169695Skan{
323169695Skan  gomp_nthreads_var = (n > 0 ? n : 1);
324169695Skan}
325169695Skan
326169695Skanvoid
327169695Skanomp_set_dynamic (int val)
328169695Skan{
329169695Skan  gomp_dyn_var = val;
330169695Skan}
331169695Skan
332169695Skanint
333169695Skanomp_get_dynamic (void)
334169695Skan{
335169695Skan  return gomp_dyn_var;
336169695Skan}
337169695Skan
338169695Skanvoid
339169695Skanomp_set_nested (int val)
340169695Skan{
341169695Skan  gomp_nest_var = val;
342169695Skan}
343169695Skan
344169695Skanint
345169695Skanomp_get_nested (void)
346169695Skan{
347169695Skan  return gomp_nest_var;
348169695Skan}
349169695Skan
350169695Skanialias (omp_set_dynamic)
351169695Skanialias (omp_set_nested)
352169695Skanialias (omp_set_num_threads)
353169695Skanialias (omp_get_dynamic)
354169695Skanialias (omp_get_nested)
355