Sleep is interrupted by signals other then message reception
authorArjen Baart <arjen@andromeda.nl>
Sat, 25 Jul 2020 11:59:34 +0000 (13:59 +0200)
committerArjen Baart <arjen@andromeda.nl>
Sat, 25 Jul 2020 11:59:34 +0000 (13:59 +0200)
doc/design.xml
src/Tachyon.cpp
src/Tachyon.h
test/Makefile.am
test/accellerate_signal [new file with mode: 0755]
test/sleep_signal.cpp [new file with mode: 0644]

index eb12e28..385350f 100644 (file)
@@ -120,10 +120,36 @@ The nanosleep method calculates the virtual time at which the sleep ends and cal
 the actual sleep by suspending the process.
 Interruptions of the sleep are handled by recalculating the sleep period from the possibly changed parameters
 and resuming the sleep if needed.
+The nanosleep(2) system call is used to perform the actual sleep.
+This system call may return for either one of four reasons.
+
+<enumerate>
+<item>
+The sleep period ended normally. Return value = 0.
+</item>
+
+<item>
+The sleep is interrupted by the reception of a message. Return value = -1 and errno = EINTR.
+Internally, message notification is handled by the signal SIGUSR1.
+The virtual time is recalculated and the sleep resumes if the virtual end time has not passed yet.
+</item>
+
+<item>
+The sleep is interrupted by a signal which is handled by a signal handler. Return value = -1 and errno = EINTR.
+This is not caused by receiving a message.
+The sleep in virtual time is interrupted and control is returned to the calling process.
+</item>
+
+<item>
+An error occured. Return value = -1 and errno = EFAULT or errnno = EINVAL.
+</item>
+</enumerate>
+
 </para>
 
 <para>
-To control the virtual time from an external process, a form of inter process communication (IPC) is used. The external process, usually a test program, can find the Tachyon object to control through a name which is assigned for that Tachyon object. Two or more Tachyon objects may exist, one of which holds the virtual time used by the time dependent software. The other Tachyon object(s) are used to control the virtual timebase and send the timebase parameters through a form of IPC.
+To control the virtual time from an external process, a form of inter process communication (IPC) is used.
+The external process, usually a test program, can find the Tachyon object to control through a name which is assigned for that Tachyon object. Two or more Tachyon objects may exist, one of which holds the virtual time used by the time dependent software. The other Tachyon object(s) are used to control the virtual timebase and send the timebase parameters through a form of IPC.
 </para>
 
 </chapter>
@@ -174,8 +200,15 @@ The time is specified with nanosecond precision in the <code>struct timespec</co
 </item>
 
 <item tag='int nanosleep(float req)'>
-Suspend execution until the time in req is passed. That time is divided by the accelleration set in the Tachyon object. So, if a waiting time of 60 seconds is requested and the accelleration is 10, execution will be actually be suspended for 6 seconds.
-The time is specified with nanosecond precision in a floating point number.
+Suspend execution until the the number of seconds in req is passed.
+That time is divided by the accelleration set in the Tachyon object.
+So, if a waiting time of 60 seconds is requested and the accelleration is 10, execution will be actually be suspended for 6 seconds.
+The time in seconds is specified with nanosecond precision in a floating point number.
+</item>
+
+<item tag='int sleep_until(timespec end_time)'>
+Suspend execution until the virtual time in end_time has passed.
+If, during the sleep, the accelleration or the offset is changed, these changes are taken into account.
 </item>
 
 <item tag='settime (time_t sec)'>
index b9adaa4..bd50f66 100644 (file)
@@ -90,6 +90,7 @@ bool operator < (timespec a, timespec b)
 static void message_notification(int signum, siginfo_t *si, void *notused)
 {
    Tachyon *tp = (Tachyon *) si->si_value.sival_ptr;
+   //printf("Tachyon: Caught signal %d\n", signum);
    tp->receive_message();
    tp->expect_message();
 }
@@ -102,6 +103,8 @@ Tachyon::Tachyon()
 
    accelleration = 1.0;
 
+   msg_sequence  = 0;
+
    struct mq_attr  queue_attributes;
    struct sigevent sev;
    struct sigaction act;
@@ -124,6 +127,8 @@ Tachyon::Tachyon(const char *name)
 
    accelleration = 1.0;
 
+   msg_sequence  = 0;
+
    queue_name[0] = '\0';
    msg_queue = mq_open(name, O_WRONLY);
 }
@@ -168,7 +173,11 @@ void Tachyon::receive_message(void)
    int  message_size;
 
    //printf("Receiving message.\n");
+
    message_size = mq_receive(msg_queue, message, MAX_MESSAGE_SIZE, NULL);
+   msg_sequence++;
+
+   //printf("Message size = %d\n", message_size);
    message[message_size] = '\0';
 
    //printf("The message is %s.\n", message);
@@ -205,7 +214,7 @@ timespec Tachyon::gettime(void)
 
    //  Tvirtual = T0 + acc * (Tactual - T0) + offset
 
-   difference = timespec_subtract(actual_time, T0);
+   difference = actual_time - T0;
    //printf("  Ta - T0 = %d,%d\n", difference.tv_sec, difference.tv_nsec);
 
    seconds     = difference.tv_sec  * accelleration;
