Browse Source

Fixed output redirection regression (and improved it).

Johny Mattsson 8 years ago
parent
commit
af9df2ae98
2 changed files with 97 additions and 37 deletions
  1. 89 34
      app/modules/node.c
  2. 8 3
      docs/en/modules/node.md

+ 89 - 34
app/modules/node.c

@@ -27,10 +27,19 @@
 #include "user_version.h"
 #include "rom.h"
 #include "task/task.h"
+#include <freertos/FreeRTOS.h>
+#include <freertos/queue.h>
 
 #define CPU80MHZ 80
 #define CPU160MHZ 160
 
+#define REDIRECT_BUFFER_SZ 128
+
+static xQueueHandle redirQ;
+static task_handle_t redir_task;
+static int output_redir_ref = LUA_NOREF;
+static int serial_output = 1;
+
 // Lua: restart()
 static int node_restart( lua_State* L )
 {
@@ -324,55 +333,101 @@ static int node_input( lua_State* L )
   return 0;
 }
 
-static int output_redir_ref = LUA_NOREF;
-static int serial_debug = 1;
-void output_redirect(const char *str) {
+void output_redirect(task_param_t arg, task_prio_t prio)
+{
+  (void)arg;
+  (void)prio;
+
   lua_State *L = lua_getstate();
-  // if(strlen(str)>=TX_BUFF_SIZE){
-  //   NODE_ERR("output too long.\n");
-  //   return;
-  // }
 
-  if (output_redir_ref == LUA_NOREF || !L) {
-    uart0_sendStr(str);
-    return;
-  }
+  luaL_Buffer buf;
 
-  if (serial_debug != 0) {
-    uart0_sendStr(str);
+  char *p;
+  size_t total = 0;
+  unsigned qd = uxQueueMessagesWaitingFromISR (redirQ);
+  while (qd)
+  {
+    if (!total) /* load function onto stack and prepare a new buffer */
+    {
+      lua_rawgeti(L, LUA_REGISTRYINDEX, output_redir_ref);
+      luaL_buffinit (L, &buf);
+      p = luaL_prepbuffer (&buf);
+    }
+
+    xQueueReceive (redirQ, p++, portMAX_DELAY);
+    ++total;
+    if (--qd == 0) /* Has anything been appended since we started? */
+      qd = uxQueueMessagesWaitingFromISR (redirQ);
+
+    /* Invoke callback either when we're out of characters, or when we've
+     * filled up the available buffer space. */
+    if (!qd || total == LUAL_BUFFERSIZE)
+    {
+      luaL_addsize (&buf, total);
+      luaL_pushresult (&buf);
+      lua_call(L, 1, 0);
+      total = 0;
+    }
   }
+}
+
+
+/* This function may be invoked from any context, so we use xxxFromISR
+ * functions for all queue handling. */
+static void redir_putc (char c)
+{
+  if (serial_output)
+    uart0_putc (c);
 
-  lua_rawgeti(L, LUA_REGISTRYINDEX, output_redir_ref);
-  lua_pushstring(L, str);
-  lua_call(L, 1, 0);   // this call back function should never user output.
+  if (redirQ)
+  {
+    bool wasEmpty = xQueueIsQueueEmptyFromISR (redirQ);
+    /* If we get pre-empted and re-entered here we'll end up posting two copies
+     * to our task, which is mostly harmless (just some wasted cycles) */
+    xQueueSendToBackFromISR(redirQ, &c, NULL);
+    if (wasEmpty)
+      task_post_high (redir_task, 0); /* Limited buffer, service often! */
+  }
 }
 
+
 // Lua: output(function(c), debug)
 static int node_output( lua_State* L )
 {
-  // luaL_checkanyfunction(L, 1);
-  if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION) {
-    lua_pushvalue(L, 1);  // copy argument (func) to the top of stack
-    if (output_redir_ref != LUA_NOREF)
-      luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref);
+  luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref);
+  output_redir_ref = LUA_NOREF;
+
+  unsigned len = REDIRECT_BUFFER_SZ;
+  if (lua_isnumber (L, 3))
+    len = lua_tonumber (L, 3);
+
+  if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION)
+  {
+    lua_pushvalue(L, 1);
     output_redir_ref = luaL_ref(L, LUA_REGISTRYINDEX);
-  } else {    // unref the key press function
-    if (output_redir_ref != LUA_NOREF)
-      luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref);
-    output_redir_ref = LUA_NOREF;
-    serial_debug = 1;
-    return 0;
+    if (!redirQ)
+      redirQ = xQueueCreate (len, sizeof(char));
+    if (!redir_task)
+      redir_task = task_get_id (output_redirect);
+    os_install_putc1 (redir_putc); /* It doesn't give us back the original :( */
   }
-
-  if ( lua_isnumber(L, 2) )
+  else
   {
-    serial_debug = lua_tointeger(L, 2);
-    if (serial_debug != 0)
-      serial_debug = 1;
-  } else {
-    serial_debug = 1; // default to 1
+    if (redirQ)
+    {
+      xQueueHandle tmp = redirQ;
+      /* Dumb "locking" before free, relying on 32bit load/store atomicity */
+      redirQ = NULL;
+      vQueueDelete (tmp);
+    }
+    serial_output = 1; /* support node.output(nil, 0) to suppress all output */
   }
 
+  if ( lua_isnumber(L, 2) )
+    serial_output = lua_tointeger(L, 2) ? 1 : 0;
+  else
+    serial_output = 1; // default to 1
+
   return 0;
 }
 

+ 8 - 3
docs/en/modules/node.md

@@ -257,11 +257,11 @@ Redirects the Lua interpreter output to a callback function. Optionally also pri
     Do **not** attempt to `print()` or otherwise induce the Lua interpreter to produce output from within the callback function. Doing so results in infinite recursion, and leads to a watchdog-triggered restart.
 
 #### Syntax
-`node.output(function(str), serial_debug)`
+`node.output(function(str), serial_output)`
 
 #### Parameters
-  - `output_fn(str)` a function accept every output as str, and can send the output to a socket (or maybe a file).
-  - `serial_debug` 1 output also show in serial. 0: no serial output.
+  - `output_fn(str)` a function accept every output as str, and can send the output to a socket (or maybe a file). `nil` to unregister the previous function.
+  - `serial_output` 1 output also sent out the serial port. 0: no serial output. Defaults to 1 if not specified.
 
 #### Returns
 `nil`
@@ -294,6 +294,11 @@ s:listen(2323,function(c)
    end)
 end)
 ```
+
+```lua
+-- disable all output completely
+node.output(nil, 0)
+```
 #### See also
 [`node.input()`](#nodeinput)