More tuning of periods.
authorbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Thu, 13 Mar 2014 20:59:39 +0000 (20:59 +0000)
committerbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Thu, 13 Mar 2014 20:59:39 +0000 (20:59 +0000)
Add mloclkall() so that paging won't interrupt real-time performance.
Add message about the need to set
setcap cap_ipc_lock+ep

git-svn-id: https://svn.code.sf.net/p/freetel/code@1437 01035d8c-6547-0410-b346-abe4f91aad63

freedv-server/source/codec_noop.cpp
freedv-server/source/platform/linux/privilege.cpp
freedv-server/source/platform/linux/scheduler.cpp
freedv-server/source/run.cpp

index 54af458ce2439d8e32d631c47ec75a704b6b4e9c..fed732ff45fa457b0c788f5e22f1ee345e355757 100644 (file)
@@ -6,6 +6,8 @@
 namespace FreeDV {
   /// Codec "no-op", just copies its input to its output. For plain SSB voice, and for testing.
   class CodecNoOp : public Codec {
+    static const std::size_t   FrameDuration = 40;
+
   public:
 
     /// Instantiate the no-op codec.
@@ -56,7 +58,8 @@ namespace FreeDV {
   CodecNoOp::decode16(const std::uint8_t * i, std::int16_t * o, std::size_t * data_length, std::size_t sample_length)
   {
     const std::size_t length = min(*data_length / 2, sample_length);
-    if ( length < (std::size_t)(((double)SampleRate / 1000.0) * 40) ) {
+    if ( length < (std::size_t)(((double)SampleRate / 1000.0) * FrameDuration) )
+    {
       *data_length = 0;
       return 0;
     }
@@ -75,7 +78,7 @@ namespace FreeDV {
   int
   CodecNoOp::min_frame_duration() const
   {
-    return 40;
+    return FrameDuration;
   }
 
   Codec *
index 72c2acf69367d9a2808dcf4f0a77ed65063f0ebb..271e4848732fb019394f02ef9702c2beb0549406 100644 (file)
@@ -33,12 +33,13 @@ namespace FreeDV {
 
     if ( audio ) {
       gid_t    groups[NGROUPS_MAX];
-      int      length = sizeof(groups) / sizeof(*groups);
+      int      size = sizeof(groups) / sizeof(*groups);
+      int      length;
 
       if ( gid == audio->gr_gid || egid == audio->gr_gid )
         return;
 
-      if ( getgroups(length, groups) == 0 ) {
+      if ( (length = getgroups(size, groups)) > 0 ) {
         for ( int i = 0; i < length; i++ ) {
           if ( groups[i] == audio->gr_gid )
            return;
index cc1bc13aa09027ad579c06d4272b84226c01af71..cc07ba578bd49a79ae4fe5711804c948ae6114a8 100644 (file)
@@ -1,46 +1,71 @@
 #include <unistd.h>
 #ifdef _POSIX_PRIORITY_SCHEDULING
-#include <errno.h>
 #include <sched.h>
+#endif
+#ifdef _POSIX_MEMLOCK_RANGE
+#include <sys/mman.h>
+#endif
+#include <errno.h>
 #include <cstring>
 #include <iostream>
-#endif
 #include "drivers.h"
 
 static const char insufficient_privilege_message[] =
-"Warning: Insufficient privilege to set a real-time scheduling priority.\n"
+"Warning: Insufficient privilege to set a real-time scheduling priority,\n"
+"or to lock memory.\n"
 "This could cause audio to be interrupted while other programs use the CPU.\n" 
 "To fix: As root, run\n"
 "\n"
 "\tsetcap cap_sys_nice+ep filename\n"
+"\tsetcap cap_ipc_lock+ep filename\n"
 "\n"
-"where filename is the executable file for this program. That will\n"
-"allow you to use a real-time scheduling priority while running as any user.\n"
-"Alternatively, you can execute this program as root.\n\n";
+"where filename is the executable file for this program.\n"
+"That will allow you to use a real-time scheduling priority and locked memory\n"
+"while running as any user.\n"
+"Alternatively, you can execute this program as root.\n";
 
 static const char old_kernel_message[] =
-"This kernel doesn't seem to have real-time facilities.\n"
-"If audio is sometimes interrupted, try a newer kernel.\n\n";
+"This kernel doesn't seem to have real-time facilities or memory locking.\n"
+"If audio is sometimes interrupted, try a newer kernel.\n";
 
 namespace FreeDV {
   void
   set_scheduler()
   {
+    bool insufficient_privilege = false;
+    bool old_kernel = false;
+
 #ifdef _POSIX_PRIORITY_SCHEDULING
+    // Put this process on the round-robin realtime scheduling queue at the
+    // minimum priority. Other real-time processes (perhaps portaudio) can
+    // run with a higher priority than this, but this process will run at
+    // a higher priority than all normal processes.
     sched_param        p;
     int                policy = SCHED_RR;
 
     p.sched_priority = sched_get_priority_min(policy);
     const int result = sched_setscheduler(0, policy, &p);
     if ( result < 0 ) {
+      std::cerr << "sched_setscheduler: " << strerror(errno) << std::endl;
+      if ( errno == EINVAL )
+         old_kernel = true;
       if ( errno == EPERM )
-         std::cerr << insufficient_privilege_message << std::flush;
-      else if ( errno == EINVAL )
-         std::cerr << old_kernel_message << std::flush;
-      else {
-        std::cerr << "sched_setscheduler: " << strerror(errno) << std::endl;
-      }
+         insufficient_privilege = true;
+    }
+#endif
+
+#ifdef _POSIX_MEMLOCK_RANGE
+    if ( mlockall(MCL_CURRENT|MCL_FUTURE) < 0 ) {
+      std::cerr << "mlockall: " << strerror(errno) << std::endl;
+      if ( errno == EINVAL )
+         old_kernel = true;
+      if ( errno == EPERM )
+         insufficient_privilege = true;
     }
-  }
 #endif
+    if ( old_kernel )
+      std::cerr << old_kernel_message;
+    else if ( insufficient_privilege )
+      std::cerr << insufficient_privilege_message;
+  }
 }
index 35272e8e63d3270156361c978c74a7ff332af35c..747a6ff0193ed160865a8e23e605b8c3204401b0 100644 (file)
@@ -35,7 +35,6 @@ namespace FreeDV {
     bool               ptt_digital;
     bool               ptt_ssb;
     std::size_t                min_frame_duration;
-    struct timespec    last_frame_time;
     struct timespec    next_frame_time;
  
     void               key_down();
@@ -66,7 +65,6 @@ namespace FreeDV {
   void
   Run::reset()
   {
-    clock_gettime(CLOCK_MONOTONIC, &last_frame_time);
     in_fifo.reset();
     codec_fifo.reset();
     out_fifo.reset();
@@ -170,23 +168,26 @@ namespace FreeDV {
       if ( bytes_to_decode > 0 )
         codec_fifo.get_done(bytes_to_decode);
 
-      // TODO: We can do clock tuning here to maximize power saving, if it
-      // results in any noticable gain.
       if ( result > 0 ) {
        // std::cerr << '.';
         out_fifo.put_done(result * 2);
 
-        last_frame_time = start_time;
+        // Calculate a time one millisecond short of when the next frame
+        // should start. We can sleep until then.
         const long duration = ((min_frame_duration - 1) * 1000000);
 
-        next_frame_time.tv_sec = last_frame_time.tv_sec;
-        next_frame_time.tv_nsec = last_frame_time.tv_nsec + duration;
+        next_frame_time.tv_sec = start_time.tv_sec;
+        next_frame_time.tv_nsec = start_time.tv_nsec + duration;
         next_frame_time.tv_sec += next_frame_time.tv_nsec / 1000000000;
         next_frame_time.tv_nsec %= 1000000000;
       }
       else {
+
        // std::cerr << '+';
-       // Add a microsecond. We really could poll the I/O interfaces instead.
+
+       // Go to sleep for a millisecond and try again. We could poll the
+        // I/O interfaces for ready instead.
+        next_frame_time = start_time;
         next_frame_time.tv_nsec += 1000000;
         next_frame_time.tv_sec += next_frame_time.tv_nsec / 1000000000;
         next_frame_time.tv_nsec %= 1000000000;
@@ -202,7 +203,8 @@ namespace FreeDV {
     min_frame_duration = max(min_frame_duration, i->codec->min_frame_duration());
     min_frame_duration = max(min_frame_duration, i->framer->min_frame_duration());
 
-    std::cerr << "minimum frame duration is " << min_frame_duration << std::endl;
+    std::cerr << "The minimum frame duration is "
+     << min_frame_duration << " milliseconds." << std::endl;
     if ( min_frame_duration > MaximumFrameDuration ) {
       std::ostringstream str;
 
@@ -211,7 +213,8 @@ namespace FreeDV {
       str << " is larger than MaximumFrameDuration of ";
       str << MaximumFrameDuration << '.' << std::endl;
       str << "A Modem, Framer, or Codec returned min_frame_duration() that";
-      str << " was too large, or MaximumFrameDuration must be increased.";
+      str << " was too large," << std::endl;
+      str << "or MaximumFrameDuration must be increased.";
       throw std::runtime_error(str.str().c_str());
     }
     assert(min_frame_duration < 1000000);