acassis commented on code in PR #3682:
URL: https://github.com/apache/nuttx-apps/pull/3682#discussion_r3690611305


##########
examples/fdpicxip/modules/callback.c:
##########
@@ -0,0 +1,517 @@
+/****************************************************************************
+ * apps/examples/fdpicxip/modules/callback.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* Hands a module function to the firmware every way the firmware will
+ * accept one.
+ *
+ * In FDPIC a function pointer is the address of a two-word descriptor, not
+ * a code address, and that descriptor lives in the module's writable
+ * segment in RAM.  Firmware that stores such a pointer and later branches to
+ * it therefore jumps into the module's *data* and faults -- unless the entry
+ * point it came through resolved the descriptor first.
+ *
+ * qsort and bsearch have always done that.  pthread_create, signal and
+ * task_create did not, which made them look usable and then take the board
+ * down; pthread_once, task_spawn, scandir's filter and sigaction followed
+ * later.  This module is what keeps all of them honest.
+ *
+ * scandir is one worth noting: its filter is called by scandir itself and
+ * must be resolved there, while its comparison function is handed to qsort,
+ * which resolves it.  Resolving both would be a bug, so both are passed here
+ * at once.
+ *
+ * mq_notify and timer_create with SIGEV_THREAD are the interesting ones:
+ * their callback runs on a work-queue worker that carries no module data
+ * base.  The base is captured at registration and installed around the call,
+ * so the callback still reaches the module's own globals.  Each is checked
+ * by having the callback write a distinctive value into a module global.
+ *
+ * Everything is checked by observing a side effect the callback itself
+ * produces, because a callback that never runs is the failure being tested
+ * for -- and on this target the alternative failure is a HardFault, which
+ * reports nothing at all.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <spawn.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define FAIL_QSORT    0x01
+#define FAIL_PTHREAD  0x02
+#define FAIL_SIGNAL   0x04
+#define FAIL_TASK     0x08
+#define FAIL_ONCE     0x10
+#define FAIL_SPAWN    0x20
+#define FAIL_SCANDIR  0x40
+#define FAIL_SIGACTION 0x80
+
+/* The mq checks run under "callback mq" and report in their own byte, so the
+ * exit status -- only eight bits survive waitpid -- never has to hold more
+ * than eight flags at once.
+ */
+
+#define FAIL_MQ_THREAD    0x01
+#define FAIL_MQ_SIGNAL    0x02
+#define FAIL_TIMER_THREAD 0x04
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* Written by the callbacks, read by main.  These live in the module's
+ * writable segment, which is what the callback can only reach if it was
+ * entered with this module's data base in the FDPIC register.
+ */
+
+static volatile int g_qsort_calls;
+static volatile int g_thread_ran;
+static volatile int g_signal_ran;
+static volatile int g_task_ran;
+static volatile int g_once_ran;
+static volatile int g_spawn_ran;
+static volatile int g_filter_calls;
+static volatile int g_compar_calls;
+static volatile int g_sigaction_ran;
+static volatile int g_mq_signal_ran;
+static volatile int g_mq_thread_ran;
+static volatile int g_timer_thread_ran;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int cmp_int(const void *a, const void *b)
+{
+  g_qsort_calls++;
+  return *(const int *)a - *(const int *)b;
+}
+
+static void *thread_main(void *arg)
+{
+  g_thread_ran = (int)(intptr_t)arg;
+  return NULL;
+}
+
+static void sig_handler(int signo)
+{
+  g_signal_ran = signo;
+}
+
+static void sigaction_handler(int signo, siginfo_t *info, void *context)
+{
+  g_sigaction_ran = signo;
+}
+
+static void mq_signal_handler(int signo, siginfo_t *info, void *context)
+{
+  g_mq_signal_ran = signo;
+}
+
+static void mq_thread_notify(union sigval value)
+{
+  /* Runs on a work-queue worker, which carries no module data base of its
+   * own.  Writing this module global proves the base was installed around
+   * the call -- without it this store would fault or land nowhere.
+   */
+
+  g_mq_thread_ran = value.sival_int;
+}
+
+static void timer_thread_notify(union sigval value)
+{
+  g_timer_thread_ran = value.sival_int;
+}
+
+static int task_main(int argc, char *argv[])
+{
+  g_task_ran = 1;
+  return 0;
+}
+
+static void once_routine(void)
+{
+  g_once_ran++;
+}
+
+static int spawn_main(int argc, char *argv[])
+{
+  g_spawn_ran = 1;
+  return 0;
+}
+
+static int dir_filter(const struct dirent *entry)
+{
+  g_filter_calls++;
+  return 1;
+}
+
+static int dir_compar(const struct dirent **a, const struct dirent **b)
+{
+  g_compar_calls++;
+  return strcmp((*a)->d_name, (*b)->d_name);
+}
+
+/* The message-queue checks run as a second invocation, "callback mq", so
+ * their two flags report in a byte of their own rather than overflowing the
+ * eight-bit exit status the other checks already fill.
+ */
+
+static int run_mq(void)
+{
+  struct mq_attr attr;
+  struct sigaction sa;
+  struct sigevent ev;
+  struct itimerspec its;
+  timer_t timer;
+  mqd_t mqd;
+  int fails = 0;
+  int i;
+
+  attr.mq_maxmsg = 1;
+  attr.mq_msgsize = 1;
+  attr.mq_flags = 0;
+
+  mqd = mq_open("/cbmq", O_CREAT | O_RDWR, 0644, &attr);
+  if (mqd == (mqd_t)-1)
+    {
+      return FAIL_MQ_THREAD | FAIL_MQ_SIGNAL | FAIL_TIMER_THREAD;
+    }
+
+  /* mq_notify with SIGEV_THREAD.  The callback runs on a work-queue worker
+   * that carries no module data base of its own; the base captured at
+   * registration is installed around the call, so the callback reaches this
+   * module's globals.  A distinctive sival marks that the right callback
+   * ran with the right argument.
+   */
+
+  memset(&ev, 0, sizeof(ev));
+  ev.sigev_notify = SIGEV_THREAD;
+  ev.sigev_notify_function = mq_thread_notify;
+  ev.sigev_value.sival_int = 0x5a;
+
+  if (mq_notify(mqd, &ev) < 0)
+    {
+      fails |= FAIL_MQ_THREAD;
+    }
+  else
+    {
+      char buf[1];
+
+      /* The notification fires on a send to an empty queue; drain the
+       * message afterwards so the queue is empty for the next test and the
+       * next send does not block on a full queue.
+       */
+
+      mq_send(mqd, "x", 1, 0);
+
+      for (i = 0; i < 50 && g_mq_thread_ran == 0; i++)
+        {
+          usleep(10000);
+        }
+
+      mq_receive(mqd, buf, sizeof(buf), NULL);
+
+      if (g_mq_thread_ran != 0x5a)
+        {
+          fails |= FAIL_MQ_THREAD;
+        }
+    }
+
+  /* mq_notify with SIGEV_SIGNAL, delivered to this task and caught by a
+   * handler installed through sigaction.
+   */
+
+  memset(&sa, 0, sizeof(sa));
+  sa.sa_sigaction = mq_signal_handler;
+  sa.sa_flags = SA_SIGINFO;
+  sigaction(SIGUSR1, &sa, NULL);
+
+  memset(&ev, 0, sizeof(ev));
+  ev.sigev_notify = SIGEV_SIGNAL;
+  ev.sigev_signo = SIGUSR1;
+
+  if (mq_notify(mqd, &ev) < 0)
+    {
+      fails |= FAIL_MQ_SIGNAL;
+    }
+  else
+    {
+      char buf[1];
+
+      mq_send(mqd, "x", 1, 0);
+
+      for (i = 0; i < 50 && g_mq_signal_ran == 0; i++)
+        {
+          usleep(10000);
+        }
+
+      mq_receive(mqd, buf, sizeof(buf), NULL);
+
+      if (g_mq_signal_ran != SIGUSR1)
+        {
+          fails |= FAIL_MQ_SIGNAL;
+        }
+    }
+
+  mq_close(mqd);
+  mq_unlink("/cbmq");
+
+  /* timer_create with SIGEV_THREAD reaches the module the same way: the
+   * expiry callback runs on the worker with the module's base installed.
+   */
+
+  memset(&ev, 0, sizeof(ev));
+  ev.sigev_notify = SIGEV_THREAD;
+  ev.sigev_notify_function = timer_thread_notify;
+  ev.sigev_value.sival_int = 0xa5;
+
+  if (timer_create(CLOCK_MONOTONIC, &ev, &timer) < 0)
+    {
+      fails |= FAIL_TIMER_THREAD;
+    }
+  else
+    {
+      its.it_value.tv_sec = 0;
+      its.it_value.tv_nsec = 20 * 1000 * 1000;
+      its.it_interval.tv_sec = 0;
+      its.it_interval.tv_nsec = 0;
+      timer_settime(timer, 0, &its, NULL);
+
+      for (i = 0; i < 50 && g_timer_thread_ran == 0; i++)
+        {
+          usleep(10000);
+        }
+
+      if (g_timer_thread_ran != 0xa5)
+        {
+          fails |= FAIL_TIMER_THREAD;
+        }
+
+      timer_delete(timer);
+    }
+
+  syslog(LOG_INFO,
+         "[callback] mq_thread=%#x mq_signal=%d timer_thread=%#x -- %s\n",
+         g_mq_thread_ran, g_mq_signal_ran, g_timer_thread_ran,
+         fails == 0 ? "PASS" : "FAIL");
+
+  return fails;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, char *argv[])
+{
+  int v[5] =
+  {
+    5, 3, 1, 4, 2
+  };

Review Comment:
   @casaroli why to this qsort() test in the beginning of the modules's 
callback? Seems like a unnecessary test 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to