@@ -218,8 +227,7 @@ timespec Tachyon::gettime(void)
    difference.tv_nsec = trunc(nanoseconds);
    //printf("  a * (Ta - T0) = %d,%d\n", difference.tv_sec, difference.tv_nsec);
 
-   virtual_time   = timespec_add(T0, difference);
-   virtual_time   = timespec_add(virtual_time, offset);
+   virtual_time   = T0 + difference + offset;
  
    return virtual_time;
 }
@@ -234,7 +242,7 @@ int Tachyon::nanosleep(struct timespec req)
 
    virt_time = gettime();
 
-   return sleep_until(timespec_add(virt_time, req));
+   return sleep_until(virt_time + req);
 }
 
 int Tachyon::nanosleep(float req)
@@ -262,13 +270,16 @@ int Tachyon::sleep_until(timespec ends_at)
    double seconds;
    double nanoseconds;
    int    sleep_return;
-   bool   error;
+   int    last_msg_sequence;
 
    timespec  virt_time;
    timespec  remaining;
 
+   last_msg_sequence = msg_sequence;
+   sleep_return      = 0;
+
    virt_time = gettime();
-   while (!error && virt_time < ends_at)
+   while (sleep_return == 0 && virt_time < ends_at)
    {
       timespec period;
 
@@ -287,7 +298,20 @@ int Tachyon::sleep_until(timespec ends_at)
       sleep_return = ::nanosleep(&period, &remaining);
       //printf("nanosleep returns %d, error = %s\n", sleep_return, strerror(errno));
 
-      error = sleep_return == -1 && errno != EINTR;
+      // If a sleep interruption is caused by receiving a message, the sleep is resumed.
+      if (sleep_return == -1 && errno == EINTR)
+      {
+         if (msg_sequence != last_msg_sequence)
+         {
+            sleep_return = 0;
+            last_msg_sequence = msg_sequence;
+         }
+         else
+         {
+            sleep_return = -2;   //  Interrupted by a different signal
+         }
+      }
+
       virt_time = gettime();
    }
 
index c073506..93f65f6 100644 (file)
@@ -9,6 +9,7 @@ class Tachyon
 {
    char      queue_name[255];
    mqd_t     msg_queue;
+   int       msg_sequence;
 
    timespec  offset;
    timespec  T0;
index 6801cf9..0a8505a 100644 (file)
@@ -1,15 +1,16 @@
-TESTS = create timespec sleep accellerate accellerate_multi sleep_float accellerate_float
+TESTS = create timespec sleep accellerate accellerate_multi sleep_float accellerate_float accellerate_signal
 
 AM_CPPFLAGS = -I../src
 LDADD = ../src/.libs/libTachyon.la -lrt -lm
 
-check_PROGRAMS = create timespec sleep sleep_float
+check_PROGRAMS = create timespec sleep sleep_float sleep_signal
 
 
-create_SOURCES       = create.cpp
-timespec_SOURCES     = timespec.cpp
-sleep_SOURCES        = sleep.cpp
-sleep_float_SOURCES  = sleep_float.cpp
+create_SOURCES        = create.cpp
+timespec_SOURCES      = timespec.cpp
+sleep_SOURCES         = sleep.cpp
+sleep_float_SOURCES   = sleep_float.cpp
+sleep_signal_SOURCES  = sleep_signal.cpp
 
 clean-local:
        rm -f sleep.tmp
diff --git a/test/accellerate_signal b/test/accellerate_signal
new file mode 100755 (executable)
index 0000000..bb091b6
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+set -m
+
+PATH=../src:$PATH
+
+START_TIME=`date +%s`
+./sleep_signal >sleep.tmp &
+sleep 1
+read a b PID <sleep.tmp
+read a b c TACHYON_NAME <sleep.tmp
+kill -1 $PID
+sleep 1
+kill -1 $PID
+sleep 1
+kill -1 $PID
+fg
+END_TIME=`date +%s`
+duration=$(($END_TIME - $START_TIME))
+echo "Elapsed time is $duration"
+cat sleep.tmp
+
+if [[ $duration -eq 9 ]] || [[ $duration -eq 10 ]]
+then
+   echo "Elapsed time within 10 seconds"
+   exit 0
+fi
+echo "Elapsed time $duration seconds is unexpected."
+exit 1
diff --git a/test/sleep_signal.cpp b/test/sleep_signal.cpp
new file mode 100644 (file)
index 0000000..1643178
--- /dev/null
@@ -0,0 +1,40 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <iostream>
+#include "Tachyon.h"
+
+/*
+ *  Test the sleep function with signal handling
+ *  By default, sleep 4 times 6 seconds.
+ *  The sleep can be interrupted by a HUP signal, which is caught by a signal handler.
+ */
+
+
+// Signal handler for SIGHUP
+
+void sighup_handler(int sig)
+{
+   std::cout << "Caught signal " << sig << "\n";
+}
+
+int main()
+{
+
+   Tachyon timebase;
+   float interval;
+
+   interval = 6.0;
+
+   signal(SIGHUP, sighup_handler);
+
+   std::cout << "PID = " << getpid() << "\n";
+   std::cout << "Tachyon name is " << timebase.name() << "\n";
+   std::cout.flush();
+   for (int i = 0; i < 4; i++)
+   {
+      timebase.nanosleep(interval);
+      std::cout << "6 seconds.\n";
+   }
+}