NEURON
inithoc.cpp
Go to the documentation of this file.
1 #include "nrnmpiuse.h"
2 #include "nrnpthread.h"
3 #include <stdio.h>
4 #include <stdint.h>
5 #include "nrnmpi.h"
6 #include "nrnpython_config.h"
7 #if defined(__MINGW32__)
8 #define _hypot hypot
9 #endif
10 #include "nrnpy_utils.h"
11 #include <stdlib.h>
12 #include <ctype.h>
13 
14 #if defined(NRNPYTHON_DYNAMICLOAD) && NRNPYTHON_DYNAMICLOAD > 0
15 // when compiled with different Python.h, force correct value
16 #undef NRNPYTHON_DYNAMICLOAD
17 #define NRNPYTHON_DYNAMICLOAD PY_MAJOR_VERSION
18 #endif
19 
20 
21 extern int nrn_is_python_extension;
22 extern int nrn_nobanner_;
23 extern int ivocmain(int, const char**, const char**);
24 extern int nrn_main_launch;
25 
26 
27 // int nrn_global_argc;
28 extern char** nrn_global_argv;
29 
30 extern void nrnpy_augment_path();
31 extern void (*p_nrnpython_finalize)();
32 extern PyObject* nrnpy_hoc();
33 
34 #if NRNMPI_DYNAMICLOAD
35 extern void nrnmpi_stubs();
36 extern char* nrnmpi_load(int is_python);
37 #endif
38 #if NRNPYTHON_DYNAMICLOAD
39 extern int nrnpy_site_problem;
40 #endif
41 
42 #if USE_PTHREAD
43 #include <pthread.h>
44 static pthread_t main_thread_;
45 #endif
46 
47 /**
48  * Manage argc,argv for calling ivocmain
49  * add_arg(...) will only add if name is not already in the arg list
50  * returns 1 if added, 0 if not
51 */
52 static size_t arg_size;
53 static int argc;
54 static char** argv;
55 static int add_arg(const char* name, const char* value) {
56  if (size_t(argc + 2 + bool(value)) >= arg_size) {
57  arg_size += 10;
58  argv = (char**)realloc(argv, arg_size*sizeof(char*));
59  }
60  // Don't add if already in argv
61  for (int i=1; i < argc; ++i) {
62  if (strcmp(name, argv[i]) == 0) {
63  return 0;
64  }
65  }
66  argv[argc++] = strdup(name);
67  if (value) {
68  argv[argc++] = strdup(value);
69  }
70  // To match C/C++ semantics for the main function then argv[argc] should
71  // always be null. https://en.cppreference.com/w/cpp/language/main_function,
72  // for example, says argv is a "Pointer to the first element of an array of
73  // argc + 1 pointers, of which the last one is null". Section 11.2.1 of the
74  // MPI 4.0 standard says "MPI_INIT accepts the argc and argv that are provided
75  // by the arguments to main or NULL", so it seems correct to follow the rules
76  // for a C/C++ main function. More pragmatically, OpenMPI assumes argv[argc]
77  // is nullptr and crashes if it is not.
78  argv[argc] = NULL;
79  return 1;
80 }
81 
82 /**
83  * Return 1 if string, 0 otherwise.
84 */
85 static int is_string(PyObject* po) {
86  if (PyUnicode_Check(po) || PyBytes_Check(po)) {
87  return 1;
88  }
89  return 0;
90 }
91 
92 /**
93  * Add all name:value from __main__.neuron_options dict if exists
94  * to the argc,argv for calling ivocmain
95  * Note: if value is None then only name is added to argv.
96  * The special "-print-options" name is not added to argv but
97  * causes a return value of 1. Otherwise the return value is 0.
98 */
99 static int add_neuron_options() {
100  PyObject* modules = PyImport_GetModuleDict();
101  PyObject* module = PyDict_GetItemString(modules, "__main__");
102  int rval = 0;
103  if (!module) {
104  PyErr_Clear();
105  PySys_WriteStdout("No __main__ module\n");
106  return rval;
107  }
108  PyObject* neuron_options = PyObject_GetAttrString(module, "neuron_options");
109  if (!neuron_options) {
110  PyErr_Clear();
111  return rval;
112  }
113  if (!PyDict_Check(neuron_options)) {
114  PySys_WriteStdout("__main__.neuron_options is not a dict\n");
115  return rval;
116  }
117  PyObject *key, *value;
118  Py_ssize_t pos = 0;
119  while(PyDict_Next(neuron_options, &pos, &key, &value)) {
120  if (!is_string(key) || (!is_string(value) && value != Py_None)) {
121  PySys_WriteStdout("A neuron_options key:value is not a string:string or string:None\n");
122  continue;
123  }
124  Py2NRNString skey(key);
125  Py2NRNString sval(value);
126  if (strcmp(skey.c_str(), "-print-options") == 0) {
127  rval = 1;
128  continue;
129  }
130  add_arg(skey.c_str(), sval.c_str());
131  }
132  return rval;
133 }
134 
135 /**
136  * Space separated options. Must handle escaped space, '...' and "...".
137  * Return 1 if contains a -print-options (not added to options)
138 */
139 
140 static int add_space_separated_options(const char* str) {
141  int rval = 0;
142  if (!str) { return rval; }
143  char* s = strdup(str);
144  //int state = 0; // 1 means in "...", 2 means in '...'
145  for (char* cp = s; *cp; cp++) {
146  while (isspace(*cp)) { // skip spaces
147  ++cp;
148  if (*cp == '\0') {
149  free(s);
150  return rval;
151  }
152  }
153  // start processing a token
154  char* cpbegin = cp;
155  char* cp1 = cpbegin; // in token pointer, escapes cause to lag behind
156  while (!isspace(*cp) && *cp != '\0') { // to next space delimiter
157  *cp1++ = *cp++;
158  if (cp1[-1] == '\\' && (isspace(*cp) || *cp == '"' || *cp == '\'')) {
159  // escaped space, ", or '
160  cp1[-1] = *cp++;
161  }else if (cp1[-1] == '"') { // consume to next (unescaped) "
162  cp1--; // backup over the "
163  while (*cp != '"' && *cp != '\0') {
164  *cp1++ = *cp++;
165  if (cp1[-1] == '\\' && *cp == '"') { // escaped " inside "..."
166  cp1[-1] = *cp++;
167  }
168  }
169  if (*cp == '"') {
170  cp++; // skip over the closing "
171  }
172  }else if (cp1[-1] == '\'') { // consume to next (unescaped) '
173  cp1--; // backup over the '
174  while (*cp != '\'' && *cp != '\0') {
175  *cp1++ = *cp++;
176  if (cp1[-1] == '\\' && *cp == '\'') { // escaped ' inside '...'
177  cp1[-1] = *cp++;
178  }
179  }
180  if (*cp == '\'') {
181  cp++; // skip over the closing '
182  }
183  }
184  }
185  if (*cp == '\0') { // at end of s. 'for' will return after it increments.
186  --cp;
187  }
188  if (cp1 > cpbegin) {
189  *cp1 = '\0';
190  }
191  if (strcmp(cpbegin, "-print-options") == 0) {
192  rval = 1;
193  }else{
194  add_arg(cpbegin, NULL);
195  }
196  }
197  free(s);
198  return rval;
199 }
200 
201 /**
202  * Return 1 if the option exists in argv[]
203 */
204 static int have_opt(const char* arg) {
205  if (!arg) { return 0; }
206  for (int i=0; i < argc; ++i) {
207  if (strcmp(arg, argv[i]) == 0) {
208  return 1;
209  }
210  }
211  return 0;
212 }
213 
215 #if USE_PTHREAD
216  pthread_t now = pthread_self();
217  if (pthread_equal(main_thread_, now)) {
218 #else
219  {
220 #endif
221  Py_Finalize();
222  }
223 #if linux
224  if (system("stty sane > /dev/null 2>&1")){} // 'if' to avoid ignoring return value warning
225 #endif
226 }
227 
228 static char* env[] = {0};
229 
230 extern "C" PyObject* PyInit_hoc() {
231 
232  char buf[200];
233 
234 #if USE_PTHREAD
235  main_thread_ = pthread_self();
236 #endif
237 
238  if (nrn_global_argv) { // ivocmain was already called so already loaded
239  return nrnpy_hoc();
240  }
241 
242  add_arg("NEURON", NULL);
243  int print_options = add_neuron_options();
244  print_options += add_space_separated_options(getenv("NEURON_MODULE_OPTIONS"));
245 
246 #ifdef NRNMPI
247 
248  int flag = 0; // true if MPI_Initialized is called
249  int mpi_mes = 0; // for printing mpi message only once
250  int libnrnmpi_is_loaded = 1; // becomes 0 if NEURON_INIT_MPI == 0 with dynamic mpi
251  char* pmes = NULL; // error message
252  char* env_mpi = getenv("NEURON_INIT_MPI");
253 
254 #if NRNMPI_DYNAMICLOAD
255  nrnmpi_stubs();
256  /**
257  * In case of dynamic mpi build we load MPI unless NEURON_INIT_MPI is explicitly set to 0.
258  * and there is no '-mpi' arg.
259  * We call nrnmpi_load to load MPI library which returns:
260  * - nil if loading is successfull
261  * - error message in case of loading error
262  */
263  if(env_mpi != NULL && strcmp(env_mpi, "0") == 0 && !have_opt("-mpi")) {
264  libnrnmpi_is_loaded = 0;
265  }
266  if (libnrnmpi_is_loaded) {
267  pmes = nrnmpi_load(1);
268  if (pmes && env_mpi == NULL) {
269  // common case on MAC distribution is no NEURON_INIT_MPI and
270  // no MPI installed (so nrnmpi_load fails)
271  libnrnmpi_is_loaded = 0;
272  }
273  if (pmes && libnrnmpi_is_loaded) {
274  printf(
275  "NEURON_INIT_MPI nonzero in env (or -mpi arg) but NEURON cannot initialize MPI "
276  "because:\n%s\n",
277  pmes);
278  exit(1);
279  }
280  }
281 #else
282  have_opt(NULL); // avoid 'defined but not used' warning
283 #endif
284 
285  /**
286  * In case of non-dynamic mpi build mpi library is already linked. We add -mpi
287  * argument in following scenario:
288  * - if user has not explicitly set NEURON_INIT_MPI to 0 and mpi is already initialized
289  * - if user has explicitly set NEURON_INIT_MPI to 1 to load mpi initialization
290  */
291  if (libnrnmpi_is_loaded) {
292  nrnmpi_wrap_mpi_init(&flag);
293  if (flag) {
294  mpi_mes = 1;
295  add_arg("-mpi", NULL);
296  } else if(env_mpi != NULL && strcmp(env_mpi, "1") == 0) {
297  mpi_mes = 2;
298  add_arg("-mpi", NULL);
299  }else{
300  mpi_mes = 3;
301  }
302  } else {
303  // no mpi
304  mpi_mes = 3;
305  }
306 
307  // merely avoids unused variable warning
308  if (pmes && mpi_mes == 2){
309  exit(1);
310  }
311 
312 #endif //NRNMPI
313 
314 #if !defined(__CYGWIN__)
315  sprintf(buf, "%s/.libs/libnrnmech.so", NRNHOSTCPU);
316  // printf("buf = |%s|\n", buf);
317  FILE* f;
318  if ((f = fopen(buf, "r")) != 0) {
319  fclose(f);
320  add_arg("-dll", buf);
321  }
322 #endif // !defined(__CYGWIN__)
324  nrn_nobanner_ = 1;
325  const char* pyver = Py_GetVersion();
326  nrn_is_python_extension = (pyver[0]-'0')*10 + (pyver[2] - '0');
327  if (isdigit(pyver[3])) { // minor >= 10 e.g. 3.10 is 310
328  nrn_is_python_extension = nrn_is_python_extension*10 + pyver[3] - '0';
329  }
331 #if NRNMPI
332  if (libnrnmpi_is_loaded) {
333  nrnmpi_init(1, &argc, &argv); // may change argc and argv
334  }
335 #if 0 && !defined(NRNMPI_DYNAMICLOAD)
336  if (nrnmpi_myid == 0) {
337  switch(mpi_mes) {
338  case 0:
339  break;
340  case 1:
341  printf("MPI_Initialized==true, MPI functionality enabled by Python.\n");
342  break;
343  case 2:
344  printf("MPI functionality enabled by NEURON.\n");
345  break;
346  case 3:
347  printf("MPI_Initialized==false, MPI functionality not enabled.\n");
348  break;
349  }
350  }
351 #endif // 0 && !defined(NRNMPI_DYNAMICLOAD)
352  if (pmes) {
353  free(pmes);
354  }
355 #endif // NRNMPI
356 
357  char* env_nframe = getenv("NEURON_NFRAME");
358  if(env_nframe != NULL ) {
359  char *endptr;
360  const int nframe_env_value = strtol(env_nframe, &endptr, 10);
361  if (*endptr == '\0') {
362  if(nframe_env_value > 0) {
363  add_arg("-NFRAME", env_nframe);
364  }else{
365  PySys_WriteStdout(
366  "NEURON_NFRAME env value must be positive\n");
367  }
368  }else{
369  PySys_WriteStdout(
370  "NEURON_NFRAME env value is invalid!\n");
371  }
372 
373  }
374 
375  if (print_options) {
376  PySys_WriteStdout("ivocmain options:");
377  for (int i=1; i < argc; ++i ) {
378  PySys_WriteStdout(" '%s'", argv[i]);
379  }
380  PySys_WriteStdout("\n");
381  }
382 
383  nrn_main_launch = 2;
384  ivocmain(argc, (const char**)argv, (const char**)env);
385 // nrnpy_augment_path();
386 #if NRNPYTHON_DYNAMICLOAD
387  nrnpy_site_problem = 0;
388 #endif // NRNPYTHON_DYNAMICLOAD
389  return nrnpy_hoc();
390 }
391 
392 #if !defined(CYGWIN)
393 extern "C" void modl_reg() {}
394 #endif // !defined(CYGWIN)
PyObject * nrnpy_hoc()
Definition: nrnpy_hoc.cpp:3069
char * c_str() const
Definition: nrnpy_utils.h:42
static int is_string(PyObject *po)
Return 1 if string, 0 otherwise.
Definition: inithoc.cpp:85
void nrnpy_augment_path()
Definition: nrnpython.cpp:44
int nrn_main_launch
Definition: hoc_init.cpp:396
void
sprintf(buf," if (secondorder) {\ " int _i;\" " for(_i=0;_i< %d;++_i) {\" " _p[_slist%d[_i]]+=dt *_p[_dlist%d[_i]];\" " }}\", numeqn, listnum, listnum)
int system(const char *s)
Definition: mswinprt.cpp:281
static int have_opt(const char *arg)
Return 1 if the option exists in argv[].
Definition: inithoc.cpp:204
void(* p_nrnpython_finalize)()
_CONST char * s
Definition: system.cpp:74
static int add_neuron_options()
Add all name:value from main.neuron_options dict if exists to the argc,argv for calling ivocmain Note...
Definition: inithoc.cpp:99
#define printf
Definition: mwprefix.h:26
void nrnmpi_init(int nrnmpi_under_nrncontrol, int *pargc, char ***pargv)
Definition: nrnmpi.cpp:58
int ivocmain(int, const char **, const char **)
Main entrypoint function into the HOC interpeter.
Definition: ivocmain.cpp:417
#define key
Definition: spt2queue.cpp:20
char * getenv(const char *s)
Definition: macprt.cpp:67
static char * env[]
Definition: inithoc.cpp:228
char * name
Definition: init.cpp:16
PyObject * PyInit_hoc()
Definition: inithoc.cpp:230
int nrn_is_python_extension
Definition: fileio.cpp:871
char ** nrn_global_argv
Definition: hoc.cpp:30
static uint32_t value
Definition: scoprand.cpp:26
int nrn_nobanner_
Definition: hoc.cpp:146
int nrnmpi_myid
static int add_arg(const char *name, const char *value)
Definition: inithoc.cpp:55
#define i
Definition: md1redef.h:12
void modl_reg()
Definition: inithoc.cpp:393
#define arg
Definition: redef.h:28
char buf[512]
Definition: init.cpp:13
static int argc
Definition: inithoc.cpp:53
FILE * fopen()
return NULL
Definition: cabcode.cpp:461
static char ** argv
Definition: inithoc.cpp:54
static size_t arg_size
Manage argc,argv for calling ivocmain add_arg(...) will only add if name is not already in the arg li...
Definition: inithoc.cpp:52
void nrnpython_finalize()
Definition: inithoc.cpp:214
static int add_space_separated_options(const char *str)
Space separated options.
Definition: inithoc.cpp:140