From: bruceperens Date: Thu, 13 Mar 2014 20:59:39 +0000 (+0000) Subject: More tuning of periods. X-Git-Url: http://git.whiteaudio.com/gitweb/?a=commitdiff_plain;h=85491aa15d5becb2d23693db780d29104c551ceb;p=freetel-svn-tracking.git More tuning of periods. 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 --- diff --git a/freedv-server/source/codec_noop.cpp b/freedv-server/source/codec_noop.cpp index 54af458c..fed732ff 100644 --- a/freedv-server/source/codec_noop.cpp +++ b/freedv-server/source/codec_noop.cpp @@ -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 * diff --git a/freedv-server/source/platform/linux/privilege.cpp b/freedv-server/source/platform/linux/privilege.cpp index 72c2acf6..271e4848 100644 --- a/freedv-server/source/platform/linux/privilege.cpp +++ b/freedv-server/source/platform/linux/privilege.cpp @@ -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; diff --git a/freedv-server/source/platform/linux/scheduler.cpp b/freedv-server/source/platform/linux/scheduler.cpp index cc1bc13a..cc07ba57 100644 --- a/freedv-server/source/platform/linux/scheduler.cpp +++ b/freedv-server/source/platform/linux/scheduler.cpp @@ -1,46 +1,71 @@ #include #ifdef _POSIX_PRIORITY_SCHEDULING -#include #include +#endif +#ifdef _POSIX_MEMLOCK_RANGE +#include +#endif +#include #include #include -#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; + } } diff --git a/freedv-server/source/run.cpp b/freedv-server/source/run.cpp index 35272e8e..747a6ff0 100644 --- a/freedv-server/source/run.cpp +++ b/freedv-server/source/run.cpp @@ -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);