OpenCog Framework  Branch: master, revision 6f0b7fc776b08468cf1b74aa9db028f387b4f0c0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
SchemeEval.cc
Go to the documentation of this file.
1 
10 #ifdef HAVE_GUILE
11 
12 #include <unistd.h>
13 #include <fcntl.h>
14 
15 #include <cstddef>
16 #include <libguile.h>
17 #include <libguile/backtrace.h>
18 #include <libguile/debug.h>
19 #ifndef HAVE_GUILE2
20  #include <libguile/lang.h>
21 #endif
22 #include <pthread.h>
23 
24 #include <opencog/util/Logger.h>
25 #include <opencog/util/oc_assert.h>
26 #include <opencog/util/platform.h>
28 
29 #include "SchemeEval.h"
30 #include "SchemePrimitive.h"
31 #include "SchemeSmob.h"
32 
33 using namespace opencog;
34 
35 std::mutex init_mtx;
36 
41 void SchemeEval::init(void)
42 {
43  // Arghhh! Avoid ongoing utf8 fruitcake nutiness in guile-2.0
44  scm_c_eval_string ("(setlocale LC_ALL "")\n");
45 
48 
49  _in_server = false;
50  _in_redirect = 0;
51  _in_shell = false;
52  _in_eval = false;
53 
54  // User error and crash management
55  error_string = SCM_EOL;
56  error_string = scm_gc_protect_object(error_string);
57 
58  captured_stack = SCM_BOOL_F;
59  captured_stack = scm_gc_protect_object(captured_stack);
60 
61  pexpr = NULL;
62  _eval_done = false;
63  _poll_done = false;
64 
65  _rc = SCM_EOL;
66  _rc = scm_gc_protect_object(_rc);
67 
68  _gc_ctr = 0;
69 }
70 
79 {
80  // If we've already captured, don't do it again.
81  if (_in_server) return;
82 
83  // Lock to prevent racey setting of the output port.
84  // XXX FIXME This lock is not needed, because in guile 2.2,
85  // at least, every thread has its own output port, and so its
86  // impossible for two different threads to compete to set the
87  // same outport. Not to sure about guile-2.0, though... so
88  // I'm leaving the lock in, for now. Its harmless.
89  std::lock_guard<std::mutex> lck(init_mtx);
90 
91  // Try again, under the lock this time.
92  if (_in_server) return;
93  _in_server = true;
94  _in_redirect = 1;
95 
96  // When running in the cogserver, this pipe will become the output
97  // port. Scheme code will be writing into one end of it, while, in a
98  // different thread, we will be sucking it dry, and displaying the
99  // contents to the user.
100  SCM pair = scm_pipe();
101  _pipe = scm_car(pair);
102  _pipe = scm_gc_protect_object(_pipe);
103  _pipeno = scm_to_int(scm_fileno(_pipe));
104  _outport = scm_cdr(pair);
105  _outport = scm_gc_protect_object(_outport);
106  scm_setvbuf(_outport, scm_from_int (_IONBF), SCM_UNDEFINED);
107 
108  // We want non-blocking reads.
109  int flags = fcntl(_pipeno, F_GETFL, 0);
110  if (flags < 0) flags = 0;
111  fcntl(_pipeno, F_SETFL, flags | O_NONBLOCK);
112 }
113 
120 {
121  _in_redirect++;
122  if (1 < _in_redirect) return;
123  capture_port();
124 
125  // Output ports for side-effects.
126  _saved_outport = scm_current_output_port();
127  _saved_outport = scm_gc_protect_object(_saved_outport);
128 
129  scm_set_current_output_port(_outport);
130 }
131 
133 {
134  _in_redirect --;
135  if (0 < _in_redirect) return;
136 
137  // Restore the previous outport (if its still alive)
138  if (scm_is_false(scm_port_closed_p(_saved_outport)))
139  scm_set_current_output_port(_saved_outport);
140  scm_gc_unprotect_object(_saved_outport);
141 }
142 
145 {
146  // read and discard.
147  poll_port();
148 }
149 
150 void * SchemeEval::c_wrap_init(void *p)
151 {
152  SchemeEval *self = (SchemeEval *) p;
153  self->init();
154  return self;
155 }
156 
158 {
159  scm_gc_unprotect_object(_rc);
160 
161  std::lock_guard<std::mutex> lck(init_mtx);
162 
163  // If we had once set up the async I/O, the release it.
164  if (_in_server)
165  {
166  scm_close_port(_outport);
167  scm_gc_unprotect_object(_outport);
168 
169  scm_close_port(_pipe);
170  scm_gc_unprotect_object(_pipe);
171  }
172 
173  scm_gc_unprotect_object(error_string);
174  scm_gc_unprotect_object(captured_stack);
175 
176  // Force garbage collection
177  scm_gc();
178 }
179 
181 {
182  SchemeEval *self = (SchemeEval *) p;
183  self->finish();
184  return self;
185 }
186 
187 // The following two routines are needed to avoid bad garbage collection
188 // of anything we've kept in the object.
190 {
191  // protect before unprotecting, to avoid multi-threaded races.
192  SCM oldstack = captured_stack;
193  captured_stack = scm_gc_protect_object(newstack);
194  scm_gc_unprotect_object(oldstack);
195 }
196 
198 {
199  SCM olderror = error_string;
200  error_string = scm_gc_protect_object(newerror);
201  scm_gc_unprotect_object(olderror);
202 }
203 
204 static std::atomic_flag eval_is_inited = ATOMIC_FLAG_INIT;
205 static thread_local bool thread_is_inited = false;
206 
207 #ifndef HAVE_GUILE2
208  #define WORK_AROUND_GUILE_185_BUG
209 #endif
210 #ifdef WORK_AROUND_GUILE_185_BUG
211 /* There's a bug in guile-1.8.5, where the second and subsequent
212  * threads run in guile mode with a bogus/broken current-module.
213  * This cannot be worked around by anything as simple as saying
214  * "(set-current-module the-root-module)" because dynwind undoes
215  * any module-setting that we do.
216  *
217  * So we work around it here, by explicitly setting the module
218  * outside of a dynwind context.
219  */
220 static SCM guile_user_module;
221 
222 static void * do_bogus_scm(void *p)
223 {
224  scm_c_eval_string ("(+ 2 2)\n");
225  return p;
226 }
227 #endif /* WORK_AROUND_GUILE_185_BUG */
228 
229 #ifndef HAVE_GUILE2
230  #define WORK_AROUND_GUILE_THREADING_BUG
231 #endif
232 #ifdef WORK_AROUND_GUILE_THREADING_BUG
233 /* There are bugs in guile-1.8.6 and earlier that prevent proper
234  * multi-threaded operation. Currently, the most serious of these is
235  * a parallel-define bug, documented in
236  * https://savannah.gnu.org/bugs/index.php?24867
237  *
238  * Until that bug is fixed and released, this work-around is needed.
239  * The work-around serializes all guile-mode thread execution, by
240  * means of a mutex lock.
241  *
242  * As of December 2013, the bug still seems to be there: the test
243  * case provided in the bug report crashes, when linked against
244  * guile-2.0.5 and gc-7.1 from Ubuntu Precise.
245  *
246  * Its claimed that the bug only happens for top-level defines.
247  * Thus, in principle, threading should be OK after all scripts have
248  * been loaded.
249  *
250  * FWIW, the unit test MultiThreadUTest tests atom creation in multiple
251  * threads. As of 29 Nov 2014, it passes, for me, using guile-2.0.9
252  * which is the stock version of guile in Mint Qiana 17 aka Ubuntu 14.04
253  */
254 static pthread_mutex_t serialize_lock;
255 static pthread_key_t ser_key = 0;
256 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
257 
258 // Initialization that needs to be performed only once, for the entire
259 // process.
260 static void init_only_once(void)
261 {
262  if (eval_is_inited.test_and_set()) return;
263 
264 #ifdef WORK_AROUND_GUILE_THREADING_BUG
265  pthread_mutex_init(&serialize_lock, NULL);
266  pthread_key_create(&ser_key, NULL);
267  pthread_setspecific(ser_key, (const void *) 0x0);
268 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
269 #ifdef WORK_AROUND_GUILE_185_BUG
270  scm_with_guile(do_bogus_scm, NULL);
271  guile_user_module = scm_current_module();
272 #endif /* WORK_AROUND_GUILE_185_BUG */
273 }
274 
275 #ifdef WORK_AROUND_GUILE_THREADING_BUG
276 
282 {
283  long cnt = (long) pthread_getspecific(ser_key);
284  if (0 >= cnt)
285  {
286  pthread_mutex_lock(&serialize_lock);
287  }
288  cnt ++;
289  pthread_setspecific(ser_key, (const void *) cnt);
290 }
291 
293 {
294  long cnt = (long) pthread_getspecific(ser_key);
295  cnt --;
296  pthread_setspecific(ser_key, (const void *) cnt);
297  if (0 >= cnt)
298  {
299  pthread_mutex_unlock(&serialize_lock);
300  }
301 }
302 #endif
303 
305 {
306  init_only_once();
307 
308 #ifdef WORK_AROUND_GUILE_THREADING_BUG
309  thread_lock();
310 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
311  atomspace = as;
312 
313  scm_with_guile(c_wrap_init, this);
314 
315 #ifdef WORK_AROUND_GUILE_THREADING_BUG
316  thread_unlock();
317 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
318 
319 }
320 
321 /* This should be called once for every new thread. */
323 {
324  /* Avoid more than one call per thread. */
325  if (thread_is_inited) return;
326  thread_is_inited = true;
327 
328 #ifdef WORK_AROUND_GUILE_185_BUG
329  scm_set_current_module(guile_user_module);
330 #endif /* WORK_AROUND_GUILE_185_BUG */
331 
332  // Arghhh! Avoid ongoing utf8 fruitcake nutiness in guile-2.0
333  scm_c_eval_string ("(setlocale LC_ALL "")\n");
334 }
335 
337 {
338  scm_with_guile(c_wrap_finish, this);
339 }
340 
341 /* ============================================================== */
342 
343 std::string SchemeEval::prt(SCM node)
344 {
345  if (SCM_SMOB_PREDICATE(SchemeSmob::cog_misc_tag, node))
346  {
347  return SchemeSmob::misc_to_string(node);
348  }
349  else if (scm_is_eq(node, SCM_UNSPECIFIED))
350  {
351  return "";
352  }
353  else
354  {
355  // Let SCM display do the rest of the work.
356  SCM port = scm_open_output_string();
357  scm_display (node, port);
358  SCM rc = scm_get_output_string(port);
359  char * str = scm_to_utf8_string(rc);
360  std::string rv = str;
361  free(str);
362  scm_close_port(port);
363  return rv;
364  }
365 
366  return "";
367 }
368 
369 /* ============================================================== */
370 
371 SCM SchemeEval::preunwind_handler_wrapper (void *data, SCM tag, SCM throw_args)
372 {
373  SchemeEval *ss = (SchemeEval *) data;
374  return ss->preunwind_handler(tag, throw_args);
375 }
376 
377 SCM SchemeEval::catch_handler_wrapper (void *data, SCM tag, SCM throw_args)
378 {
379  SchemeEval *ss = (SchemeEval *) data;
380  return ss->catch_handler(tag, throw_args);
381 }
382 
383 SCM SchemeEval::preunwind_handler (SCM tag, SCM throw_args)
384 {
385  // We can only record the stack before it is unwound.
386  // The normal catch handler body runs only *after* the stack
387  // has been unwound.
388  set_captured_stack(scm_make_stack(SCM_BOOL_T, SCM_EOL));
389  return SCM_EOL;
390 }
391 
392 SCM SchemeEval::catch_handler (SCM tag, SCM throw_args)
393 {
394  // Check for read error. If a read error, then wait for user to correct it.
395  SCM re = scm_symbol_to_string(tag);
396  char * restr = scm_to_utf8_string(re);
397  _pending_input = false;
398 
399  if (0 == strcmp(restr, "read-error"))
400  {
401  _pending_input = true;
402  free(restr);
403  return SCM_EOL;
404  }
405 
406  // Check for a simple flow-control directive: i.e. just return to
407  // the C code from anywhere within the scheme code.
408  if (0 == strcmp(restr, "cog-yield"))
409  {
410  free(restr);
411  return SCM_CAR(throw_args);
412  }
413 
414  // If it's not a read error, and it's not flow-control,
415  // then its a regular error; report it.
416  _caught_error = true;
417 
418  /* get string port into which we write the error message and stack. */
419  SCM port = scm_open_output_string();
420 
421  if (scm_is_true(scm_list_p(throw_args)) && (scm_ilength(throw_args) >= 1))
422  {
423  long nargs = scm_ilength(throw_args);
424  SCM subr = SCM_CAR (throw_args);
425  SCM message = SCM_EOL;
426  if (nargs >= 2)
427  message = SCM_CADR (throw_args);
428  SCM parts = SCM_EOL;
429  if (nargs >= 3)
430  parts = SCM_CADDR (throw_args);
431  SCM rest = SCM_EOL;
432  if (nargs >= 4)
433  rest = SCM_CADDDR (throw_args);
434 
435  if (scm_is_true (captured_stack))
436  {
437  SCM highlights;
438 
439  if (scm_is_eq (tag, scm_arg_type_key) ||
440  scm_is_eq (tag, scm_out_of_range_key))
441  highlights = rest;
442  else
443  highlights = SCM_EOL;
444 
445  scm_puts ("Backtrace:\n", port);
446  scm_display_backtrace_with_highlights (captured_stack, port,
447  SCM_BOOL_F, SCM_BOOL_F,
448  highlights);
449  scm_newline (port);
450  }
451 #ifdef HAVE_GUILE2
452  if (SCM_STACK_LENGTH (captured_stack))
453  set_captured_stack(scm_stack_ref (captured_stack, SCM_INUM0));
454 #endif
455  scm_display_error (captured_stack, port, subr, message, parts, rest);
456  }
457  else
458  {
459  scm_puts ("ERROR: throw args are unexpectedly short!\n", port);
460  }
461  scm_puts("ABORT: ", port);
462  scm_puts(restr, port);
463  free(restr);
464 
465  set_error_string(scm_get_output_string(port));
466  scm_close_port(port);
467  return SCM_BOOL_F;
468 }
469 
470 /* ============================================================== */
490 void SchemeEval::eval_expr(const std::string &expr)
491 {
492  // If we are recursing, then we already are in the guile
493  // environment, and don't need to do any additional setup.
494  // Just go.
495  if (_in_eval) {
496  do_eval(expr);
497  return;
498  }
499 
500 #ifdef WORK_AROUND_GUILE_THREADING_BUG
501  thread_lock();
502 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
503 
504  pexpr = &expr;
505  _in_shell = true;
506  _in_eval = true;
507  scm_with_guile(c_wrap_eval, this);
508  _in_eval = false;
509  _in_shell = false;
510 
511 #ifdef WORK_AROUND_GUILE_THREADING_BUG
512  thread_unlock();
513 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
514 }
515 
517 {
518  SchemeEval* self = (SchemeEval*) p;
519  self->answer = self->do_poll_result();
520  return p;
521 }
522 
524 {
525  scm_with_guile(c_wrap_poll, this);
526  return answer;
527 }
528 
530 {
531  SchemeEval* self = (SchemeEval*) p;
532 
533  // Normally, neither of these are ever null.
534  // But sometimes, a heavily loaded server can crash here.
535  // Trying to figure out why ...
536  OC_ASSERT(self, "c_wrap_eval got null pointer!");
537  OC_ASSERT(self->pexpr, "c_wrap_eval got null expression!");
538 
539  self->do_eval(*(self->pexpr));
540  return self;
541 }
542 
551 static void do_gc(void)
552 {
553  static size_t prev_usage = 0;
554 
555  size_t curr_usage = getMemUsage();
556  // Yes, this is 10MBytes. Which seems nutty. But it does the trick...
557  if (10 * 1024 * 1024 < curr_usage - prev_usage)
558  {
559  prev_usage = curr_usage;
560  scm_gc();
561  }
562 
563 #if 0
564  static SCM since = scm_from_utf8_symbol("heap-allocated-since-gc");
565  SCM stats = scm_gc_stats();
566  size_t allo = scm_to_size_t(scm_assoc_ref(stats, since));
567 
568  int times = scm_to_int(scm_assoc_ref(stats, scm_from_utf8_symbol("gc-times")));
569  printf("allo=%lu mem=%lu time=%d\n", allo/1024, (getMemUsage() / (1024)), times);
570  scm_gc();
571  logger().info() << "Guile evaluated: " << expr;
572  logger().info() << "Mem usage=" << (getMemUsage() / (1024*1024)) << "MB";
573 #endif
574 }
575 
583 void SchemeEval::do_eval(const std::string &expr)
584 {
585  per_thread_init();
586 
587  // Set global atomspace variable in the execution environment.
588  AtomSpace* saved_as = NULL;
589  if (atomspace)
590  {
591  saved_as = SchemeSmob::ss_get_env_as("do_eval");
592  if (saved_as != atomspace)
594  else
595  saved_as = NULL;
596  }
597 
598  _input_line += expr;
599 
600  redirect_output();
601  _caught_error = false;
602  _pending_input = false;
603  error_msg.clear();
604  set_captured_stack(SCM_BOOL_F);
605  scm_gc_unprotect_object(_rc);
606  SCM eval_str = scm_from_utf8_string(_input_line.c_str());
607  _rc = scm_c_catch (SCM_BOOL_T,
608  (scm_t_catch_body) scm_eval_string,
609  (void *) eval_str,
612  _rc = scm_gc_protect_object(_rc);
613 
614  restore_output();
615 
616  if (saved_as)
617  SchemeSmob::ss_set_env_as(saved_as);
618 
619  if (++_gc_ctr%80 == 0) { do_gc(); _gc_ctr = 0; }
620 
621  _eval_done = true;
622  _wait_done.notify_all();
623 }
624 
626 {
627  _eval_done = false;
628  _poll_done = false;
629 }
630 
639 {
640  std::string rv;
641 
642  // drain_output() calls us, and not always in server mode.
643  if (not _in_server) return rv;
644 
645 #define BUFSZ 1000
646  char buff[BUFSZ];
647  while (1)
648  {
649  int nr = read(_pipeno, buff, BUFSZ-1);
650  if (-1 == nr) return rv;
651  buff[nr] = 0x0;
652  rv += buff;
653  }
654  return rv;
655 }
656 
676 {
677  per_thread_init();
678  if (_poll_done) return "";
679 
680  if (not _eval_done)
681  {
682  // We don't have a real need to lock anything here; we're just
683  // using this as a hack, so that the condition variable will
684  // wake us up periodically. The goal here is to block when
685  // there's no output to be reported.
686  //
687  // Hmmm. I guess we could just block on reading the pipe...
688  // unless the eval did not produce any output, in which case
689  // I guess we could ... close and re-open the pipe? Somehow
690  // unblock it? I dunno. The below curently works, and I'm
691  // loosing interest just right now.
692  std::unique_lock<std::mutex> lck(_poll_mtx);
693  while (not _eval_done)
694  {
695  _wait_done.wait_for(lck, std::chrono::milliseconds(300));
696  std::string rv = poll_port();
697  if (0 < rv.size()) return rv;
698  }
699  }
700  // If we are here, then evaluation is done. Check the various
701  // evalution result flags, etc.
702  _poll_done = true;
703 
704  // Save the result of evaluation, and clear it. Recall that _rc is
705  // typically set in a different thread. We want it cleared before
706  // we ever get here again, on later evals.
707  SCM tmp_rc = _rc;
708  scm_gc_unprotect_object(_rc);
709  _rc = SCM_EOL;
710  _rc = scm_gc_protect_object(_rc);
711 
712  /* An error is thrown if the input expression is incomplete,
713  * in which case the error handler sets the _pending_input flag
714  * to true. */
715  if (_pending_input)
716  {
717  return "";
718  }
719  _pending_input = false;
720  _input_line = "";
721 
722  if (_caught_error)
723  {
724  std::string rv = poll_port();
725 
726  char * str = scm_to_utf8_string(error_string);
727  rv += str;
728  free(str);
729  set_error_string(SCM_EOL);
730  set_captured_stack(SCM_BOOL_F);
731 
732  rv += "\n";
733  return rv;
734  }
735  else
736  {
737  // First, we get the contents of the output port,
738  // and pass that on.
739  std::string rv = poll_port();
740 
741  // Next, we append the "interpreter" output
742  rv += prt(tmp_rc);
743  rv += "\n";
744  scm_remember_upto_here_1(tmp_rc);
745  return rv;
746  }
747  return "#<Error: Unreachable statement reached>";
748 }
749 
750 /* ============================================================== */
751 
764 SCM SchemeEval::do_scm_eval(SCM sexpr, SCM (*evo)(void *))
765 {
766  per_thread_init();
767 
768  // Set global atomspace variable in the execution environment.
769  AtomSpace* saved_as = NULL;
770  if (atomspace)
771  {
772  saved_as = SchemeSmob::ss_get_env_as("do_scm_eval");
773  if (saved_as != atomspace)
775  else
776  saved_as = NULL;
777  }
778 
779  // If we are running from the cogserver shell, capture all output
780  if (_in_shell)
781  redirect_output();
782 
783  _caught_error = false;
784  error_msg.clear();
785  set_captured_stack(SCM_BOOL_F);
786  SCM rc = scm_c_catch (SCM_BOOL_T,
787  evo, (void *) sexpr,
790 
791  _eval_done = true;
792 
793  // Restore the outport
794  if (_in_shell)
795  restore_output();
796 
797  if (saved_as)
798  SchemeSmob::ss_set_env_as(saved_as);
799 
800  if (_caught_error)
801  {
802  char * str = scm_to_utf8_string(error_string);
803  // Don't blank out the error string yet.... we need it later.
804  // (probably because someone called cog-bind with an
805  // ExecutionOutputLink in it with a bad scheme schema node.)
806  // set_error_string(SCM_EOL);
807  set_captured_stack(SCM_BOOL_F);
808 
809  // ?? Why are we discarding the output??
810  drain_output();
811 
812  // Stick the guile stack trace into a string. Anyone who called
813  // us is responsible for checking for an error, and handling
814  // it as needed.
815  error_msg = str;
816  error_msg += "\n";
817 
818  free(str);
819  return SCM_EOL;
820  }
821 
822  // Get the contents of the output port, and log it
823  if (_in_server and logger().isInfoEnabled())
824  {
825  std::string str(poll_port());
826  if (0 < str.size())
827  {
828  logger().info("%s: Output: %s\n"
829  "Was generated by expr: %s\n",
830  __FUNCTION__, str.c_str(), prt(sexpr).c_str());
831  }
832  }
833 
834  // If we are in the cogservdr, but are anot in a shell context,
835  // then truncate the output, because it will never ever be displayed.
836  // (i.e. don't overflow the output buffers.) If we are in_shell,
837  // then we are here probably because user typed something that
838  // caused some ExecutionOutputLink to call some scheme snippet.
839  // We do want to display that.
840  if (_in_server and not _in_shell)
841  drain_output();
842 
843  return rc;
844 }
845 
846 /* ============================================================== */
847 
848 SCM recast_scm_eval_string(void * expr)
849 {
850  return scm_eval_string((SCM)expr);
851 }
852 
858 Handle SchemeEval::eval_h(const std::string &expr)
859 {
860  // If we are recursing, then we already are in the guile
861  // environment, and don't need to do any additional setup.
862  // Just go.
863  if (_in_eval) {
864  // scm_from_utf8_string is lots faster than scm_from_locale_string
865  SCM expr_str = scm_from_utf8_string(expr.c_str());
866  SCM rc = do_scm_eval(expr_str, recast_scm_eval_string);
867  return SchemeSmob::scm_to_handle(rc);
868  }
869 
870 #ifdef WORK_AROUND_GUILE_THREADING_BUG
871  thread_lock();
872 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
873 
874  pexpr = &expr;
875  _in_eval = true;
876  scm_with_guile(c_wrap_eval_h, this);
877  _in_eval = false;
878 
879 #ifdef WORK_AROUND_GUILE_THREADING_BUG
880  thread_unlock();
881 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
882 
883  // Convert evaluation errors into C++ exceptions.
884  if (eval_error())
885  throw RuntimeException(TRACE_INFO, error_msg.c_str());
886 
887  return hargs;
888 }
889 
891 {
892  SchemeEval *self = (SchemeEval *) p;
893  // scm_from_utf8_string is lots faster than scm_from_locale_string
894  SCM expr_str = scm_from_utf8_string(self->pexpr->c_str());
895  SCM rc = self->do_scm_eval(expr_str, recast_scm_eval_string);
896  self->hargs = SchemeSmob::scm_to_handle(rc);
897  return self;
898 }
899 
905 TruthValuePtr SchemeEval::eval_tv(const std::string &expr)
906 {
907  // If we are recursing, then we already are in the guile
908  // environment, and don't need to do any additional setup.
909  // Just go.
910  if (_in_eval) {
911  // scm_from_utf8_string is lots faster than scm_from_locale_string
912  SCM expr_str = scm_from_utf8_string(expr.c_str());
913  SCM rc = do_scm_eval(expr_str, recast_scm_eval_string);
914 
915  // Pass evaluation errors out of the wrapper.
916  if (eval_error()) return TruthValue::NULL_TV();
917  return SchemeSmob::to_tv(rc);
918  }
919 
920 #ifdef WORK_AROUND_GUILE_THREADING_BUG
921  thread_lock();
922 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
923 
924  pexpr = &expr;
925  _in_eval = true;
926  scm_with_guile(c_wrap_eval_tv, this);
927  _in_eval = false;
928 
929 #ifdef WORK_AROUND_GUILE_THREADING_BUG
930  thread_unlock();
931 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
932 
933  // Convert evaluation errors into C++ exceptions.
934  if (eval_error())
935  throw RuntimeException(TRACE_INFO, error_msg.c_str());
936 
937  return tvp;
938 }
939 
941 {
942  SchemeEval *self = (SchemeEval *) p;
943  // scm_from_utf8_string is lots faster than scm_from_locale_string
944  SCM expr_str = scm_from_utf8_string(self->pexpr->c_str());
945  SCM rc = self->do_scm_eval(expr_str, recast_scm_eval_string);
946 
947  // Pass evaluation errors out of the wrapper.
948  if (self->eval_error()) return self;
949 
950  self->tvp = SchemeSmob::to_tv(rc);
951  return self;
952 }
953 
954 /* ============================================================== */
963 Handle SchemeEval::apply(const std::string &func, Handle varargs)
964 {
965  // If we are recursing, then we already are in the guile
966  // environment, and don't need to do any additional setup.
967  // Just go.
968  if (_in_eval) {
969  return do_apply(func, varargs);
970  }
971 
972 #ifdef WORK_AROUND_GUILE_THREADING_BUG
973  thread_lock();
974 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
975 
976  pexpr = &func;
977  hargs = varargs;
978  _in_eval = true;
979  scm_with_guile(c_wrap_apply, this);
980  _in_eval = false;
981 
982 #ifdef WORK_AROUND_GUILE_THREADING_BUG
983  thread_unlock();
984 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
985  if (eval_error())
986  throw RuntimeException(TRACE_INFO, error_msg.c_str());
987 
988  return hargs;
989 }
990 
991 void * SchemeEval::c_wrap_apply(void * p)
992 {
993  SchemeEval *self = (SchemeEval *) p;
994  self->hargs = self->do_apply(*self->pexpr, self->hargs);
995  return self;
996 }
997 
1005 Handle SchemeEval::do_apply(const std::string &func, Handle& varargs)
1006 {
1007  // Apply the function to the args
1008  SCM sresult = do_apply_scm (func, varargs);
1009 
1010  // If the result is a handle, return the handle.
1011  return SchemeSmob::scm_to_handle(sresult);
1012 }
1013 
1014 static SCM thunk_scm_eval(void * expr)
1015 {
1016  return scm_eval((SCM)expr, scm_interaction_environment());
1017 }
1018 
1025 SCM SchemeEval::do_apply_scm(const std::string& func, Handle& varargs )
1026 {
1027  SCM sfunc = scm_from_utf8_symbol(func.c_str());
1028  SCM expr = SCM_EOL;
1029 
1030  // If there were args, pass the args to the function.
1031  const std::vector<Handle> &oset = atomspace->get_outgoing(varargs);
1032 
1033  // Iterate in reverse, because cons chains in reverse.
1034  size_t sz = oset.size();
1035  for (int i=sz-1; i>=0; i--)
1036  {
1037  SCM sh = SchemeSmob::handle_to_scm(oset[i]);
1038  expr = scm_cons(sh, expr);
1039  }
1040  expr = scm_cons(sfunc, expr);
1041  return do_scm_eval(expr, thunk_scm_eval);
1042 }
1043 
1044 /* ============================================================== */
1053 TruthValuePtr SchemeEval::apply_tv(const std::string &func, Handle varargs)
1054 {
1055  // If we are recursing, then we already are in the guile
1056  // environment, and don't need to do any additional setup.
1057  // Just go.
1058  if (_in_eval) {
1059  SCM tv_smob = do_apply_scm(func, varargs);
1060  if (eval_error())
1061  return TruthValue::NULL_TV();
1062  return SchemeSmob::to_tv(tv_smob);
1063  }
1064 
1065 #ifdef WORK_AROUND_GUILE_THREADING_BUG
1066  thread_lock();
1067 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
1068 
1069  pexpr = &func;
1070  hargs = varargs;
1071  _in_eval = true;
1072  scm_with_guile(c_wrap_apply_tv, this);
1073  _in_eval = false;
1074 
1075 #ifdef WORK_AROUND_GUILE_THREADING_BUG
1076  thread_unlock();
1077 #endif /* WORK_AROUND_GUILE_THREADING_BUG */
1078  if (eval_error())
1079  throw RuntimeException(TRACE_INFO, error_msg.c_str());
1080 
1081  // We do not want this->tvp to point at anything after we return.
1082  // This is so that we do not hold a long-term reference to the TV.
1083  TruthValuePtr rtv;
1084  swap(rtv, tvp);
1085  return rtv;
1086 }
1087 
1089 {
1090  SchemeEval *self = (SchemeEval *) p;
1091  SCM tv_smob = self->do_apply_scm(*self->pexpr, self->hargs);
1092  if (self->eval_error()) return self;
1093  self->tvp = SchemeSmob::to_tv(tv_smob);
1094  return self;
1095 }
1096 
1097 /* ============================================================== */
1098 
1099 // A pool of scheme evaluators, sitting hot and ready to go.
1100 // This is used to implement get_evaluator(), below. The only
1101 // reason this is done with a pool, instead of simply new() and
1102 // delete() is because calling delete() from TLS conflicts with
1103 // the guile garbage collector, when the thread is destroyed. See
1104 // the note below.
1105 static concurrent_stack<SchemeEval*> pool;
1106 static std::mutex pool_mtx;
1107 
1109 {
1110  std::lock_guard<std::mutex> lock(pool_mtx);
1111  SchemeEval* ev = NULL;
1112  if (pool.try_pop(ev)) return ev;
1113  return new SchemeEval();
1114 }
1115 
1116 static void return_to_pool(SchemeEval* ev)
1117 {
1118  std::lock_guard<std::mutex> lock(pool_mtx);
1119  pool.push(ev);
1120 }
1121 
1136 {
1137  static thread_local std::map<AtomSpace*,SchemeEval*> issued;
1138 
1139  // The eval_dtor runs when this thread is destroyed.
1140  class eval_dtor {
1141  public:
1142  ~eval_dtor() {
1143  for (auto ev : issued)
1144  {
1145  SchemeEval* evaluator = ev.second;
1146 
1147  // It would have been easier to just call delete evaluator
1148  // instead of return_to_pool. Unfortunately, the delete
1149  // won't work, because the STL destructor has already run
1150  // the guile GC at this point, for this thread, and so
1151  // calling delete will lead to a crash in c_wrap_finish().
1152  // It would be nice if we got called before guile did, but
1153  // there is no way in TLS to control execution order...
1154  evaluator->atomspace = NULL;
1155  return_to_pool(evaluator);
1156  }
1157  }
1158  };
1159  static thread_local eval_dtor killer;
1160 
1161  auto ev = issued.find(as);
1162  if (ev != issued.end())
1163  return ev->second;
1164 
1165  SchemeEval* evaluator = get_from_pool();
1166  evaluator->atomspace = as;
1167  issued[as] = evaluator;
1168  return evaluator;
1169 
1170 #if 0
1171  if (evaluator->recursing())
1172  throw RuntimeException(TRACE_INFO,
1173  "Evaluator thread singleton used recursively!");
1174 #endif
1175 
1176 }
1177 
1178 /* ============================================================== */
1179 
1181 {
1182  AtomSpace* as = (AtomSpace*) vas;
1184  return vas;
1185 }
1186 
1193 {
1194  scm_with_guile(c_wrap_set_atomspace, as);
1195 }
1196 
1198 {
1199  // XXX FIXME only a subset is needed.
1200  SchemeEval sch;
1201 }
1202 
1203 #endif
1204 
1205 /* ===================== END OF FILE ============================ */
void eval_expr(const std::string &)
Definition: SchemeEval.cc:490
static void * c_wrap_apply_tv(void *)
Definition: SchemeEval.cc:1088
static pthread_key_t ser_key
Definition: SchemeEval.cc:255
static pthread_mutex_t serialize_lock
Definition: SchemeEval.cc:254
static concurrent_stack< SchemeEval * > pool
Definition: SchemeEval.cc:1105
Handle apply(const std::string &func, Handle varargs)
Definition: SchemeEval.cc:963
static std::mutex pool_mtx
Definition: SchemeEval.cc:1106
SCM recast_scm_eval_string(void *expr)
Definition: SchemeEval.cc:848
std::condition_variable _wait_done
Definition: SchemeEval.h:88
std::string error_msg
Definition: SchemeEval.h:119
void drain_output()
Discard all chars in the outport.
Definition: SchemeEval.cc:144
TruthValuePtr tvp
Definition: SchemeEval.h:113
std::shared_ptr< TruthValue > TruthValuePtr
Definition: TruthValue.h:85
static SCM thunk_scm_eval(void *expr)
Definition: SchemeEval.cc:1014
static scm_t_bits cog_misc_tag
Definition: SchemeSmob.h:59
static void init_scheme(void)
Definition: SchemeEval.cc:1197
static void * c_wrap_eval_tv(void *)
Definition: SchemeEval.cc:940
std::string poll_result()
Definition: SchemeEval.cc:523
static SCM guile_user_module
Definition: SchemeEval.cc:220
static void * c_wrap_poll(void *)
Definition: SchemeEval.cc:516
const std::string * pexpr
Definition: SchemeEval.h:82
std::mutex init_mtx
Definition: SchemeEval.cc:35
virtual bool eval_error(void)
Definition: GenericEval.h:75
SCM do_apply_scm(const std::string &func, Handle &varargs)
Definition: SchemeEval.cc:1025
static void ss_set_env_as(AtomSpace *)
static void init()
Definition: SchemeSmob.cc:43
static SchemeEval * get_evaluator(AtomSpace *=NULL)
Definition: SchemeEval.cc:1135
Handle eval_h(const std::string &)
Definition: SchemeEval.cc:858
static void * c_wrap_eval(void *)
Definition: SchemeEval.cc:529
static TruthValuePtr NULL_TV()
Definition: TruthValue.cc:46
SCM preunwind_handler(SCM, SCM)
Definition: SchemeEval.cc:383
std::string do_poll_result()
Definition: SchemeEval.cc:675
static Handle scm_to_handle(SCM)
SCM catch_handler(SCM, SCM)
Definition: SchemeEval.cc:392
SchemeEval(AtomSpace *=NULL)
Definition: SchemeEval.cc:304
void set_captured_stack(SCM)
Definition: SchemeEval.cc:189
static void * c_wrap_finish(void *)
Definition: SchemeEval.cc:180
#define BUFSZ
static SCM preunwind_handler_wrapper(void *, SCM, SCM)
Definition: SchemeEval.cc:371
std::string _input_line
Definition: GenericEval.h:42
static void return_to_pool(SchemeEval *ev)
Definition: SchemeEval.cc:1116
void per_thread_init(void)
Definition: SchemeEval.cc:322
static void * c_wrap_set_atomspace(void *)
Definition: SchemeEval.cc:1180
TruthValuePtr apply_tv(const std::string &func, Handle varargs)
Definition: SchemeEval.cc:1053
static void * c_wrap_apply(void *)
Definition: SchemeEval.cc:991
static std::atomic_flag eval_is_inited
Definition: SchemeEval.cc:204
void thread_lock(void)
Definition: SchemeEval.cc:281
std::string poll_port()
Definition: SchemeEval.cc:638
static void set_scheme_as(AtomSpace *)
Definition: SchemeEval.cc:1192
std::mutex _poll_mtx
Definition: SchemeEval.h:87
static void * c_wrap_eval_h(void *)
Definition: SchemeEval.cc:890
AtomSpace * atomspace
Definition: SchemeEval.h:132
void set_error_string(SCM)
Definition: SchemeEval.cc:197
static SCM handle_to_scm(Handle)
static thread_local bool thread_is_inited
Definition: SchemeEval.cc:205
std::string answer
Definition: SchemeEval.h:83
static SchemeEval * get_from_pool(void)
Definition: SchemeEval.cc:1108
static TruthValuePtr to_tv(SCM)
void do_eval(const std::string &)
Definition: SchemeEval.cc:583
static AtomSpace * ss_get_env_as(const char *)
static void do_gc(void)
Definition: SchemeEval.cc:551
SCM do_scm_eval(SCM, SCM(*)(void *))
Definition: SchemeEval.cc:764
static SCM catch_handler_wrapper(void *, SCM, SCM)
Definition: SchemeEval.cc:377
static std::string prt(SCM)
Definition: SchemeEval.cc:343
static std::string misc_to_string(SCM)
static void * c_wrap_init(void *)
Definition: SchemeEval.cc:150
static void init_only_once(void)
Definition: SchemeEval.cc:260
const HandleSeq & get_outgoing(Handle h) const
Definition: AtomSpace.h:735
TruthValuePtr eval_tv(const std::string &)
Definition: SchemeEval.cc:905
void thread_unlock(void)
Definition: SchemeEval.cc:292
static void * do_bogus_scm(void *p)
Definition: SchemeEval.cc:222
Handle do_apply(const std::string &func, Handle &varargs)
Definition: SchemeEval.cc:1005